mirror of
https://github.com/haexhub/haex-hub.git
synced 2025-12-18 06:50:51 +01:00
Compare commits
7 Commits
6187e32f89
...
v0.1.13
| Author | SHA1 | Date | |
|---|---|---|---|
| 38cc6f36d4 | |||
| 0d4059e518 | |||
| c551641737 | |||
| 75093485bd | |||
| e1be08cb76 | |||
| 7d1f346c4b | |||
| af61972342 |
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "haex-hub",
|
"name": "haex-hub",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.1.12",
|
"version": "0.1.13",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "nuxt build",
|
"build": "nuxt build",
|
||||||
|
|||||||
6
src-tauri/bindings/ExtensionErrorCode.ts
Normal file
6
src-tauri/bindings/ExtensionErrorCode.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Error codes for frontend handling
|
||||||
|
*/
|
||||||
|
export type ExtensionErrorCode = "SecurityViolation" | "NotFound" | "PermissionDenied" | "MutexPoisoned" | "Database" | "Filesystem" | "FilesystemWithPath" | "Http" | "Shell" | "Manifest" | "Validation" | "InvalidPublicKey" | "InvalidSignature" | "InvalidActionString" | "SignatureVerificationFailed" | "CalculateHash" | "Installation";
|
||||||
6
src-tauri/bindings/SerializedExtensionError.ts
Normal file
6
src-tauri/bindings/SerializedExtensionError.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialized representation of ExtensionError for TypeScript
|
||||||
|
*/
|
||||||
|
export type SerializedExtensionError = { code: number, type: string, message: string, extension_id: string | null, };
|
||||||
@ -30,10 +30,15 @@
|
|||||||
"fs:allow-resource-write-recursive",
|
"fs:allow-resource-write-recursive",
|
||||||
"fs:allow-download-read-recursive",
|
"fs:allow-download-read-recursive",
|
||||||
"fs:allow-download-write-recursive",
|
"fs:allow-download-write-recursive",
|
||||||
|
"fs:allow-temp-read-recursive",
|
||||||
|
"fs:allow-temp-write-recursive",
|
||||||
"fs:default",
|
"fs:default",
|
||||||
{
|
{
|
||||||
"identifier": "fs:scope",
|
"identifier": "fs:scope",
|
||||||
"allow": [{ "path": "**" }]
|
"allow": [
|
||||||
|
{ "path": "**" },
|
||||||
|
{ "path": "$TEMP/**" }
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"http:allow-fetch-send",
|
"http:allow-fetch-send",
|
||||||
"http:allow-fetch",
|
"http:allow-fetch",
|
||||||
@ -44,6 +49,12 @@
|
|||||||
"notification:allow-is-permission-granted",
|
"notification:allow-is-permission-granted",
|
||||||
"notification:default",
|
"notification:default",
|
||||||
"opener:allow-open-url",
|
"opener:allow-open-url",
|
||||||
|
{
|
||||||
|
"identifier": "opener:allow-open-path",
|
||||||
|
"allow": [
|
||||||
|
{ "path": "$TEMP/**" }
|
||||||
|
]
|
||||||
|
},
|
||||||
"opener:default",
|
"opener:default",
|
||||||
"os:allow-hostname",
|
"os:allow-hostname",
|
||||||
"os:default",
|
"os:default",
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
{"default":{"identifier":"default","description":"Capability for the main window","local":true,"windows":["main"],"permissions":["core:default","core:webview:allow-create-webview-window","core:webview:allow-create-webview","core:webview:allow-webview-show","core:webview:default","core:window:allow-create","core:window:allow-get-all-windows","core:window:allow-show","core:window:default","dialog:default","fs:allow-appconfig-read-recursive","fs:allow-appconfig-write-recursive","fs:allow-appdata-read-recursive","fs:allow-appdata-write-recursive","fs:allow-applocaldata-read-recursive","fs:allow-applocaldata-write-recursive","fs:allow-read-file","fs:allow-write-file","fs:allow-read-dir","fs:allow-mkdir","fs:allow-exists","fs:allow-remove","fs:allow-resource-read-recursive","fs:allow-resource-write-recursive","fs:allow-download-read-recursive","fs:allow-download-write-recursive","fs:default",{"identifier":"fs:scope","allow":[{"path":"**"}]},"http:allow-fetch-send","http:allow-fetch","http:default","notification:allow-create-channel","notification:allow-list-channels","notification:allow-notify","notification:allow-is-permission-granted","notification:default","opener:allow-open-url","opener:default","os:allow-hostname","os:default","store:default"]}}
|
{"default":{"identifier":"default","description":"Capability for the main window","local":true,"windows":["main"],"permissions":["core:default","core:webview:allow-create-webview-window","core:webview:allow-create-webview","core:webview:allow-webview-show","core:webview:default","core:window:allow-create","core:window:allow-get-all-windows","core:window:allow-show","core:window:default","dialog:default","fs:allow-appconfig-read-recursive","fs:allow-appconfig-write-recursive","fs:allow-appdata-read-recursive","fs:allow-appdata-write-recursive","fs:allow-applocaldata-read-recursive","fs:allow-applocaldata-write-recursive","fs:allow-read-file","fs:allow-write-file","fs:allow-read-dir","fs:allow-mkdir","fs:allow-exists","fs:allow-remove","fs:allow-resource-read-recursive","fs:allow-resource-write-recursive","fs:allow-download-read-recursive","fs:allow-download-write-recursive","fs:allow-temp-read-recursive","fs:allow-temp-write-recursive","fs:default",{"identifier":"fs:scope","allow":[{"path":"**"},{"path":"$TEMP/**"}]},"http:allow-fetch-send","http:allow-fetch","http:default","notification:allow-create-channel","notification:allow-list-channels","notification:allow-notify","notification:allow-is-permission-granted","notification:default","opener:allow-open-url",{"identifier":"opener:allow-open-path","allow":[{"path":"$TEMP/**"}]},"opener:default","os:allow-hostname","os:default","store:default"]}}
|
||||||
@ -1,10 +1,12 @@
|
|||||||
// src-tauri/src/extension/error.rs
|
// src-tauri/src/extension/error.rs
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
use ts_rs::TS;
|
||||||
|
|
||||||
use crate::database::error::DatabaseError;
|
use crate::database::error::DatabaseError;
|
||||||
|
|
||||||
/// Error codes for frontend handling
|
/// Error codes for frontend handling
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, TS)]
|
||||||
|
#[ts(export)]
|
||||||
pub enum ExtensionErrorCode {
|
pub enum ExtensionErrorCode {
|
||||||
SecurityViolation = 1000,
|
SecurityViolation = 1000,
|
||||||
NotFound = 1001,
|
NotFound = 1001,
|
||||||
@ -25,6 +27,17 @@ pub enum ExtensionErrorCode {
|
|||||||
Installation = 5000,
|
Installation = 5000,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Serialized representation of ExtensionError for TypeScript
|
||||||
|
#[derive(Debug, Clone, serde::Serialize, TS)]
|
||||||
|
#[ts(export)]
|
||||||
|
pub struct SerializedExtensionError {
|
||||||
|
pub code: u16,
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
pub error_type: String,
|
||||||
|
pub message: String,
|
||||||
|
pub extension_id: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
impl serde::Serialize for ExtensionErrorCode {
|
impl serde::Serialize for ExtensionErrorCode {
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
where
|
where
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://schema.tauri.app/config/2",
|
"$schema": "https://schema.tauri.app/config/2",
|
||||||
"productName": "haex-hub",
|
"productName": "haex-hub",
|
||||||
"version": "0.1.4",
|
"version": "0.1.13",
|
||||||
"identifier": "space.haex.hub",
|
"identifier": "space.haex.hub",
|
||||||
"build": {
|
"build": {
|
||||||
"beforeDevCommand": "pnpm dev",
|
"beforeDevCommand": "pnpm dev",
|
||||||
|
|||||||
@ -163,8 +163,9 @@ const loadDevExtensionAsync = async () => {
|
|||||||
extensionPath.value = ''
|
extensionPath.value = ''
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load dev extension:', error)
|
console.error('Failed to load dev extension:', error)
|
||||||
|
const { getErrorMessage } = useExtensionError()
|
||||||
add({
|
add({
|
||||||
description: t('add.errors.loadFailed') + error,
|
description: `${t('add.errors.loadFailed')}: ${getErrorMessage(error)}`,
|
||||||
color: 'error',
|
color: 'error',
|
||||||
})
|
})
|
||||||
} finally {
|
} finally {
|
||||||
@ -196,8 +197,9 @@ const reloadDevExtensionAsync = async (extension: ExtensionInfoResponse) => {
|
|||||||
})
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to reload dev extension:', error)
|
console.error('Failed to reload dev extension:', error)
|
||||||
|
const { getErrorMessage } = useExtensionError()
|
||||||
add({
|
add({
|
||||||
description: t('list.errors.reloadFailed') + error,
|
description: `${t('list.errors.reloadFailed')}: ${getErrorMessage(error)}`,
|
||||||
color: 'error',
|
color: 'error',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -223,8 +225,9 @@ const removeDevExtensionAsync = async (extension: ExtensionInfoResponse) => {
|
|||||||
await loadExtensionsAsync()
|
await loadExtensionsAsync()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to remove dev extension:', error)
|
console.error('Failed to remove dev extension:', error)
|
||||||
|
const { getErrorMessage } = useExtensionError()
|
||||||
add({
|
add({
|
||||||
description: t('list.errors.removeFailed') + error,
|
description: `${t('list.errors.removeFailed')}: ${getErrorMessage(error)}`,
|
||||||
color: 'error',
|
color: 'error',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
description="Workspaces"
|
description="Workspaces"
|
||||||
>
|
>
|
||||||
<template #content>
|
<template #content>
|
||||||
<div class="pl-8 pr-4 h-full overflow-y-auto">
|
<div class="pl-8 pr-4 overflow-y-auto py-8">
|
||||||
<!-- Workspace Cards -->
|
<!-- Workspace Cards -->
|
||||||
<div class="flex flex-col gap-3">
|
<div class="flex flex-col gap-3">
|
||||||
<HaexWorkspaceCard
|
<HaexWorkspaceCard
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
v-bind="$attrs"
|
v-bind="$attrs"
|
||||||
:ui="{
|
:ui="{
|
||||||
content:
|
content:
|
||||||
'pb-[env(safe-area-inset-bottom)] pt-[env(safe-area-inset-top)] w-dvw max-w-md sm:max-w-fit',
|
'pb-[env(safe-area-inset-bottom)] pt-[env(safe-area-inset-top)] ',
|
||||||
...(ui || {}),
|
...(ui || {}),
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
@ -26,5 +26,7 @@ import type { DrawerProps } from '@nuxt/ui'
|
|||||||
* Wrapper around UDrawer that automatically applies safe area insets for mobile devices.
|
* Wrapper around UDrawer that automatically applies safe area insets for mobile devices.
|
||||||
* Passes through all props and slots to UDrawer.
|
* Passes through all props and slots to UDrawer.
|
||||||
*/
|
*/
|
||||||
defineProps</* @vue-ignore */ DrawerProps>()
|
const props = defineProps</* @vue-ignore */ DrawerProps>()
|
||||||
|
|
||||||
|
const { ui } = toRefs(props)
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
import { save } from '@tauri-apps/plugin-dialog'
|
import { save } from '@tauri-apps/plugin-dialog'
|
||||||
import { writeFile } from '@tauri-apps/plugin-fs'
|
import { writeFile } from '@tauri-apps/plugin-fs'
|
||||||
|
import { openPath } from '@tauri-apps/plugin-opener'
|
||||||
|
import { tempDir, join } from '@tauri-apps/api/path'
|
||||||
import type { IHaexHubExtension } from '~/types/haexhub'
|
import type { IHaexHubExtension } from '~/types/haexhub'
|
||||||
import type { ExtensionRequest } from './types'
|
import type { ExtensionRequest } from './types'
|
||||||
|
|
||||||
@ -42,6 +44,48 @@ export async function handleFilesystemMethodAsync(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'haextension.fs.showImage': {
|
||||||
|
// This method is now handled by the frontend using PhotoSwipe
|
||||||
|
// We keep it for backwards compatibility but it's a no-op
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
useFrontend: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'haextension.fs.openFile': {
|
||||||
|
const params = request.params as {
|
||||||
|
data: number[]
|
||||||
|
fileName: string
|
||||||
|
mimeType?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Convert number array back to Uint8Array
|
||||||
|
const data = new Uint8Array(params.data)
|
||||||
|
|
||||||
|
// Get temp directory and create file path
|
||||||
|
const tempDirPath = await tempDir()
|
||||||
|
const tempFilePath = await join(tempDirPath, params.fileName)
|
||||||
|
|
||||||
|
// Write file to temp directory
|
||||||
|
await writeFile(tempFilePath, data)
|
||||||
|
|
||||||
|
// Open file with system's default viewer
|
||||||
|
await openPath(tempFilePath)
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.error('[Filesystem] Error opening file:', error)
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unknown filesystem method: ${request.method}`)
|
throw new Error(`Unknown filesystem method: ${request.method}`)
|
||||||
}
|
}
|
||||||
|
|||||||
43
src/composables/useExtensionError.ts
Normal file
43
src/composables/useExtensionError.ts
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import type { SerializedExtensionError } from '~~/src-tauri/bindings/SerializedExtensionError'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type guard to check if error is a SerializedExtensionError
|
||||||
|
*/
|
||||||
|
export function isSerializedExtensionError(error: unknown): error is SerializedExtensionError {
|
||||||
|
return (
|
||||||
|
typeof error === 'object' &&
|
||||||
|
error !== null &&
|
||||||
|
'code' in error &&
|
||||||
|
'message' in error &&
|
||||||
|
'type' in error
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract error message from unknown error type
|
||||||
|
*/
|
||||||
|
export function getErrorMessage(error: unknown): string {
|
||||||
|
if (isSerializedExtensionError(error)) {
|
||||||
|
return error.message
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error instanceof Error) {
|
||||||
|
return error.message
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof error === 'string') {
|
||||||
|
return error
|
||||||
|
}
|
||||||
|
|
||||||
|
return String(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Composable for handling extension errors
|
||||||
|
*/
|
||||||
|
export function useExtensionError() {
|
||||||
|
return {
|
||||||
|
isSerializedExtensionError,
|
||||||
|
getErrorMessage,
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -13,7 +13,7 @@ export interface ViewportDimensions {
|
|||||||
*/
|
*/
|
||||||
export function getViewportDimensions(): ViewportDimensions {
|
export function getViewportDimensions(): ViewportDimensions {
|
||||||
const viewportWidth = window.innerWidth
|
const viewportWidth = window.innerWidth
|
||||||
const viewportHeight = window.innerHeight - 60 // Subtract tab bar height
|
const viewportHeight = window.innerHeight - 40 // Subtract header height
|
||||||
|
|
||||||
// Get safe-area-insets from CSS variables
|
// Get safe-area-insets from CSS variables
|
||||||
const safeAreaTop = parseFloat(
|
const safeAreaTop = parseFloat(
|
||||||
@ -45,11 +45,7 @@ export function getViewportDimensions(): ViewportDimensions {
|
|||||||
*/
|
*/
|
||||||
export function getAvailableContentHeight(): number {
|
export function getAvailableContentHeight(): number {
|
||||||
const dimensions = getViewportDimensions()
|
const dimensions = getViewportDimensions()
|
||||||
return (
|
return dimensions.height - dimensions.safeAreaTop - dimensions.safeAreaBottom
|
||||||
dimensions.height -
|
|
||||||
dimensions.safeAreaTop -
|
|
||||||
dimensions.safeAreaBottom
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user