feat(theseus): Update to Tauri v2 (#2178)
* feat(theseus): Initial migration to Tauri v2 * feat(theseus): Added a way to zoom / scale UI * chore(theseus): Started cleaning up some plugins * fix(theseus): Github Actions * refactor(theseus): Reduced boilerplate & more work * feat(theseus): Allow multiple app instances to be open at once (#995) * fix(theseus): Lint & more * fix(theseus): App Release github action * fix(theseus): Open links in browser & macos builds * fix(theseus): Rebase fixes * fix(theseus): Updater & app release action * fix(theseus): Fixed definitions in `build.rs` * Fix MacOS deep linking, window decorations * fix(theseus): Closing & maximizing app * Fix macos build * add back release conf * acc fix build * make updater for release builds only * focus window on startup --------- Co-authored-by: Jai A <jaiagr+gpg@pm.me> Co-authored-by: Geometrically <18202329+Geometrically@users.noreply.github.com>
This commit is contained in:
parent
396f737612
commit
d6a72fbfc4
@ -13,3 +13,6 @@ max_line_length = 100
|
||||
[*.md]
|
||||
max_line_length = off
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.rs]
|
||||
indent_size = 4
|
20
.github/workflows/app-release.yml
vendored
20
.github/workflows/app-release.yml
vendored
@ -20,12 +20,12 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform: [macos-latest, windows-latest, ubuntu-20.04]
|
||||
platform: [macos-latest, windows-latest, ubuntu-22.04]
|
||||
|
||||
runs-on: ${{ matrix.platform }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Rust setup (mac)
|
||||
if: startsWith(matrix.platform, 'macos')
|
||||
@ -49,7 +49,7 @@ jobs:
|
||||
${{ runner.os }}-rust-target-
|
||||
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
@ -66,7 +66,7 @@ jobs:
|
||||
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Setup pnpm cache
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
@ -77,7 +77,7 @@ jobs:
|
||||
if: startsWith(matrix.platform, 'ubuntu')
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libappindicator3-dev librsvg2-dev patchelf libselinux1
|
||||
sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf
|
||||
|
||||
- name: Install frontend dependencies
|
||||
run: pnpm install
|
||||
@ -95,11 +95,12 @@ jobs:
|
||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
|
||||
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
|
||||
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
||||
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
|
||||
with:
|
||||
args: "--target universal-apple-darwin --config ./apps/app/tauri-release.conf.json"
|
||||
working-directory: ./apps/app
|
||||
tauriScript: pnpm --filter=@modrinth/app run tauri
|
||||
|
||||
- name: build app
|
||||
uses: tauri-apps/tauri-action@v0
|
||||
@ -107,21 +108,22 @@ jobs:
|
||||
if: "!startsWith(matrix.platform, 'macos')"
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
|
||||
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
||||
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
|
||||
with:
|
||||
tauriScript: pnpm --filter=@modrinth/app run tauri
|
||||
args: "--config ./apps/app/tauri-release.conf.json"
|
||||
working-directory: ./apps/app
|
||||
|
||||
- name: upload ${{ matrix.platform }}
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
if: startsWith(matrix.platform, 'macos')
|
||||
with:
|
||||
name: ${{ matrix.platform }}
|
||||
path: "${{ join(fromJSON(steps.build_os_mac.outputs.artifactPaths), '\n') }}"
|
||||
|
||||
- name: upload ${{ matrix.platform }}
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
if: "!startsWith(matrix.platform, 'macos')"
|
||||
with:
|
||||
name: ${{ matrix.platform }}
|
||||
|
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
@ -11,7 +11,7 @@ on:
|
||||
jobs:
|
||||
build:
|
||||
name: Build, Test, and Lint
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
@ -30,7 +30,7 @@ jobs:
|
||||
- name: Install build dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libappindicator3-dev librsvg2-dev patchelf libselinux1
|
||||
sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf
|
||||
|
||||
- name: Setup Node.JS environment
|
||||
uses: actions/setup-node@v4
|
||||
|
2216
Cargo.lock
generated
2216
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -13,14 +13,17 @@
|
||||
"@modrinth/assets": "workspace:*",
|
||||
"@modrinth/ui": "workspace:*",
|
||||
"@modrinth/utils": "workspace:*",
|
||||
"@tauri-apps/api": "^1.6.0",
|
||||
"@tauri-apps/api": "^2.0.0-rc.3",
|
||||
"@tauri-apps/plugin-dialog": "^2.0.0-rc.0",
|
||||
"@tauri-apps/plugin-os": "^2.0.0-rc.0",
|
||||
"@tauri-apps/plugin-window-state": "^2.0.0-rc.0",
|
||||
"@tauri-apps/plugin-shell": "^2.0.0-rc.0",
|
||||
"@vintl/vintl": "^4.4.1",
|
||||
"dayjs": "^1.11.10",
|
||||
"floating-vue": "^5.2.2",
|
||||
"mixpanel-browser": "^2.49.0",
|
||||
"ofetch": "^1.3.4",
|
||||
"pinia": "^2.1.7",
|
||||
"tauri-plugin-window-state-api": "github:tauri-apps/tauri-plugin-window-state#v1",
|
||||
"vite-svg-loader": "^5.1.0",
|
||||
"vue": "^3.4.21",
|
||||
"vue-multiselect": "3.0.0",
|
||||
@ -28,7 +31,7 @@
|
||||
"vue-virtual-scroller": "v2.0.0-beta.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tauri-apps/cli": "^1.6.0",
|
||||
"@tauri-apps/cli": "^2.0.0-rc",
|
||||
"@vitejs/plugin-vue": "^5.0.4",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"eslint": "^8.57.0",
|
||||
|
@ -15,8 +15,7 @@ import ModrinthLoadingIndicator from '@/components/modrinth-loading-indicator'
|
||||
import { handleError, useNotifications } from '@/store/notifications.js'
|
||||
import { command_listener, warning_listener } from '@/helpers/events.js'
|
||||
import { MinimizeIcon, MaximizeIcon } from '@/assets/icons'
|
||||
import { type } from '@tauri-apps/api/os'
|
||||
import { appWindow } from '@tauri-apps/api/window'
|
||||
import { type } from '@tauri-apps/plugin-os'
|
||||
import { isDev, getOS } from '@/helpers/utils.js'
|
||||
import {
|
||||
mixpanel_track,
|
||||
@ -24,18 +23,20 @@ import {
|
||||
mixpanel_opt_out_tracking,
|
||||
mixpanel_is_loaded,
|
||||
} from '@/helpers/mixpanel'
|
||||
import { saveWindowState, StateFlags } from 'tauri-plugin-window-state-api'
|
||||
import { saveWindowState, StateFlags } from '@tauri-apps/plugin-window-state'
|
||||
import { getCurrentWindow } from '@tauri-apps/api/window'
|
||||
import { getVersion } from '@tauri-apps/api/app'
|
||||
import { window as TauriWindow } from '@tauri-apps/api'
|
||||
import { TauriEvent } from '@tauri-apps/api/event'
|
||||
import URLConfirmModal from '@/components/ui/URLConfirmModal.vue'
|
||||
import { install_from_file } from './helpers/pack'
|
||||
import { useError } from '@/store/error.js'
|
||||
import { useCheckDisableMouseover } from '@/composables/macCssFix.js'
|
||||
import ModInstallModal from '@/components/ui/install_flow/ModInstallModal.vue'
|
||||
import IncompatibilityWarningModal from '@/components/ui/install_flow/IncompatibilityWarningModal.vue'
|
||||
import InstallConfirmModal from '@/components/ui/install_flow/InstallConfirmModal.vue'
|
||||
import { useInstall } from '@/store/install.js'
|
||||
import { invoke } from '@tauri-apps/api/tauri'
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
import { open } from '@tauri-apps/plugin-shell'
|
||||
import { get_opening_command, initialize_state } from '@/helpers/state'
|
||||
|
||||
const themeStore = useTheming()
|
||||
@ -57,6 +58,10 @@ const os = ref('')
|
||||
|
||||
const stateInitialized = ref(false)
|
||||
|
||||
onMounted(async () => {
|
||||
await useCheckDisableMouseover()
|
||||
})
|
||||
|
||||
async function setupApp() {
|
||||
stateInitialized.value = true
|
||||
const {
|
||||
@ -79,7 +84,7 @@ async function setupApp() {
|
||||
showOnboarding.value = !onboarded
|
||||
|
||||
nativeDecorations.value = native_decorations
|
||||
if (os.value !== 'MacOS') await appWindow.setDecorations(native_decorations)
|
||||
if (os.value !== 'MacOS') await getCurrentWindow().setDecorations(native_decorations)
|
||||
|
||||
themeStore.setThemeState(theme)
|
||||
themeStore.collapsedNavigation = collapsed_navigation
|
||||
@ -93,7 +98,8 @@ async function setupApp() {
|
||||
|
||||
if (!dev) document.addEventListener('contextmenu', (event) => event.preventDefault())
|
||||
|
||||
if ((await type()) === 'Darwin') {
|
||||
const osType = await type()
|
||||
if (osType === 'macos') {
|
||||
document.getElementsByTagName('html')[0].classList.add('mac')
|
||||
} else {
|
||||
document.getElementsByTagName('html')[0].classList.add('windows')
|
||||
@ -126,14 +132,9 @@ initialize_state()
|
||||
})
|
||||
|
||||
const handleClose = async () => {
|
||||
await saveWindowState(StateFlags.ALL)
|
||||
await TauriWindow.getCurrent().close()
|
||||
await getCurrentWindow().close()
|
||||
}
|
||||
|
||||
TauriWindow.getCurrent().listen(TauriEvent.WINDOW_CLOSE_REQUESTED, async () => {
|
||||
await handleClose()
|
||||
})
|
||||
|
||||
const router = useRouter()
|
||||
router.afterEach((to, from, failure) => {
|
||||
if (mixpanel_is_loaded()) {
|
||||
@ -180,13 +181,7 @@ document.querySelector('body').addEventListener('click', function (e) {
|
||||
!target.href.startsWith('http://localhost') &&
|
||||
!target.href.startsWith('https://tauri.localhost')
|
||||
) {
|
||||
window.__TAURI_INVOKE__('tauri', {
|
||||
__tauriModule: 'Shell',
|
||||
message: {
|
||||
cmd: 'open',
|
||||
path: target.href,
|
||||
},
|
||||
})
|
||||
open(target.href)
|
||||
}
|
||||
e.preventDefault()
|
||||
break
|
||||
@ -288,10 +283,14 @@ async function handleCommand(e) {
|
||||
</section>
|
||||
</div>
|
||||
<section v-if="!nativeDecorations" class="window-controls">
|
||||
<Button class="titlebar-button" icon-only @click="() => appWindow.minimize()">
|
||||
<Button class="titlebar-button" icon-only @click="() => getCurrentWindow().minimize()">
|
||||
<MinimizeIcon />
|
||||
</Button>
|
||||
<Button class="titlebar-button" icon-only @click="() => appWindow.toggleMaximize()">
|
||||
<Button
|
||||
class="titlebar-button"
|
||||
icon-only
|
||||
@click="() => getCurrentWindow().toggleMaximize()"
|
||||
>
|
||||
<MaximizeIcon />
|
||||
</Button>
|
||||
<Button class="titlebar-button close" icon-only @click="handleClose">
|
||||
|
@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { DropdownIcon, FolderOpenIcon, SearchIcon } from '@modrinth/assets'
|
||||
import { Button, OverflowMenu } from '@modrinth/ui'
|
||||
import { open } from '@tauri-apps/api/dialog'
|
||||
import { open } from '@tauri-apps/plugin-dialog'
|
||||
import { add_project_from_path } from '@/helpers/profile.js'
|
||||
import { handleError } from '@/store/notifications.js'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
@ -4,7 +4,7 @@ import { Button, Checkbox, Modal } from '@modrinth/ui'
|
||||
import { PackageIcon, VersionIcon } from '@/assets/icons'
|
||||
import { ref } from 'vue'
|
||||
import { export_profile_mrpack, get_pack_export_candidates } from '@/helpers/profile.js'
|
||||
import { open } from '@tauri-apps/api/dialog'
|
||||
import { open } from '@tauri-apps/plugin-dialog'
|
||||
import { handleError } from '@/store/notifications.js'
|
||||
import { useTheming } from '@/store/theme'
|
||||
|
||||
|
@ -3,7 +3,7 @@ import { onUnmounted, ref, computed } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { StopCircleIcon, PlayIcon } from '@modrinth/assets'
|
||||
import { Card, Avatar, AnimatedLogo } from '@modrinth/ui'
|
||||
import { convertFileSrc } from '@tauri-apps/api/tauri'
|
||||
import { convertFileSrc } from '@tauri-apps/api/core'
|
||||
import { kill, run } from '@/helpers/profile'
|
||||
import { get_by_profile_path } from '@/helpers/process'
|
||||
import { process_listener } from '@/helpers/events'
|
||||
|
@ -211,8 +211,8 @@ import { Avatar, Button, Chips, Modal, Checkbox } from '@modrinth/ui'
|
||||
import { computed, onUnmounted, ref, shallowRef } from 'vue'
|
||||
import { get_loaders } from '@/helpers/tags'
|
||||
import { create } from '@/helpers/profile'
|
||||
import { open } from '@tauri-apps/api/dialog'
|
||||
import { tauri } from '@tauri-apps/api'
|
||||
import { open } from '@tauri-apps/plugin-dialog'
|
||||
import { convertFileSrc } from '@tauri-apps/api/core'
|
||||
import { get_game_versions, get_loader_versions } from '@/helpers/metadata'
|
||||
import { handleError } from '@/store/notifications.js'
|
||||
import Multiselect from 'vue-multiselect'
|
||||
@ -382,7 +382,7 @@ const upload_icon = async () => {
|
||||
})
|
||||
|
||||
if (!icon.value) return
|
||||
display_icon.value = tauri.convertFileSrc(icon.value)
|
||||
display_icon.value = convertFileSrc(icon.value)
|
||||
}
|
||||
|
||||
const reset_icon = () => {
|
||||
|
@ -63,7 +63,7 @@ import {
|
||||
import { Button } from '@modrinth/ui'
|
||||
import { auto_install_java, find_filtered_jres, get_jre, test_jre } from '@/helpers/jre.js'
|
||||
import { ref } from 'vue'
|
||||
import { open } from '@tauri-apps/api/dialog'
|
||||
import { open } from '@tauri-apps/plugin-dialog'
|
||||
import JavaDetectionModal from '@/components/ui/JavaDetectionModal.vue'
|
||||
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||
import { handleError } from '@/store/state.js'
|
||||
|
@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<div v-if="!hidden" class="splash-screen dark" :class="{ 'fade-out': doneLoading }">
|
||||
<div v-if="os !== 'MacOS'" class="app-buttons">
|
||||
<button class="btn icon-only transparent" icon-only @click="() => appWindow.minimize()">
|
||||
<button class="btn icon-only transparent" icon-only @click="() => getCurrent().minimize()">
|
||||
<MinimizeIcon />
|
||||
</button>
|
||||
<button class="btn icon-only transparent" @click="() => appWindow.toggleMaximize()">
|
||||
<button class="btn icon-only transparent" @click="() => getCurrent().toggleMaximize()">
|
||||
<MaximizeIcon />
|
||||
</button>
|
||||
<button class="btn icon-only transparent" @click="handleClose">
|
||||
@ -85,12 +85,11 @@
|
||||
import { ref, watch } from 'vue'
|
||||
import ProgressBar from '@/components/ui/ProgressBar.vue'
|
||||
import { loading_listener } from '@/helpers/events.js'
|
||||
import { appWindow } from '@tauri-apps/api/window'
|
||||
import { getCurrentWindow } from '@tauri-apps/api/window'
|
||||
import { XIcon } from '@modrinth/assets'
|
||||
import { MaximizeIcon, MinimizeIcon } from '@/assets/icons/index.js'
|
||||
import { window as TauriWindow } from '@tauri-apps/api'
|
||||
import { TauriEvent } from '@tauri-apps/api/event'
|
||||
import { saveWindowState, StateFlags } from 'tauri-plugin-window-state-api'
|
||||
import { saveWindowState, StateFlags } from '@tauri-apps/plugin-window-state'
|
||||
import { getOS } from '@/helpers/utils.js'
|
||||
import { useLoading } from '@/store/loading.js'
|
||||
|
||||
@ -138,13 +137,8 @@ loading_listener(async (e) => {
|
||||
})
|
||||
|
||||
const handleClose = async () => {
|
||||
await saveWindowState(StateFlags.ALL)
|
||||
await TauriWindow.getCurrent().close()
|
||||
await getCurrentWindow().close()
|
||||
}
|
||||
|
||||
TauriWindow.getCurrent().listen(TauriEvent.WINDOW_CLOSE_REQUESTED, async () => {
|
||||
await handleClose()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
@ -16,13 +16,13 @@ import {
|
||||
list,
|
||||
create,
|
||||
} from '@/helpers/profile'
|
||||
import { open } from '@tauri-apps/api/dialog'
|
||||
import { open } from '@tauri-apps/plugin-dialog'
|
||||
import { installVersionDependencies } from '@/store/install.js'
|
||||
import { handleError } from '@/store/notifications.js'
|
||||
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||
import { useTheming } from '@/store/theme.js'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { tauri } from '@tauri-apps/api'
|
||||
import { convertFileSrc } from '@tauri-apps/api/core'
|
||||
|
||||
const themeStore = useTheming()
|
||||
const router = useRouter()
|
||||
@ -153,7 +153,7 @@ const upload_icon = async () => {
|
||||
})
|
||||
|
||||
if (!icon.value) return
|
||||
display_icon.value = tauri.convertFileSrc(icon.value)
|
||||
display_icon.value = convertFileSrc(icon.value)
|
||||
}
|
||||
|
||||
const reset_icon = () => {
|
||||
|
20
apps/app-frontend/src/composables/macCssFix.js
Normal file
20
apps/app-frontend/src/composables/macCssFix.js
Normal file
@ -0,0 +1,20 @@
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
import cssContent from '@/assets/stylesheets/macFix.css?inline'
|
||||
|
||||
export async function useCheckDisableMouseover() {
|
||||
try {
|
||||
// Fetch the CSS content from the Rust backend
|
||||
let should_disable_mouseover = await invoke('plugin:utils|should_disable_mouseover')
|
||||
|
||||
if (should_disable_mouseover) {
|
||||
// Create a style element and set its content
|
||||
const styleElement = document.createElement('style')
|
||||
styleElement.innerHTML = cssContent
|
||||
|
||||
// Append the style element to the document's head
|
||||
document.head.appendChild(styleElement)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error checking OS version from Rust backend', error)
|
||||
}
|
||||
}
|
@ -3,7 +3,7 @@
|
||||
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
|
||||
* and deserialized into a usable JS object.
|
||||
*/
|
||||
import { invoke } from '@tauri-apps/api/tauri'
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
|
||||
// Example function:
|
||||
// User goes to auth_url to complete flow, and when completed, authenticate_await_completion() returns the credentials
|
||||
@ -13,35 +13,46 @@ import { invoke } from '@tauri-apps/api/tauri'
|
||||
// await authenticate_await_completion()
|
||||
// }
|
||||
|
||||
/// Authenticate a user with Hydra - part 1
|
||||
/// This begins the authentication flow quasi-synchronously
|
||||
/// This returns a DeviceLoginSuccess object, with two relevant fields:
|
||||
/// - verification_uri: the URL to go to to complete the flow
|
||||
/// - user_code: the code to enter on the verification_uri page
|
||||
/**
|
||||
* Authenticate a user with Hydra - part 1.
|
||||
* This begins the authentication flow quasi-synchronously.
|
||||
*
|
||||
* @returns {Promise<DeviceLoginSuccess>} A DeviceLoginSuccess object with two relevant fields:
|
||||
* @property {string} verification_uri - The URL to go to complete the flow.
|
||||
* @property {string} user_code - The code to enter on the verification_uri page.
|
||||
*/
|
||||
export async function login() {
|
||||
return await invoke('auth_login')
|
||||
return await invoke('plugin:auth|login')
|
||||
}
|
||||
|
||||
/// Retrieves the default user
|
||||
/// user is UUID
|
||||
/**
|
||||
* Retrieves the default user
|
||||
* @return {Promise<UUID | undefined>}
|
||||
*/
|
||||
export async function get_default_user() {
|
||||
return await invoke('plugin:auth|auth_get_default_user')
|
||||
return await invoke('plugin:auth|get_default_user')
|
||||
}
|
||||
|
||||
/// Updates the default user
|
||||
/// user is UUID
|
||||
/**
|
||||
* Updates the default user
|
||||
* @param {UUID} user
|
||||
*/
|
||||
export async function set_default_user(user) {
|
||||
return await invoke('plugin:auth|auth_set_default_user', { user })
|
||||
return await invoke('plugin:auth|set_default_user', { user })
|
||||
}
|
||||
|
||||
/// Remove a user account from the database
|
||||
/// user is UUID
|
||||
/**
|
||||
* Remove a user account from the database
|
||||
* @param {UUID} user
|
||||
*/
|
||||
export async function remove_user(user) {
|
||||
return await invoke('plugin:auth|auth_remove_user', { user })
|
||||
return await invoke('plugin:auth|remove_user', { user })
|
||||
}
|
||||
|
||||
/// Returns a list of users
|
||||
/// Returns an Array of Credentials
|
||||
/**
|
||||
* Returns a list of users
|
||||
* @returns {Promise<Credential[]>}
|
||||
*/
|
||||
export async function users() {
|
||||
return await invoke('plugin:auth|auth_users')
|
||||
return await invoke('plugin:auth|get_users')
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { invoke } from '@tauri-apps/api/tauri'
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
|
||||
export async function get_project(id, cacheBehaviour) {
|
||||
return await invoke('plugin:cache|get_project', { id, cacheBehaviour })
|
||||
|
@ -3,7 +3,7 @@
|
||||
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
|
||||
* and deserialized into a usable JS object.
|
||||
*/
|
||||
import { invoke } from '@tauri-apps/api/tauri'
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
import { create } from './profile'
|
||||
|
||||
/*
|
||||
@ -27,7 +27,7 @@ import { create } from './profile'
|
||||
/// eg: get_importable_instances("MultiMC", "C:/MultiMC")
|
||||
/// returns ["Instance 1", "Instance 2"]
|
||||
export async function get_importable_instances(launcherType, basePath) {
|
||||
return await invoke('plugin:import|import_get_importable_instances', { launcherType, basePath })
|
||||
return await invoke('plugin:import|get_importable_instances', { launcherType, basePath })
|
||||
}
|
||||
|
||||
/// Import an instance from a launcher type and base path
|
||||
@ -38,7 +38,7 @@ export async function import_instance(launcherType, basePath, instanceFolder) {
|
||||
// fs watching will be enabled once the instance is imported
|
||||
const profilePath = await create(instanceFolder, '1.19.4', 'vanilla', 'latest', null, true)
|
||||
|
||||
return await invoke('plugin:import|import_import_instance', {
|
||||
return await invoke('plugin:import|import_instance', {
|
||||
profilePath,
|
||||
launcherType,
|
||||
basePath,
|
||||
@ -49,7 +49,7 @@ export async function import_instance(launcherType, basePath, instanceFolder) {
|
||||
/// Checks if this instance is valid for importing, given a certain launcher type
|
||||
/// eg: is_valid_importable_instance("C:/MultiMC/Instance 1", "MultiMC")
|
||||
export async function is_valid_importable_instance(instanceFolder, launcherType) {
|
||||
return await invoke('plugin:import|import_is_valid_importable_instance', {
|
||||
return await invoke('plugin:import|is_valid_importable_instance', {
|
||||
instanceFolder,
|
||||
launcherType,
|
||||
})
|
||||
@ -59,5 +59,5 @@ export async function is_valid_importable_instance(instanceFolder, launcherType)
|
||||
/// null if it can't be found or doesn't exist
|
||||
/// eg: get_default_launcher_path("MultiMC")
|
||||
export async function get_default_launcher_path(launcherType) {
|
||||
return await invoke('plugin:import|import_get_default_launcher_path', { launcherType })
|
||||
return await invoke('plugin:import|get_default_launcher_path', { launcherType })
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
|
||||
* and deserialized into a usable JS object.
|
||||
*/
|
||||
import { invoke } from '@tauri-apps/api/tauri'
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
|
||||
/*
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
|
||||
* and deserialized into a usable JS object.
|
||||
*/
|
||||
import { invoke } from '@tauri-apps/api/tauri'
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
|
||||
/*
|
||||
A log is a struct containing the filename string, stdout, and stderr, as follows:
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { invoke } from '@tauri-apps/api/tauri'
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
|
||||
/// Gets the game versions from daedalus
|
||||
// Returns a VersionManifest
|
||||
|
@ -3,22 +3,22 @@
|
||||
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
|
||||
* and deserialized into a usable JS object.
|
||||
*/
|
||||
import { invoke } from '@tauri-apps/api/tauri'
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
|
||||
export async function login(provider) {
|
||||
return await invoke('modrinth_auth_login', { provider })
|
||||
}
|
||||
|
||||
export async function login_pass(username, password, challenge) {
|
||||
return await invoke('plugin:mr_auth|login_pass', { username, password, challenge })
|
||||
return await invoke('plugin:mr-auth|login_pass', { username, password, challenge })
|
||||
}
|
||||
|
||||
export async function login_2fa(code, flow) {
|
||||
return await invoke('plugin:mr_auth|login_2fa', { code, flow })
|
||||
return await invoke('plugin:mr-auth|login_2fa', { code, flow })
|
||||
}
|
||||
|
||||
export async function create_account(username, email, password, challenge, signUpNewsletter) {
|
||||
return await invoke('plugin:mr_auth|create_account', {
|
||||
return await invoke('plugin:mr-auth|create_account', {
|
||||
username,
|
||||
email,
|
||||
password,
|
||||
@ -28,9 +28,9 @@ export async function create_account(username, email, password, challenge, signU
|
||||
}
|
||||
|
||||
export async function logout() {
|
||||
return await invoke('plugin:mr_auth|logout')
|
||||
return await invoke('plugin:mr-auth|logout')
|
||||
}
|
||||
|
||||
export async function get() {
|
||||
return await invoke('plugin:mr_auth|get')
|
||||
return await invoke('plugin:mr-auth|get')
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
|
||||
* and deserialized into a usable JS object.
|
||||
*/
|
||||
import { invoke } from '@tauri-apps/api/tauri'
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
import { create } from './profile'
|
||||
|
||||
// Installs pack from a version ID
|
||||
|
@ -3,7 +3,7 @@
|
||||
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
|
||||
* and deserialized into a usable JS object.
|
||||
*/
|
||||
import { invoke } from '@tauri-apps/api/tauri'
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
|
||||
/// Gets all running process IDs with a given profile path
|
||||
/// Returns [u32]
|
||||
|
@ -3,7 +3,7 @@
|
||||
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
|
||||
* and deserialized into a usable JS object.
|
||||
*/
|
||||
import { invoke } from '@tauri-apps/api/tauri'
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
|
||||
/// Add instance
|
||||
/*
|
||||
@ -19,7 +19,7 @@ import { invoke } from '@tauri-apps/api/tauri'
|
||||
export async function create(name, gameVersion, modloader, loaderVersion, iconPath, skipInstall) {
|
||||
//Trim string name to avoid "Unable to find directory"
|
||||
name = name.trim()
|
||||
return await invoke('plugin:profile_create|profile_create', {
|
||||
return await invoke('plugin:profile-create|profile_create', {
|
||||
name,
|
||||
gameVersion,
|
||||
modloader,
|
||||
@ -31,7 +31,7 @@ export async function create(name, gameVersion, modloader, loaderVersion, iconPa
|
||||
|
||||
// duplicate a profile
|
||||
export async function duplicate(path) {
|
||||
return await invoke('plugin:profile_create|profile_duplicate', { path })
|
||||
return await invoke('plugin:profile-create|profile_duplicate', { path })
|
||||
}
|
||||
|
||||
// Remove a profile
|
||||
|
@ -3,7 +3,7 @@
|
||||
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
|
||||
* and deserialized into a usable JS object.
|
||||
*/
|
||||
import { invoke } from '@tauri-apps/api/tauri'
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
|
||||
// Settings object
|
||||
/*
|
||||
|
@ -3,7 +3,7 @@
|
||||
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
|
||||
* and deserialized into a usable JS object.
|
||||
*/
|
||||
import { invoke } from '@tauri-apps/api/tauri'
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
|
||||
// Initialize the theseus API state
|
||||
// This should be called during the initializion/opening of the launcher
|
||||
|
@ -3,7 +3,7 @@
|
||||
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
|
||||
* and deserialized into a usable JS object.
|
||||
*/
|
||||
import { invoke } from '@tauri-apps/api/tauri'
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
|
||||
// Gets cached category tags
|
||||
export async function get_categories() {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { get_full_path, get_mod_full_path } from '@/helpers/profile'
|
||||
import { invoke } from '@tauri-apps/api/tauri'
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
|
||||
export async function isDev() {
|
||||
return await invoke('is_dev')
|
||||
|
@ -4,7 +4,6 @@ import App from '@/App.vue'
|
||||
import { createPinia } from 'pinia'
|
||||
import FloatingVue from 'floating-vue'
|
||||
import 'floating-vue/dist/style.css'
|
||||
import loadCssMixin from './mixins/macCssFix.js'
|
||||
import { createPlugin } from '@vintl/vintl/plugin'
|
||||
|
||||
const VIntlPlugin = createPlugin({
|
||||
@ -30,7 +29,6 @@ let app = createApp(App)
|
||||
app.use(router)
|
||||
app.use(pinia)
|
||||
app.use(FloatingVue)
|
||||
app.mixin(loadCssMixin)
|
||||
app.use(VIntlPlugin)
|
||||
|
||||
app.mount('#app')
|
||||
|
@ -1,27 +0,0 @@
|
||||
import { invoke } from '@tauri-apps/api/tauri'
|
||||
import cssContent from '@/assets/stylesheets/macFix.css?inline'
|
||||
|
||||
export default {
|
||||
async mounted() {
|
||||
await this.checkDisableMouseover()
|
||||
},
|
||||
methods: {
|
||||
async checkDisableMouseover() {
|
||||
try {
|
||||
// Fetch the CSS content from the Rust backend
|
||||
const should_disable_mouseover = await invoke('plugin:utils|should_disable_mouseover')
|
||||
|
||||
if (should_disable_mouseover) {
|
||||
// Create a style element and set its content
|
||||
const styleElement = document.createElement('style')
|
||||
styleElement.innerHTML = cssContent
|
||||
|
||||
// Append the style element to the document's head
|
||||
document.head.appendChild(styleElement)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error checking OS version from Rust backend', error)
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
@ -19,7 +19,7 @@ import { get_categories, get_loaders, get_game_versions } from '@/helpers/tags'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import SearchCard from '@/components/ui/SearchCard.vue'
|
||||
import { get as getInstance, get_projects as getInstanceProjects } from '@/helpers/profile.js'
|
||||
import { convertFileSrc } from '@tauri-apps/api/tauri'
|
||||
import { convertFileSrc } from '@tauri-apps/api/core'
|
||||
import { get_search_results } from '@/helpers/cache.js'
|
||||
import { debounce } from '@/helpers/utils.js'
|
||||
import PromotionWrapper from '@/components/ui/PromotionWrapper.vue'
|
||||
|
@ -9,7 +9,7 @@ import { get as getCreds, logout } from '@/helpers/mr_auth.js'
|
||||
import JavaSelector from '@/components/ui/JavaSelector.vue'
|
||||
import ModrinthLoginScreen from '@/components/ui/tutorial/ModrinthLoginScreen.vue'
|
||||
import { mixpanel_opt_out_tracking, mixpanel_opt_in_tracking } from '@/helpers/mixpanel'
|
||||
import { open } from '@tauri-apps/api/dialog'
|
||||
import { open } from '@tauri-apps/plugin-dialog'
|
||||
import { getOS } from '@/helpers/utils.js'
|
||||
import { getVersion } from '@tauri-apps/api/app'
|
||||
import { get_user, purge_cache_types } from '@/helpers/cache.js'
|
||||
|
@ -132,7 +132,7 @@ import { handleError, useBreadcrumbs, useLoading } from '@/store/state'
|
||||
import { showProfileInFolder } from '@/helpers/utils.js'
|
||||
import ContextMenu from '@/components/ui/ContextMenu.vue'
|
||||
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||
import { convertFileSrc } from '@tauri-apps/api/tauri'
|
||||
import { convertFileSrc } from '@tauri-apps/api/core'
|
||||
import { useFetch } from '@/helpers/fetch'
|
||||
import { handleSevereError } from '@/store/error.js'
|
||||
import { get_project, get_version_many } from '@/helpers/cache.js'
|
||||
|
@ -541,8 +541,8 @@ import { computed, readonly, ref, shallowRef, watch } from 'vue'
|
||||
import { get_max_memory } from '@/helpers/jre.js'
|
||||
import { get } from '@/helpers/settings.js'
|
||||
import JavaSelector from '@/components/ui/JavaSelector.vue'
|
||||
import { convertFileSrc } from '@tauri-apps/api/tauri'
|
||||
import { open } from '@tauri-apps/api/dialog'
|
||||
import { convertFileSrc } from '@tauri-apps/api/core'
|
||||
import { open } from '@tauri-apps/plugin-dialog'
|
||||
import { get_loader_versions } from '@/helpers/metadata.js'
|
||||
import { get_game_versions, get_loaders } from '@/helpers/tags.js'
|
||||
import { handleError } from '@/store/notifications.js'
|
||||
|
@ -257,7 +257,7 @@ import { useRoute } from 'vue-router'
|
||||
import { ref, shallowRef, watch } from 'vue'
|
||||
import { useBreadcrumbs } from '@/store/breadcrumbs'
|
||||
import { handleError } from '@/store/notifications.js'
|
||||
import { convertFileSrc } from '@tauri-apps/api/tauri'
|
||||
import { convertFileSrc } from '@tauri-apps/api/core'
|
||||
import ContextMenu from '@/components/ui/ContextMenu.vue'
|
||||
import { install as installVersion } from '@/store/install.js'
|
||||
import { get_project, get_project_many, get_team, get_version_many } from '@/helpers/cache.js'
|
||||
|
@ -139,7 +139,7 @@ export default new createRouter({
|
||||
linkExactActiveClass: 'router-link-exact-active',
|
||||
scrollBehavior() {
|
||||
// Sometimes Vue's scroll behavior is not working as expected, so we need to manually scroll to top (especially on Linux)
|
||||
document.querySelector('.router-view').scrollTop = 0
|
||||
document.querySelector('.router-view')?.scrollTo(0, 0)
|
||||
return {
|
||||
el: '.router-view',
|
||||
top: 0,
|
||||
|
@ -10,14 +10,13 @@ theseus = { path = "../../packages/app-lib", features = ["cli"] }
|
||||
|
||||
serde_json = "1.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
tauri = { version = "1.7.1", features = ["shell-open"] }
|
||||
tauri = "2.0.0-rc.4"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
thiserror = "1.0"
|
||||
url = "2.2"
|
||||
webbrowser = "0.8.13"
|
||||
dunce = "1.0.3"
|
||||
|
||||
tokio-stream = { version = "0.1", features = ["fs"] }
|
||||
futures = "0.3"
|
||||
uuid = { version = "1.1", features = ["serde", "v4"] }
|
||||
|
||||
|
@ -1,17 +1,14 @@
|
||||
[package]
|
||||
name = "theseus_gui"
|
||||
version = "0.8.3-1"
|
||||
description = "A Tauri App"
|
||||
authors = ["you"]
|
||||
license = ""
|
||||
repository = ""
|
||||
description = "The Modrinth App is a desktop application for managing your Minecraft mods"
|
||||
license = "GPL-3.0-only"
|
||||
repository = "https://github.com/modrinth/code/apps/app/"
|
||||
edition = "2021"
|
||||
build = "build.rs"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { version = "1.5.3", features = [] }
|
||||
tauri-build = { version = "2.0.0-rc", features = ["codegen"] }
|
||||
|
||||
[dependencies]
|
||||
theseus = { path = "../../packages/app-lib", features = ["tauri"] }
|
||||
@ -19,14 +16,16 @@ theseus = { path = "../../packages/app-lib", features = ["tauri"] }
|
||||
serde_json = "1.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
|
||||
tauri = { version = "1.7.1", features = [ "app-all", "devtools", "dialog", "dialog-confirm", "dialog-open", "macos-private-api", "os-all", "protocol-asset", "shell-open", "window-close", "window-create", "window-hide", "window-maximize", "window-minimize", "window-set-decorations", "window-show", "window-start-dragging", "window-unmaximize", "window-unminimize"] }
|
||||
tauri-plugin-single-instance = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" }
|
||||
tauri-plugin-window-state = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" }
|
||||
tauri-plugin-deep-link = "0.1.2"
|
||||
tauri = { version = "2.0.0-rc.6", features = ["devtools", "macos-private-api", "protocol-asset", "unstable"] }
|
||||
tauri-plugin-window-state = "2.0.0-rc"
|
||||
tauri-plugin-deep-link = "2.0.0-rc"
|
||||
tauri-plugin-os = "2.0.0-rc"
|
||||
tauri-plugin-shell = "2.0.0-rc"
|
||||
tauri-plugin-dialog = "2.0.0-rc"
|
||||
tauri-plugin-updater = { version = "2.0.0-rc.1", optional = true }
|
||||
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
thiserror = "1.0"
|
||||
tokio-stream = { version = "0.1", features = ["fs"] }
|
||||
futures = "0.3"
|
||||
daedalus = "0.2.3"
|
||||
chrono = "0.4.26"
|
||||
@ -56,6 +55,7 @@ window-shadows = "0.2.1"
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
cocoa = "0.25.0"
|
||||
objc = "0.2.7"
|
||||
rand = "0.8.5"
|
||||
|
||||
[features]
|
||||
# by default Tauri runs in production mode
|
||||
@ -64,3 +64,4 @@ default = ["custom-protocol"]
|
||||
# this feature is used for production builds where `devPath` points to the filesystem
|
||||
# DO NOT remove this
|
||||
custom-protocol = ["tauri/custom-protocol"]
|
||||
updater = ["dep:tauri-plugin-updater"]
|
||||
|
@ -1,4 +1,223 @@
|
||||
use tauri_build::{DefaultPermissionRule, InlinedPlugin};
|
||||
|
||||
fn main() {
|
||||
// Build the Tauri app
|
||||
tauri_build::build();
|
||||
// Sadly, there is no better way to do it right now
|
||||
// You could try parsing source code here and detecting #[tauri::command]
|
||||
// But I think it's not worth it
|
||||
// https://github.com/tauri-apps/tauri/issues/10075
|
||||
tauri_build::try_build(
|
||||
tauri_build::Attributes::new()
|
||||
.codegen(tauri_build::CodegenContext::new())
|
||||
.plugin(
|
||||
"auth",
|
||||
InlinedPlugin::new()
|
||||
.commands(&[
|
||||
"login",
|
||||
"remove_user",
|
||||
"get_default_user",
|
||||
"set_default_user",
|
||||
"get_users",
|
||||
])
|
||||
.default_permission(
|
||||
DefaultPermissionRule::AllowAllCommands,
|
||||
),
|
||||
)
|
||||
.plugin(
|
||||
"cache",
|
||||
InlinedPlugin::new()
|
||||
.commands(&[
|
||||
"get_project",
|
||||
"get_project_many",
|
||||
"get_version",
|
||||
"get_version_many",
|
||||
"get_user",
|
||||
"get_user_many",
|
||||
"get_team",
|
||||
"get_team_many",
|
||||
"get_organization",
|
||||
"get_organization_many",
|
||||
"get_search_results",
|
||||
"get_search_results_many",
|
||||
"purge_cache_types",
|
||||
])
|
||||
.default_permission(
|
||||
DefaultPermissionRule::AllowAllCommands,
|
||||
),
|
||||
)
|
||||
.plugin(
|
||||
"import",
|
||||
InlinedPlugin::new()
|
||||
.commands(&[
|
||||
"get_importable_instances",
|
||||
"import_instance",
|
||||
"is_valid_importable_instance",
|
||||
"get_default_launcher_path",
|
||||
])
|
||||
.default_permission(
|
||||
DefaultPermissionRule::AllowAllCommands,
|
||||
),
|
||||
)
|
||||
.plugin(
|
||||
"jre",
|
||||
InlinedPlugin::new()
|
||||
.commands(&[
|
||||
"get_java_versions",
|
||||
"set_java_versions",
|
||||
"jre_find_filtered_jres",
|
||||
"jre_get_jre",
|
||||
"jre_test_jre",
|
||||
"jre_auto_install_java",
|
||||
"jre_get_max_memory",
|
||||
])
|
||||
.default_permission(
|
||||
DefaultPermissionRule::AllowAllCommands,
|
||||
),
|
||||
)
|
||||
.plugin(
|
||||
"logs",
|
||||
InlinedPlugin::new()
|
||||
.commands(&[
|
||||
"logs_get_logs",
|
||||
"logs_get_logs_by_filename",
|
||||
"logs_get_output_by_filename",
|
||||
"logs_delete_logs",
|
||||
"logs_delete_logs_by_filename",
|
||||
"logs_get_latest_log_cursor",
|
||||
])
|
||||
.default_permission(
|
||||
DefaultPermissionRule::AllowAllCommands,
|
||||
),
|
||||
)
|
||||
.plugin(
|
||||
"metadata",
|
||||
InlinedPlugin::new()
|
||||
.commands(&[
|
||||
"metadata_get_game_versions",
|
||||
"metadata_get_loader_versions",
|
||||
])
|
||||
.default_permission(
|
||||
DefaultPermissionRule::AllowAllCommands,
|
||||
),
|
||||
)
|
||||
.plugin(
|
||||
"mr-auth",
|
||||
InlinedPlugin::new()
|
||||
.commands(&[
|
||||
"login_pass",
|
||||
"login_2fa",
|
||||
"create_account",
|
||||
"logout",
|
||||
"get",
|
||||
])
|
||||
.default_permission(
|
||||
DefaultPermissionRule::AllowAllCommands,
|
||||
),
|
||||
)
|
||||
.plugin(
|
||||
"pack",
|
||||
InlinedPlugin::new()
|
||||
.commands(&["pack_install", "pack_get_profile_from_pack"])
|
||||
.default_permission(
|
||||
DefaultPermissionRule::AllowAllCommands,
|
||||
),
|
||||
)
|
||||
.plugin(
|
||||
"process",
|
||||
InlinedPlugin::new()
|
||||
.commands(&[
|
||||
"process_get_all",
|
||||
"process_get_by_profile_path",
|
||||
"process_kill",
|
||||
"process_wait_for",
|
||||
])
|
||||
.default_permission(
|
||||
DefaultPermissionRule::AllowAllCommands,
|
||||
),
|
||||
)
|
||||
.plugin(
|
||||
"profile",
|
||||
InlinedPlugin::new()
|
||||
.commands(&[
|
||||
"profile_remove",
|
||||
"profile_get",
|
||||
"profile_get_many",
|
||||
"profile_get_projects",
|
||||
"profile_get_optimal_jre_key",
|
||||
"profile_get_full_path",
|
||||
"profile_get_mod_full_path",
|
||||
"profile_list",
|
||||
"profile_check_installed",
|
||||
"profile_install",
|
||||
"profile_update_all",
|
||||
"profile_update_project",
|
||||
"profile_add_project_from_version",
|
||||
"profile_add_project_from_path",
|
||||
"profile_toggle_disable_project",
|
||||
"profile_remove_project",
|
||||
"profile_update_managed_modrinth_version",
|
||||
"profile_repair_managed_modrinth",
|
||||
"profile_run",
|
||||
"profile_run_credentials",
|
||||
"profile_kill",
|
||||
"profile_edit",
|
||||
"profile_edit_icon",
|
||||
"profile_export_mrpack",
|
||||
"profile_get_pack_export_candidates",
|
||||
])
|
||||
.default_permission(
|
||||
DefaultPermissionRule::AllowAllCommands,
|
||||
),
|
||||
)
|
||||
.plugin(
|
||||
"profile-create",
|
||||
InlinedPlugin::new()
|
||||
.commands(&["profile_create", "profile_duplicate"])
|
||||
.default_permission(
|
||||
DefaultPermissionRule::AllowAllCommands,
|
||||
),
|
||||
)
|
||||
.plugin(
|
||||
"settings",
|
||||
InlinedPlugin::new()
|
||||
.commands(&[
|
||||
"settings_get",
|
||||
"settings_set",
|
||||
"cancel_directory_change",
|
||||
])
|
||||
.default_permission(
|
||||
DefaultPermissionRule::AllowAllCommands,
|
||||
),
|
||||
)
|
||||
.plugin(
|
||||
"tags",
|
||||
InlinedPlugin::new()
|
||||
.commands(&[
|
||||
"tags_get_categories",
|
||||
"tags_get_report_types",
|
||||
"tags_get_loaders",
|
||||
"tags_get_game_versions",
|
||||
"tags_get_donation_platforms",
|
||||
])
|
||||
.default_permission(
|
||||
DefaultPermissionRule::AllowAllCommands,
|
||||
),
|
||||
)
|
||||
.plugin(
|
||||
"utils",
|
||||
InlinedPlugin::new()
|
||||
.commands(&[
|
||||
"get_os",
|
||||
"should_disable_mouseover",
|
||||
"highlight_in_folder",
|
||||
"open_path",
|
||||
"show_launcher_logs_folder",
|
||||
"progress_bars_list",
|
||||
"get_opening_command",
|
||||
])
|
||||
.default_permission(
|
||||
DefaultPermissionRule::AllowAllCommands,
|
||||
),
|
||||
),
|
||||
)
|
||||
.expect("Failed to run tauri-build");
|
||||
}
|
||||
|
30
apps/app/capabilities/core.json
Normal file
30
apps/app/capabilities/core.json
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"identifier": "core",
|
||||
"description": "",
|
||||
"local": true,
|
||||
"windows": [
|
||||
"main"
|
||||
],
|
||||
"permissions": [
|
||||
"core:default",
|
||||
"core:path:default",
|
||||
"core:event:default",
|
||||
"core:window:default",
|
||||
"core:app:default",
|
||||
"core:resources:default",
|
||||
"core:menu:default",
|
||||
"core:tray:default",
|
||||
"core:window:allow-create",
|
||||
"core:window:allow-maximize",
|
||||
"core:window:allow-toggle-maximize",
|
||||
"core:window:allow-unmaximize",
|
||||
"core:window:allow-minimize",
|
||||
"core:window:allow-unminimize",
|
||||
"core:window:allow-show",
|
||||
"core:window:allow-hide",
|
||||
"core:window:allow-close",
|
||||
"core:window:allow-set-decorations",
|
||||
"core:window:allow-start-dragging",
|
||||
"core:webview:allow-set-webview-zoom"
|
||||
]
|
||||
}
|
40
apps/app/capabilities/plugins.json
Normal file
40
apps/app/capabilities/plugins.json
Normal file
@ -0,0 +1,40 @@
|
||||
{
|
||||
"identifier": "plugins",
|
||||
"description": "",
|
||||
"local": true,
|
||||
"windows": [
|
||||
"main"
|
||||
],
|
||||
"permissions": [
|
||||
"dialog:allow-open",
|
||||
"dialog:allow-confirm",
|
||||
"shell:allow-open",
|
||||
"os:allow-platform",
|
||||
"os:allow-version",
|
||||
"os:allow-os-type",
|
||||
"os:allow-family",
|
||||
"os:allow-arch",
|
||||
"os:allow-exe-extension",
|
||||
"os:allow-locale",
|
||||
"os:allow-hostname",
|
||||
"deep-link:default",
|
||||
"window-state:default",
|
||||
"window-state:allow-restore-state",
|
||||
"window-state:allow-save-window-state",
|
||||
|
||||
"auth:default",
|
||||
"import:default",
|
||||
"jre:default",
|
||||
"logs:default",
|
||||
"metadata:default",
|
||||
"mr-auth:default",
|
||||
"profile-create:default",
|
||||
"pack:default",
|
||||
"process:default",
|
||||
"profile:default",
|
||||
"cache:default",
|
||||
"settings:default",
|
||||
"tags:default",
|
||||
"utils:default"
|
||||
]
|
||||
}
|
1
apps/app/gen/schemas/acl-manifests.json
Normal file
1
apps/app/gen/schemas/acl-manifests.json
Normal file
File diff suppressed because one or more lines are too long
1
apps/app/gen/schemas/capabilities.json
Normal file
1
apps/app/gen/schemas/capabilities.json
Normal file
@ -0,0 +1 @@
|
||||
{"core":{"identifier":"core","description":"","local":true,"windows":["main"],"permissions":["core:default","core:path:default","core:event:default","core:window:default","core:app:default","core:resources:default","core:menu:default","core:tray:default","core:window:allow-create","core:window:allow-maximize","core:window:allow-toggle-maximize","core:window:allow-unmaximize","core:window:allow-minimize","core:window:allow-unminimize","core:window:allow-show","core:window:allow-hide","core:window:allow-close","core:window:allow-set-decorations","core:window:allow-start-dragging","core:webview:allow-set-webview-zoom"]},"plugins":{"identifier":"plugins","description":"","local":true,"windows":["main"],"permissions":["dialog:allow-open","dialog:allow-confirm","shell:allow-open","os:allow-platform","os:allow-version","os:allow-os-type","os:allow-family","os:allow-arch","os:allow-exe-extension","os:allow-locale","os:allow-hostname","deep-link:default","window-state:default","window-state:allow-restore-state","window-state:allow-save-window-state","auth:default","import:default","jre:default","logs:default","metadata:default","mr-auth:default","profile-create:default","pack:default","process:default","profile:default","cache:default","settings:default","tags:default","utils:default"]}}
|
4153
apps/app/gen/schemas/desktop-schema.json
Normal file
4153
apps/app/gen/schemas/desktop-schema.json
Normal file
File diff suppressed because it is too large
Load Diff
4216
apps/app/gen/schemas/linux-schema.json
Normal file
4216
apps/app/gen/schemas/linux-schema.json
Normal file
File diff suppressed because it is too large
Load Diff
4153
apps/app/gen/schemas/macOS-schema.json
Normal file
4153
apps/app/gen/schemas/macOS-schema.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -2,16 +2,17 @@
|
||||
"name": "@modrinth/app",
|
||||
"scripts": {
|
||||
"build": "tauri build",
|
||||
"tauri": "tauri",
|
||||
"dev": "tauri dev",
|
||||
"test": "cargo test",
|
||||
"lint": "cargo fmt --check && cargo clippy -- -D warnings",
|
||||
"fix": "cargo fmt && cargo clippy --fix"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tauri-apps/cli": "^1.6.0"
|
||||
"@tauri-apps/cli": "2.0.0-rc.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@modrinth/app-lib": "workspace:*",
|
||||
"@modrinth/app-frontend": "workspace:*"
|
||||
"@modrinth/app-frontend": "workspace:*",
|
||||
"@modrinth/app-lib": "workspace:*"
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,17 @@
|
||||
use crate::api::Result;
|
||||
use chrono::{Duration, Utc};
|
||||
use tauri::plugin::TauriPlugin;
|
||||
use tauri::{Manager, UserAttentionType};
|
||||
use tauri::{Manager, Runtime, UserAttentionType};
|
||||
use theseus::prelude::*;
|
||||
|
||||
pub fn init<R: tauri::Runtime>() -> TauriPlugin<R> {
|
||||
tauri::plugin::Builder::new("auth")
|
||||
pub fn init<R: Runtime>() -> TauriPlugin<R> {
|
||||
tauri::plugin::Builder::<R>::new("auth")
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
auth_get_default_user,
|
||||
auth_set_default_user,
|
||||
auth_remove_user,
|
||||
auth_users,
|
||||
login,
|
||||
remove_user,
|
||||
get_default_user,
|
||||
set_default_user,
|
||||
get_users,
|
||||
])
|
||||
.build()
|
||||
}
|
||||
@ -18,19 +19,21 @@ pub fn init<R: tauri::Runtime>() -> TauriPlugin<R> {
|
||||
/// Authenticate a user with Hydra - part 1
|
||||
/// This begins the authentication flow quasi-synchronously, returning a URL to visit (that the user will sign in at)
|
||||
#[tauri::command]
|
||||
pub async fn auth_login(app: tauri::AppHandle) -> Result<Option<Credentials>> {
|
||||
pub async fn login<R: Runtime>(
|
||||
app: tauri::AppHandle<R>,
|
||||
) -> Result<Option<Credentials>> {
|
||||
let flow = minecraft_auth::begin_login().await?;
|
||||
|
||||
let start = Utc::now();
|
||||
|
||||
if let Some(window) = app.get_window("signin") {
|
||||
if let Some(window) = app.get_webview_window("signin") {
|
||||
window.close()?;
|
||||
}
|
||||
|
||||
let window = tauri::WindowBuilder::new(
|
||||
let window = tauri::WebviewWindowBuilder::new(
|
||||
&app,
|
||||
"signin",
|
||||
tauri::WindowUrl::External(flow.redirect_uri.parse().map_err(
|
||||
tauri::WebviewUrl::External(flow.redirect_uri.parse().map_err(
|
||||
|_| {
|
||||
theseus::ErrorKind::OtherError(
|
||||
"Error parsing auth redirect URL".to_string(),
|
||||
@ -53,12 +56,12 @@ pub async fn auth_login(app: tauri::AppHandle) -> Result<Option<Credentials>> {
|
||||
}
|
||||
|
||||
if window
|
||||
.url()
|
||||
.url()?
|
||||
.as_str()
|
||||
.starts_with("https://login.live.com/oauth20_desktop.srf")
|
||||
{
|
||||
if let Some((_, code)) =
|
||||
window.url().query_pairs().find(|x| x.0 == "code")
|
||||
window.url()?.query_pairs().find(|x| x.0 == "code")
|
||||
{
|
||||
window.close()?;
|
||||
let val =
|
||||
@ -75,23 +78,22 @@ pub async fn auth_login(app: tauri::AppHandle) -> Result<Option<Credentials>> {
|
||||
Ok(None)
|
||||
}
|
||||
#[tauri::command]
|
||||
pub async fn auth_remove_user(user: uuid::Uuid) -> Result<()> {
|
||||
pub async fn remove_user(user: uuid::Uuid) -> Result<()> {
|
||||
Ok(minecraft_auth::remove_user(user).await?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn auth_get_default_user() -> Result<Option<uuid::Uuid>> {
|
||||
pub async fn get_default_user() -> Result<Option<uuid::Uuid>> {
|
||||
Ok(minecraft_auth::get_default_user().await?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn auth_set_default_user(user: uuid::Uuid) -> Result<()> {
|
||||
pub async fn set_default_user(user: uuid::Uuid) -> Result<()> {
|
||||
Ok(minecraft_auth::set_default_user(user).await?)
|
||||
}
|
||||
|
||||
/// Get a copy of the list of all user credentials
|
||||
// invoke('plugin:auth|auth_users',user)
|
||||
#[tauri::command]
|
||||
pub async fn auth_users() -> Result<Vec<Credentials>> {
|
||||
pub async fn get_users() -> Result<Vec<Credentials>> {
|
||||
Ok(minecraft_auth::users().await?)
|
||||
}
|
||||
|
@ -8,10 +8,10 @@ use theseus::pack::import;
|
||||
pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
|
||||
tauri::plugin::Builder::new("import")
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
import_get_importable_instances,
|
||||
import_import_instance,
|
||||
import_is_valid_importable_instance,
|
||||
import_get_default_launcher_path,
|
||||
get_importable_instances,
|
||||
import_instance,
|
||||
is_valid_importable_instance,
|
||||
get_default_launcher_path,
|
||||
])
|
||||
.build()
|
||||
}
|
||||
@ -20,7 +20,7 @@ pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
|
||||
/// eg: get_importable_instances(ImportLauncherType::MultiMC, PathBuf::from("C:/MultiMC"))
|
||||
/// returns ["Instance 1", "Instance 2"]
|
||||
#[tauri::command]
|
||||
pub async fn import_get_importable_instances(
|
||||
pub async fn get_importable_instances(
|
||||
launcher_type: ImportLauncherType,
|
||||
base_path: PathBuf,
|
||||
) -> Result<Vec<String>> {
|
||||
@ -31,7 +31,7 @@ pub async fn import_get_importable_instances(
|
||||
/// profile_path should be a blank profile for this purpose- if the function fails, it will be deleted
|
||||
/// eg: import_instance(ImportLauncherType::MultiMC, PathBuf::from("C:/MultiMC"), "Instance 1")
|
||||
#[tauri::command]
|
||||
pub async fn import_import_instance(
|
||||
pub async fn import_instance(
|
||||
profile_path: &str,
|
||||
launcher_type: ImportLauncherType,
|
||||
base_path: PathBuf,
|
||||
@ -50,7 +50,7 @@ pub async fn import_import_instance(
|
||||
/// Checks if this instance is valid for importing, given a certain launcher type
|
||||
/// eg: is_valid_importable_instance(PathBuf::from("C:/MultiMC/Instance 1"), ImportLauncherType::MultiMC)
|
||||
#[tauri::command]
|
||||
pub async fn import_is_valid_importable_instance(
|
||||
pub async fn is_valid_importable_instance(
|
||||
instance_folder: PathBuf,
|
||||
launcher_type: ImportLauncherType,
|
||||
) -> Result<bool> {
|
||||
@ -63,7 +63,7 @@ pub async fn import_is_valid_importable_instance(
|
||||
/// Returns the default path for the given launcher type
|
||||
/// None if it can't be found or doesn't exist
|
||||
#[tauri::command]
|
||||
pub async fn import_get_default_launcher_path(
|
||||
pub async fn get_default_launcher_path(
|
||||
launcher_type: ImportLauncherType,
|
||||
) -> Result<Option<PathBuf>> {
|
||||
Ok(import::get_default_launcher_path(launcher_type))
|
||||
|
@ -39,10 +39,6 @@ pub enum TheseusSerializableError {
|
||||
|
||||
#[error("Tauri error: {0}")]
|
||||
Tauri(#[from] tauri::Error),
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
#[error("Callback error: {0}")]
|
||||
Callback(String),
|
||||
}
|
||||
|
||||
// Generic implementation of From<T> for ErrorTypeA
|
||||
@ -90,14 +86,6 @@ macro_rules! impl_serialize {
|
||||
}
|
||||
|
||||
// Use the macro to implement Serialize for TheseusSerializableError
|
||||
#[cfg(target_os = "macos")]
|
||||
impl_serialize! {
|
||||
IO,
|
||||
Tauri,
|
||||
Callback
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
impl_serialize! {
|
||||
IO,
|
||||
Tauri,
|
||||
|
@ -5,7 +5,7 @@ use tauri::{Manager, UserAttentionType};
|
||||
use theseus::prelude::*;
|
||||
|
||||
pub fn init<R: tauri::Runtime>() -> TauriPlugin<R> {
|
||||
tauri::plugin::Builder::new("mr_auth")
|
||||
tauri::plugin::Builder::new("mr-auth")
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
login_pass,
|
||||
login_2fa,
|
||||
@ -25,14 +25,14 @@ pub async fn modrinth_auth_login(
|
||||
|
||||
let start = Utc::now();
|
||||
|
||||
if let Some(window) = app.get_window("modrinth-signin") {
|
||||
if let Some(window) = app.get_webview_window("modrinth-signin") {
|
||||
window.close()?;
|
||||
}
|
||||
|
||||
let window = tauri::WindowBuilder::new(
|
||||
let window = tauri::WebviewWindowBuilder::new(
|
||||
&app,
|
||||
"modrinth-signin",
|
||||
tauri::WindowUrl::External(redirect_uri.parse().map_err(|_| {
|
||||
tauri::WebviewUrl::External(redirect_uri.parse().map_err(|_| {
|
||||
theseus::ErrorKind::OtherError(
|
||||
"Error parsing auth redirect URL".to_string(),
|
||||
)
|
||||
@ -53,12 +53,12 @@ pub async fn modrinth_auth_login(
|
||||
}
|
||||
|
||||
if window
|
||||
.url()
|
||||
.url()?
|
||||
.as_str()
|
||||
.starts_with("https://launcher-files.modrinth.com/detect.txt")
|
||||
{
|
||||
let query = window
|
||||
.url()
|
||||
.url()?
|
||||
.query_pairs()
|
||||
.map(|(key, val)| {
|
||||
(
|
||||
|
@ -2,7 +2,7 @@ use crate::api::Result;
|
||||
use theseus::prelude::*;
|
||||
|
||||
pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
|
||||
tauri::plugin::Builder::new("profile_create")
|
||||
tauri::plugin::Builder::new("profile-create")
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
profile_create,
|
||||
profile_duplicate
|
||||
@ -11,7 +11,7 @@ pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
|
||||
}
|
||||
|
||||
// Creates a profile at the given filepath and adds it to the in-memory state
|
||||
// invoke('plugin:profile_create|profile_add',profile)
|
||||
// invoke('plugin:profile-create|profile_add',profile)
|
||||
#[tauri::command]
|
||||
pub async fn profile_create(
|
||||
name: String, // the name of the profile, and relative path
|
||||
@ -35,7 +35,7 @@ pub async fn profile_create(
|
||||
}
|
||||
|
||||
// Creates a profile from a duplicate
|
||||
// invoke('plugin:profile_create|profile_duplicate',profile)
|
||||
// invoke('plugin:profile-create|profile_duplicate',profile)
|
||||
#[tauri::command]
|
||||
pub async fn profile_duplicate(path: &str) -> Result<String> {
|
||||
let res = profile::create::profile_create_from_duplicate(path).await?;
|
||||
|
@ -1,98 +0,0 @@
|
||||
use cocoa::{
|
||||
base::{id, nil},
|
||||
foundation::NSAutoreleasePool,
|
||||
};
|
||||
use objc::{
|
||||
class,
|
||||
declare::ClassDecl,
|
||||
msg_send,
|
||||
runtime::{Class, Object, Sel},
|
||||
sel, sel_impl,
|
||||
};
|
||||
use once_cell::sync::OnceCell;
|
||||
|
||||
use crate::api::TheseusSerializableError;
|
||||
|
||||
type Callback = OnceCell<Box<dyn Fn(String) + Send + Sync + 'static>>;
|
||||
|
||||
static CALLBACK: Callback = OnceCell::new();
|
||||
|
||||
pub struct AppDelegateClass(pub *const Class);
|
||||
unsafe impl Send for AppDelegateClass {}
|
||||
unsafe impl Sync for AppDelegateClass {}
|
||||
|
||||
// Obj C class for the app delegate
|
||||
// This inherits from the TaoAppDelegate (used by tauri) so we do not accidentally override any functionality
|
||||
// The application_open_file method is the only method we override, as it is currently unimplemented in tauri
|
||||
lazy_static::lazy_static! {
|
||||
pub static ref THESEUS_APP_DELEGATE_CLASS: AppDelegateClass = unsafe {
|
||||
let superclass = class!(TaoAppDelegate);
|
||||
let mut decl = ClassDecl::new("TheseusAppDelegate", superclass).unwrap();
|
||||
|
||||
// Add the method to the class
|
||||
decl.add_method(
|
||||
sel!(application:openFile:),
|
||||
application_open_file as extern "C" fn(&Object, Sel, id, id) -> bool,
|
||||
);
|
||||
|
||||
// Other methods are inherited
|
||||
|
||||
AppDelegateClass(decl.register())
|
||||
};
|
||||
}
|
||||
|
||||
extern "C" fn application_open_file(
|
||||
_: &Object,
|
||||
_: Sel,
|
||||
_: id,
|
||||
file: id,
|
||||
) -> bool {
|
||||
let file = nsstring_to_string(file);
|
||||
callback(file)
|
||||
}
|
||||
|
||||
pub fn callback(file: String) -> bool {
|
||||
if let Some(callback) = CALLBACK.get() {
|
||||
callback(file);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register_open_file<T>(
|
||||
callback: T,
|
||||
) -> Result<(), TheseusSerializableError>
|
||||
where
|
||||
T: Fn(String) + Send + Sync + 'static,
|
||||
{
|
||||
unsafe {
|
||||
// Modified from tao: https://github.com/tauri-apps/tao
|
||||
// sets the current app delegate to be the inherited app delegate rather than the default tauri/tao one
|
||||
let app: id = msg_send![class!(TaoApp), sharedApplication];
|
||||
|
||||
let delegate: id = msg_send![THESEUS_APP_DELEGATE_CLASS.0, new];
|
||||
let pool = NSAutoreleasePool::new(nil);
|
||||
let _: () = msg_send![app, setDelegate: delegate];
|
||||
let _: () = msg_send![pool, drain];
|
||||
}
|
||||
CALLBACK.set(Box::new(callback)).map_err(|_| {
|
||||
TheseusSerializableError::Callback("Callback already set".to_string())
|
||||
})
|
||||
}
|
||||
|
||||
/// Convert an NSString to a Rust `String`
|
||||
/// From 'fruitbasket' https://github.com/mrmekon/fruitbasket/
|
||||
#[allow(clippy::cmp_null)]
|
||||
pub fn nsstring_to_string(nsstring: *mut Object) -> String {
|
||||
unsafe {
|
||||
let cstr: *const i8 = msg_send![nsstring, UTF8String];
|
||||
if cstr != std::ptr::null() {
|
||||
std::ffi::CStr::from_ptr(cstr)
|
||||
.to_string_lossy()
|
||||
.into_owned()
|
||||
} else {
|
||||
"".into()
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +1,2 @@
|
||||
pub mod deep_link;
|
||||
pub mod delegate;
|
||||
pub mod window_ext;
|
||||
|
@ -1,47 +1,43 @@
|
||||
/// from: https://github.com/tauri-apps/tauri/issues/4789, full credit to haasal
|
||||
#[cfg(target_os = "macos")]
|
||||
use tauri::{Runtime, Window};
|
||||
// Stolen from https://gist.github.com/charrondev/43150e940bd2771b1ea88256d491c7a9
|
||||
use objc::{msg_send, sel, sel_impl};
|
||||
use rand::{distributions::Alphanumeric, Rng};
|
||||
use tauri::{
|
||||
plugin::{Builder, TauriPlugin},
|
||||
Emitter, Runtime, Window,
|
||||
}; // 0.8
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
pub trait WindowExt {
|
||||
fn set_transparent_titlebar(&self, transparent: bool);
|
||||
fn position_traffic_lights(&self, x: f64, y: f64);
|
||||
const WINDOW_CONTROL_PAD_X: f64 = 9.0;
|
||||
const WINDOW_CONTROL_PAD_Y: f64 = 16.0;
|
||||
|
||||
struct UnsafeWindowHandle(*mut std::ffi::c_void);
|
||||
unsafe impl Send for UnsafeWindowHandle {}
|
||||
unsafe impl Sync for UnsafeWindowHandle {}
|
||||
|
||||
pub fn init<R: Runtime>() -> TauriPlugin<R> {
|
||||
Builder::new("traffic_light_positioner")
|
||||
.on_window_ready(|window| {
|
||||
#[cfg(target_os = "macos")]
|
||||
setup_traffic_light_positioner(window);
|
||||
})
|
||||
.build()
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
impl<R: Runtime> WindowExt for Window<R> {
|
||||
fn set_transparent_titlebar(&self, transparent: bool) {
|
||||
use cocoa::appkit::{NSWindow, NSWindowTitleVisibility};
|
||||
let window = self.ns_window().unwrap() as cocoa::base::id;
|
||||
|
||||
unsafe {
|
||||
window.setTitleVisibility_(
|
||||
NSWindowTitleVisibility::NSWindowTitleHidden,
|
||||
);
|
||||
|
||||
if transparent {
|
||||
window.setTitlebarAppearsTransparent_(cocoa::base::YES);
|
||||
} else {
|
||||
window.setTitlebarAppearsTransparent_(cocoa::base::NO);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn position_traffic_lights(&self, x: f64, y: f64) {
|
||||
fn position_traffic_lights(
|
||||
ns_window_handle: UnsafeWindowHandle,
|
||||
x: f64,
|
||||
y: f64,
|
||||
) {
|
||||
use cocoa::appkit::{NSView, NSWindow, NSWindowButton};
|
||||
use cocoa::foundation::NSRect;
|
||||
use objc::{msg_send, sel, sel_impl};
|
||||
|
||||
let window = self.ns_window().unwrap() as cocoa::base::id;
|
||||
|
||||
let ns_window = ns_window_handle.0 as cocoa::base::id;
|
||||
unsafe {
|
||||
let close = window
|
||||
let close = ns_window
|
||||
.standardWindowButton_(NSWindowButton::NSWindowCloseButton);
|
||||
let miniaturize = window.standardWindowButton_(
|
||||
NSWindowButton::NSWindowMiniaturizeButton,
|
||||
);
|
||||
let zoom = window
|
||||
.standardWindowButton_(NSWindowButton::NSWindowZoomButton);
|
||||
let miniaturize = ns_window
|
||||
.standardWindowButton_(NSWindowButton::NSWindowMiniaturizeButton);
|
||||
let zoom =
|
||||
ns_window.standardWindowButton_(NSWindowButton::NSWindowZoomButton);
|
||||
|
||||
let title_bar_container_view = close.superview().superview();
|
||||
|
||||
@ -52,13 +48,13 @@ impl<R: Runtime> WindowExt for Window<R> {
|
||||
let mut title_bar_rect = NSView::frame(title_bar_container_view);
|
||||
title_bar_rect.size.height = title_bar_frame_height;
|
||||
title_bar_rect.origin.y =
|
||||
NSView::frame(window).size.height - title_bar_frame_height;
|
||||
NSView::frame(ns_window).size.height - title_bar_frame_height;
|
||||
let _: () =
|
||||
msg_send![title_bar_container_view, setFrame: title_bar_rect];
|
||||
|
||||
let window_buttons = vec![close, miniaturize, zoom];
|
||||
let space_between = NSView::frame(miniaturize).origin.x
|
||||
- NSView::frame(close).origin.x;
|
||||
let space_between =
|
||||
NSView::frame(miniaturize).origin.x - NSView::frame(close).origin.x;
|
||||
|
||||
for (i, button) in window_buttons.into_iter().enumerate() {
|
||||
let mut rect: NSRect = NSView::frame(button);
|
||||
@ -66,5 +62,351 @@ impl<R: Runtime> WindowExt for Window<R> {
|
||||
button.setFrameOrigin(rect.origin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
#[derive(Debug)]
|
||||
struct WindowState<R: Runtime> {
|
||||
window: Window<R>,
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
pub fn setup_traffic_light_positioner<R: Runtime>(window: Window<R>) {
|
||||
use cocoa::appkit::NSWindow;
|
||||
use cocoa::base::{id, BOOL};
|
||||
use cocoa::foundation::NSUInteger;
|
||||
use objc::runtime::{Object, Sel};
|
||||
use std::ffi::c_void;
|
||||
|
||||
// Do the initial positioning
|
||||
position_traffic_lights(
|
||||
UnsafeWindowHandle(
|
||||
window.ns_window().expect("Failed to create window handle"),
|
||||
),
|
||||
WINDOW_CONTROL_PAD_X,
|
||||
WINDOW_CONTROL_PAD_Y,
|
||||
);
|
||||
|
||||
// Ensure they stay in place while resizing the window.
|
||||
fn with_window_state<R: Runtime, F: FnOnce(&mut WindowState<R>) -> T, T>(
|
||||
this: &Object,
|
||||
func: F,
|
||||
) {
|
||||
let ptr = unsafe {
|
||||
let x: *mut c_void = *this.get_ivar("app_box");
|
||||
&mut *(x as *mut WindowState<R>)
|
||||
};
|
||||
func(ptr);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let ns_win = window
|
||||
.ns_window()
|
||||
.expect("NS Window should exist to mount traffic light delegate.")
|
||||
as id;
|
||||
|
||||
let current_delegate: id = ns_win.delegate();
|
||||
|
||||
extern "C" fn on_window_should_close(
|
||||
this: &Object,
|
||||
_cmd: Sel,
|
||||
sender: id,
|
||||
) -> BOOL {
|
||||
unsafe {
|
||||
let super_del: id = *this.get_ivar("super_delegate");
|
||||
msg_send![super_del, windowShouldClose: sender]
|
||||
}
|
||||
}
|
||||
extern "C" fn on_window_will_close(
|
||||
this: &Object,
|
||||
_cmd: Sel,
|
||||
notification: id,
|
||||
) {
|
||||
unsafe {
|
||||
let super_del: id = *this.get_ivar("super_delegate");
|
||||
let _: () = msg_send![super_del, windowWillClose: notification];
|
||||
}
|
||||
}
|
||||
extern "C" fn on_window_did_resize<R: Runtime>(
|
||||
this: &Object,
|
||||
_cmd: Sel,
|
||||
notification: id,
|
||||
) {
|
||||
unsafe {
|
||||
with_window_state(this, |state: &mut WindowState<R>| {
|
||||
let id = state.window.ns_window().expect(
|
||||
"NS window should exist on state to handle resize",
|
||||
) as id;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
position_traffic_lights(
|
||||
UnsafeWindowHandle(id as *mut std::ffi::c_void),
|
||||
WINDOW_CONTROL_PAD_X,
|
||||
WINDOW_CONTROL_PAD_Y,
|
||||
);
|
||||
});
|
||||
|
||||
let super_del: id = *this.get_ivar("super_delegate");
|
||||
let _: () = msg_send![super_del, windowDidResize: notification];
|
||||
}
|
||||
}
|
||||
extern "C" fn on_window_did_move(
|
||||
this: &Object,
|
||||
_cmd: Sel,
|
||||
notification: id,
|
||||
) {
|
||||
unsafe {
|
||||
let super_del: id = *this.get_ivar("super_delegate");
|
||||
let _: () = msg_send![super_del, windowDidMove: notification];
|
||||
}
|
||||
}
|
||||
extern "C" fn on_window_did_change_backing_properties(
|
||||
this: &Object,
|
||||
_cmd: Sel,
|
||||
notification: id,
|
||||
) {
|
||||
unsafe {
|
||||
let super_del: id = *this.get_ivar("super_delegate");
|
||||
let _: () = msg_send![super_del, windowDidChangeBackingProperties: notification];
|
||||
}
|
||||
}
|
||||
extern "C" fn on_window_did_become_key(
|
||||
this: &Object,
|
||||
_cmd: Sel,
|
||||
notification: id,
|
||||
) {
|
||||
unsafe {
|
||||
let super_del: id = *this.get_ivar("super_delegate");
|
||||
let _: () =
|
||||
msg_send![super_del, windowDidBecomeKey: notification];
|
||||
}
|
||||
}
|
||||
extern "C" fn on_window_did_resign_key(
|
||||
this: &Object,
|
||||
_cmd: Sel,
|
||||
notification: id,
|
||||
) {
|
||||
unsafe {
|
||||
let super_del: id = *this.get_ivar("super_delegate");
|
||||
let _: () =
|
||||
msg_send![super_del, windowDidResignKey: notification];
|
||||
}
|
||||
}
|
||||
extern "C" fn on_dragging_entered(
|
||||
this: &Object,
|
||||
_cmd: Sel,
|
||||
notification: id,
|
||||
) -> BOOL {
|
||||
unsafe {
|
||||
let super_del: id = *this.get_ivar("super_delegate");
|
||||
msg_send![super_del, draggingEntered: notification]
|
||||
}
|
||||
}
|
||||
extern "C" fn on_prepare_for_drag_operation(
|
||||
this: &Object,
|
||||
_cmd: Sel,
|
||||
notification: id,
|
||||
) -> BOOL {
|
||||
unsafe {
|
||||
let super_del: id = *this.get_ivar("super_delegate");
|
||||
msg_send![super_del, prepareForDragOperation: notification]
|
||||
}
|
||||
}
|
||||
extern "C" fn on_perform_drag_operation(
|
||||
this: &Object,
|
||||
_cmd: Sel,
|
||||
sender: id,
|
||||
) -> BOOL {
|
||||
unsafe {
|
||||
let super_del: id = *this.get_ivar("super_delegate");
|
||||
msg_send![super_del, performDragOperation: sender]
|
||||
}
|
||||
}
|
||||
extern "C" fn on_conclude_drag_operation(
|
||||
this: &Object,
|
||||
_cmd: Sel,
|
||||
notification: id,
|
||||
) {
|
||||
unsafe {
|
||||
let super_del: id = *this.get_ivar("super_delegate");
|
||||
let _: () =
|
||||
msg_send![super_del, concludeDragOperation: notification];
|
||||
}
|
||||
}
|
||||
extern "C" fn on_dragging_exited(
|
||||
this: &Object,
|
||||
_cmd: Sel,
|
||||
notification: id,
|
||||
) {
|
||||
unsafe {
|
||||
let super_del: id = *this.get_ivar("super_delegate");
|
||||
let _: () = msg_send![super_del, draggingExited: notification];
|
||||
}
|
||||
}
|
||||
extern "C" fn on_window_will_use_full_screen_presentation_options(
|
||||
this: &Object,
|
||||
_cmd: Sel,
|
||||
window: id,
|
||||
proposed_options: NSUInteger,
|
||||
) -> NSUInteger {
|
||||
unsafe {
|
||||
let super_del: id = *this.get_ivar("super_delegate");
|
||||
msg_send![super_del, window: window willUseFullScreenPresentationOptions: proposed_options]
|
||||
}
|
||||
}
|
||||
extern "C" fn on_window_did_enter_full_screen<R: Runtime>(
|
||||
this: &Object,
|
||||
_cmd: Sel,
|
||||
notification: id,
|
||||
) {
|
||||
unsafe {
|
||||
with_window_state(this, |state: &mut WindowState<R>| {
|
||||
state
|
||||
.window
|
||||
.emit("did-enter-fullscreen", ())
|
||||
.expect("Failed to emit event");
|
||||
});
|
||||
|
||||
let super_del: id = *this.get_ivar("super_delegate");
|
||||
let _: () = msg_send![super_del, windowDidEnterFullScreen: notification];
|
||||
}
|
||||
}
|
||||
extern "C" fn on_window_will_enter_full_screen<R: Runtime>(
|
||||
this: &Object,
|
||||
_cmd: Sel,
|
||||
notification: id,
|
||||
) {
|
||||
unsafe {
|
||||
with_window_state(this, |state: &mut WindowState<R>| {
|
||||
state
|
||||
.window
|
||||
.emit("will-enter-fullscreen", ())
|
||||
.expect("Failed to emit event");
|
||||
});
|
||||
|
||||
let super_del: id = *this.get_ivar("super_delegate");
|
||||
let _: () = msg_send![super_del, windowWillEnterFullScreen: notification];
|
||||
}
|
||||
}
|
||||
extern "C" fn on_window_did_exit_full_screen<R: Runtime>(
|
||||
this: &Object,
|
||||
_cmd: Sel,
|
||||
notification: id,
|
||||
) {
|
||||
unsafe {
|
||||
with_window_state(this, |state: &mut WindowState<R>| {
|
||||
state
|
||||
.window
|
||||
.emit("did-exit-fullscreen", ())
|
||||
.expect("Failed to emit event");
|
||||
|
||||
let id =
|
||||
state.window.ns_window().expect("Failed to emit event")
|
||||
as id;
|
||||
position_traffic_lights(
|
||||
UnsafeWindowHandle(id as *mut std::ffi::c_void),
|
||||
WINDOW_CONTROL_PAD_X,
|
||||
WINDOW_CONTROL_PAD_Y,
|
||||
);
|
||||
});
|
||||
|
||||
let super_del: id = *this.get_ivar("super_delegate");
|
||||
let _: () =
|
||||
msg_send![super_del, windowDidExitFullScreen: notification];
|
||||
}
|
||||
}
|
||||
extern "C" fn on_window_will_exit_full_screen<R: Runtime>(
|
||||
this: &Object,
|
||||
_cmd: Sel,
|
||||
notification: id,
|
||||
) {
|
||||
unsafe {
|
||||
with_window_state(this, |state: &mut WindowState<R>| {
|
||||
state
|
||||
.window
|
||||
.emit("will-exit-fullscreen", ())
|
||||
.expect("Failed to emit event");
|
||||
});
|
||||
|
||||
let super_del: id = *this.get_ivar("super_delegate");
|
||||
let _: () = msg_send![super_del, windowWillExitFullScreen: notification];
|
||||
}
|
||||
}
|
||||
extern "C" fn on_window_did_fail_to_enter_full_screen(
|
||||
this: &Object,
|
||||
_cmd: Sel,
|
||||
window: id,
|
||||
) {
|
||||
unsafe {
|
||||
let super_del: id = *this.get_ivar("super_delegate");
|
||||
let _: () = msg_send![super_del, windowDidFailToEnterFullScreen: window];
|
||||
}
|
||||
}
|
||||
extern "C" fn on_effective_appearance_did_change(
|
||||
this: &Object,
|
||||
_cmd: Sel,
|
||||
notification: id,
|
||||
) {
|
||||
unsafe {
|
||||
let super_del: id = *this.get_ivar("super_delegate");
|
||||
let _: () = msg_send![super_del, effectiveAppearanceDidChange: notification];
|
||||
}
|
||||
}
|
||||
extern "C" fn on_effective_appearance_did_changed_on_main_thread(
|
||||
this: &Object,
|
||||
_cmd: Sel,
|
||||
notification: id,
|
||||
) {
|
||||
unsafe {
|
||||
let super_del: id = *this.get_ivar("super_delegate");
|
||||
let _: () = msg_send![
|
||||
super_del,
|
||||
effectiveAppearanceDidChangedOnMainThread: notification
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// Are we deallocing this properly ? (I miss safe Rust :( )
|
||||
let window_label = window.label().to_string();
|
||||
|
||||
let app_state = WindowState { window };
|
||||
let app_box = Box::into_raw(Box::new(app_state)) as *mut c_void;
|
||||
let random_str: String = rand::thread_rng()
|
||||
.sample_iter(&Alphanumeric)
|
||||
.take(20)
|
||||
.map(char::from)
|
||||
.collect();
|
||||
|
||||
// We need to ensure we have a unique delegate name, otherwise we will panic while trying to create a duplicate
|
||||
// delegate with the same name.
|
||||
let delegate_name =
|
||||
format!("windowDelegate_{}_{}", window_label, random_str);
|
||||
|
||||
ns_win.setDelegate_(delegate!(&delegate_name, {
|
||||
window: id = ns_win,
|
||||
app_box: *mut c_void = app_box,
|
||||
toolbar: id = cocoa::base::nil,
|
||||
super_delegate: id = current_delegate,
|
||||
(windowShouldClose:) => on_window_should_close as extern fn(&Object, Sel, id) -> BOOL,
|
||||
(windowWillClose:) => on_window_will_close as extern fn(&Object, Sel, id),
|
||||
(windowDidResize:) => on_window_did_resize::<R> as extern fn(&Object, Sel, id),
|
||||
(windowDidMove:) => on_window_did_move as extern fn(&Object, Sel, id),
|
||||
(windowDidChangeBackingProperties:) => on_window_did_change_backing_properties as extern fn(&Object, Sel, id),
|
||||
(windowDidBecomeKey:) => on_window_did_become_key as extern fn(&Object, Sel, id),
|
||||
(windowDidResignKey:) => on_window_did_resign_key as extern fn(&Object, Sel, id),
|
||||
(draggingEntered:) => on_dragging_entered as extern fn(&Object, Sel, id) -> BOOL,
|
||||
(prepareForDragOperation:) => on_prepare_for_drag_operation as extern fn(&Object, Sel, id) -> BOOL,
|
||||
(performDragOperation:) => on_perform_drag_operation as extern fn(&Object, Sel, id) -> BOOL,
|
||||
(concludeDragOperation:) => on_conclude_drag_operation as extern fn(&Object, Sel, id),
|
||||
(draggingExited:) => on_dragging_exited as extern fn(&Object, Sel, id),
|
||||
(window:willUseFullScreenPresentationOptions:) => on_window_will_use_full_screen_presentation_options as extern fn(&Object, Sel, id, NSUInteger) -> NSUInteger,
|
||||
(windowDidEnterFullScreen:) => on_window_did_enter_full_screen::<R> as extern fn(&Object, Sel, id),
|
||||
(windowWillEnterFullScreen:) => on_window_will_enter_full_screen::<R> as extern fn(&Object, Sel, id),
|
||||
(windowDidExitFullScreen:) => on_window_did_exit_full_screen::<R> as extern fn(&Object, Sel, id),
|
||||
(windowWillExitFullScreen:) => on_window_will_exit_full_screen::<R> as extern fn(&Object, Sel, id),
|
||||
(windowDidFailToEnterFullScreen:) => on_window_did_fail_to_enter_full_screen as extern fn(&Object, Sel, id),
|
||||
(effectiveAppearanceDidChange:) => on_effective_appearance_did_change as extern fn(&Object, Sel, id),
|
||||
(effectiveAppearanceDidChangedOnMainThread:) => on_effective_appearance_did_changed_on_main_thread as extern fn(&Object, Sel, id)
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
@ -4,8 +4,7 @@
|
||||
)]
|
||||
|
||||
use native_dialog::{MessageDialog, MessageType};
|
||||
use tauri::{Manager, PhysicalSize};
|
||||
use tauri_plugin_window_state::{StateFlags, WindowExt};
|
||||
use tauri::{Listener, Manager};
|
||||
use theseus::prelude::*;
|
||||
|
||||
mod api;
|
||||
@ -14,6 +13,14 @@ mod error;
|
||||
#[cfg(target_os = "macos")]
|
||||
mod macos;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
#[macro_use]
|
||||
extern crate cocoa;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
#[macro_use]
|
||||
extern crate objc;
|
||||
|
||||
// Should be called in launcher initialization
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[tauri::command]
|
||||
@ -32,7 +39,7 @@ async fn initialize_state(app: tauri::AppHandle) -> api::Result<()> {
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[tauri::command]
|
||||
fn show_window(app: tauri::AppHandle) {
|
||||
let win = app.get_window("main").unwrap();
|
||||
let win = app.get_webview_window("main").unwrap();
|
||||
if let Err(e) = win.show() {
|
||||
MessageDialog::new()
|
||||
.set_type(MessageType::Error)
|
||||
@ -45,16 +52,7 @@ fn show_window(app: tauri::AppHandle) {
|
||||
.unwrap();
|
||||
panic!("cannot display application window")
|
||||
} else {
|
||||
let _ = win.restore_state(StateFlags::all());
|
||||
let _ = win.set_focus();
|
||||
|
||||
// fix issue where window shows as extremely small
|
||||
if let Ok(size) = win.inner_size() {
|
||||
let width = if size.width < 1100 { 1280 } else { size.width };
|
||||
let height = if size.height < 700 { 800 } else { size.height };
|
||||
|
||||
let _ = win.set_size(PhysicalSize::new(width, height));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -75,17 +73,9 @@ async fn toggle_decorations(b: bool, window: tauri::Window) -> api::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Clone, serde::Serialize)]
|
||||
struct Payload {
|
||||
args: Vec<String>,
|
||||
cwd: String,
|
||||
}
|
||||
|
||||
// if Tauri app is called with arguments, then those arguments will be treated as commands
|
||||
// ie: deep links or filepaths for .mrpacks
|
||||
fn main() {
|
||||
tauri_plugin_deep_link::prepare("ModrinthApp");
|
||||
|
||||
/*
|
||||
tracing is set basd on the environment variable RUST_LOG=xxx, depending on the amount of logs to show
|
||||
ERROR > WARN > INFO > DEBUG > TRACE
|
||||
@ -105,15 +95,25 @@ fn main() {
|
||||
tracing::info!("Initialized tracing subscriber. Loading Modrinth App!");
|
||||
|
||||
let mut builder = tauri::Builder::default();
|
||||
|
||||
#[cfg(feature = "updater")]
|
||||
{
|
||||
builder = builder.plugin(tauri_plugin_updater::Builder::new().build());
|
||||
}
|
||||
|
||||
builder = builder
|
||||
.plugin(tauri_plugin_single_instance::init(|app, argv, cwd| {
|
||||
app.emit_all("single-instance", Payload { args: argv, cwd })
|
||||
.unwrap();
|
||||
}))
|
||||
.plugin(tauri_plugin_window_state::Builder::default().build())
|
||||
.plugin(tauri_plugin_os::init())
|
||||
.plugin(tauri_plugin_dialog::init())
|
||||
.plugin(tauri_plugin_deep_link::init())
|
||||
.plugin(tauri_plugin_shell::init())
|
||||
.plugin(
|
||||
tauri_plugin_window_state::Builder::default()
|
||||
.with_filename("app-window-state.json")
|
||||
.build(),
|
||||
)
|
||||
.setup(|app| {
|
||||
#[cfg(target_os = "macos")]
|
||||
let res = {
|
||||
{
|
||||
use macos::deep_link::InitialPayload;
|
||||
let mtx = std::sync::Arc::new(tokio::sync::Mutex::new(None));
|
||||
|
||||
@ -122,56 +122,32 @@ fn main() {
|
||||
});
|
||||
|
||||
let mtx_copy = mtx.clone();
|
||||
macos::delegate::register_open_file(move |filename| {
|
||||
let mtx_copy = mtx_copy.clone();
|
||||
|
||||
tauri::async_runtime::spawn(async move {
|
||||
tracing::info!("Handling file open {filename}");
|
||||
|
||||
let mut payload = mtx_copy.lock().await;
|
||||
if payload.is_none() {
|
||||
*payload = Some(filename.clone());
|
||||
}
|
||||
|
||||
let _ = api::utils::handle_command(filename).await;
|
||||
});
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let mtx_copy = mtx.clone();
|
||||
tauri_plugin_deep_link::register(
|
||||
"modrinth",
|
||||
move |request: String| {
|
||||
let mtx_copy = mtx_copy.clone();
|
||||
app.listen("deep-link://new-url", move |url| {
|
||||
let mtx_copy_copy = mtx_copy.clone();
|
||||
let request = url.payload().to_owned();
|
||||
|
||||
tauri::async_runtime::spawn(async move {
|
||||
tracing::info!("Handling deep link {request}");
|
||||
|
||||
let mut payload = mtx_copy.lock().await;
|
||||
let mut payload = mtx_copy_copy.lock().await;
|
||||
if payload.is_none() {
|
||||
*payload = Some(request.clone());
|
||||
}
|
||||
|
||||
let _ = api::utils::handle_command(request).await;
|
||||
});
|
||||
},
|
||||
)
|
||||
});
|
||||
};
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
let res = tauri_plugin_deep_link::register(
|
||||
"modrinth",
|
||||
|request: String| {
|
||||
tracing::info!("Handling deep link {request}");
|
||||
app.listen("deep-link://new-url", |url| {
|
||||
let payload = url.payload().to_owned();
|
||||
tracing::info!("Handling deep link {payload}");
|
||||
tauri::async_runtime::spawn(api::utils::handle_command(
|
||||
request,
|
||||
payload,
|
||||
));
|
||||
},
|
||||
);
|
||||
|
||||
if let Err(e) = res {
|
||||
tracing::error!("Error registering deep link handler: {}", e);
|
||||
}
|
||||
dbg!(url);
|
||||
});
|
||||
|
||||
if let Some(window) = app.get_window("main") {
|
||||
// Hide window to prevent white flash on startup
|
||||
@ -179,34 +155,14 @@ fn main() {
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
{
|
||||
use window_shadows::set_shadow;
|
||||
set_shadow(&window, true).unwrap();
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
use macos::window_ext::WindowExt;
|
||||
window.set_transparent_titlebar(true);
|
||||
window.position_traffic_lights(9.0, 16.0);
|
||||
window.set_shadow(true).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
use tauri::WindowEvent;
|
||||
builder = builder.on_window_event(|e| {
|
||||
use macos::window_ext::WindowExt;
|
||||
if let WindowEvent::Resized(..) = e.event() {
|
||||
let win = e.window();
|
||||
win.position_traffic_lights(9.0, 16.0);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
let builder = builder
|
||||
builder = builder
|
||||
.plugin(api::auth::init())
|
||||
.plugin(api::mr_auth::init())
|
||||
.plugin(api::import::init())
|
||||
@ -225,18 +181,55 @@ fn main() {
|
||||
initialize_state,
|
||||
is_dev,
|
||||
toggle_decorations,
|
||||
api::auth::auth_login,
|
||||
api::mr_auth::modrinth_auth_login,
|
||||
show_window,
|
||||
]);
|
||||
|
||||
if let Err(e) = builder.run(tauri::generate_context!()) {
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
builder = builder.plugin(macos::window_ext::init());
|
||||
}
|
||||
|
||||
let app = builder.build(tauri::generate_context!());
|
||||
|
||||
match app {
|
||||
Ok(app) => {
|
||||
#[allow(unused_variables)]
|
||||
app.run(|app, event| {
|
||||
#[cfg(target_os = "macos")]
|
||||
if let tauri::RunEvent::Opened { urls } = event {
|
||||
tracing::info!("Handling webview open {urls:?}");
|
||||
|
||||
let file = urls
|
||||
.into_iter()
|
||||
.filter_map(|url| url.to_file_path().ok())
|
||||
.next();
|
||||
|
||||
if let Some(file) = file {
|
||||
use macos::deep_link::InitialPayload;
|
||||
let initial_payload = app.state::<InitialPayload>();
|
||||
let request = file.to_string_lossy().to_string();
|
||||
|
||||
let mtx_copy = initial_payload.payload.clone();
|
||||
tauri::async_runtime::spawn(async move {
|
||||
let mut payload = mtx_copy.lock().await;
|
||||
if payload.is_none() {
|
||||
*payload = Some(request.clone());
|
||||
}
|
||||
|
||||
let _ = api::utils::handle_command(request).await;
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
Err(e) => {
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
// tauri doesn't expose runtime errors, so matching a string representation seems like the only solution
|
||||
if format!("{:?}", e)
|
||||
.contains("Runtime(CreateWebview(WebView2Error(WindowsError")
|
||||
{
|
||||
if format!("{:?}", e).contains(
|
||||
"Runtime(CreateWebview(WebView2Error(WindowsError",
|
||||
) {
|
||||
MessageDialog::new()
|
||||
.set_type(MessageType::Error)
|
||||
.set_title("Initialization error")
|
||||
@ -260,4 +253,5 @@ fn main() {
|
||||
|
||||
panic!("{1}: {:?}", e, "error while running tauri application")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,14 @@
|
||||
{
|
||||
"tauri": {
|
||||
"bundle": {
|
||||
"createUpdaterArtifacts": "v1Compatible"
|
||||
},
|
||||
"build": {
|
||||
"features": ["updater"]
|
||||
},
|
||||
"plugins": {
|
||||
"updater": {
|
||||
"active": true,
|
||||
"endpoints": ["https://launcher-files.modrinth.com/updates.json"],
|
||||
"dialog": true,
|
||||
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDIwMzM5QkE0M0FCOERBMzkKUldRNTJyZzZwSnN6SUdPRGdZREtUUGxMblZqeG9OVHYxRUlRTzJBc2U3MUNJaDMvZDQ1UytZZmYK"
|
||||
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDIwMzM5QkE0M0FCOERBMzkKUldRNTJyZzZwSnN6SUdPRGdZREtUUGxMblZqeG9OVHYxRUlRTzJBc2U3MUNJaDMvZDQ1UytZZmYK",
|
||||
"endpoints": ["https://launcher-files.modrinth.com/updates.json"]
|
||||
}
|
||||
}
|
||||
}
|
@ -2,57 +2,29 @@
|
||||
"build": {
|
||||
"beforeDevCommand": "pnpm turbo run dev --filter=@modrinth/app-frontend",
|
||||
"beforeBuildCommand": "pnpm turbo run build --filter=@modrinth/app-frontend",
|
||||
"devPath": "http://localhost:1420",
|
||||
"distDir": "../app-frontend/dist",
|
||||
"withGlobalTauri": false
|
||||
"frontendDist": "../app-frontend/dist",
|
||||
"devUrl": "http://localhost:1420"
|
||||
},
|
||||
"package": {
|
||||
"productName": "Modrinth App",
|
||||
"version": "0.8.3-1"
|
||||
},
|
||||
"tauri": {
|
||||
"allowlist": {
|
||||
"dialog": {
|
||||
"confirm": true,
|
||||
"open": true
|
||||
},
|
||||
"protocol": {
|
||||
"asset": true,
|
||||
"assetScope": []
|
||||
},
|
||||
"shell": {
|
||||
"open": true
|
||||
},
|
||||
"window": {
|
||||
"create": true,
|
||||
"close": true,
|
||||
"hide": true,
|
||||
"show": true,
|
||||
"maximize": true,
|
||||
"minimize": true,
|
||||
"unmaximize": true,
|
||||
"unminimize": true,
|
||||
"startDragging": true,
|
||||
"setDecorations": true
|
||||
},
|
||||
"os": {
|
||||
"all": true
|
||||
},
|
||||
"app": {
|
||||
"all": true
|
||||
}
|
||||
},
|
||||
"macOSPrivateApi": true,
|
||||
"bundle": {
|
||||
"active": true,
|
||||
"category": "Game",
|
||||
"copyright": "",
|
||||
"deb": {
|
||||
"depends": []
|
||||
},
|
||||
"targets": "all",
|
||||
"externalBin": [],
|
||||
"icon": ["icons/128x128.png", "icons/128x128@2x.png", "icons/icon.icns", "icons/icon.ico"],
|
||||
"identifier": "ModrinthApp",
|
||||
"icon": [
|
||||
"icons/128x128.png",
|
||||
"icons/128x128@2x.png",
|
||||
"icons/icon.icns",
|
||||
"icons/icon.ico"
|
||||
],
|
||||
"windows": {
|
||||
"certificateThumbprint": null,
|
||||
"digestAlgorithm": "sha256",
|
||||
"timestampUrl": "http://timestamp.digicert.com",
|
||||
"wix": {
|
||||
"template": "./msi/main.wxs"
|
||||
}
|
||||
},
|
||||
"longDescription": "",
|
||||
"macOS": {
|
||||
"entitlements": "App.entitlements",
|
||||
@ -63,22 +35,26 @@
|
||||
},
|
||||
"resources": [],
|
||||
"shortDescription": "",
|
||||
"targets": "all",
|
||||
"windows": {
|
||||
"certificateThumbprint": null,
|
||||
"digestAlgorithm": "sha256",
|
||||
"timestampUrl": "http://timestamp.digicert.com",
|
||||
"wix": {
|
||||
"template": "./msi/main.wxs"
|
||||
"linux": {
|
||||
"deb": {
|
||||
"depends": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": {
|
||||
"csp": "default-src 'self'; connect-src https://modrinth.com https://*.modrinth.com https://mixpanel.com https://*.mixpanel.com https://*.cloudflare.com https://api.mclo.gs; font-src https://cdn-raw.modrinth.com/fonts/inter/; img-src tauri: https: data: blob: 'unsafe-inline' asset: https://asset.localhost; script-src https://*.cloudflare.com 'self'; frame-src https://*.cloudflare.com https://www.youtube.com https://www.youtube-nocookie.com https://discord.com 'self'; style-src 'unsafe-inline' 'self'"
|
||||
"productName": "Modrinth App",
|
||||
"version": "0.8.3-1",
|
||||
"identifier": "ModrinthApp",
|
||||
"plugins": {
|
||||
"deep-link": {
|
||||
"desktop": {
|
||||
"schemes": ["modrinth"]
|
||||
},
|
||||
"updater": {
|
||||
"active": false
|
||||
"mobile": []
|
||||
}
|
||||
},
|
||||
"app": {
|
||||
"withGlobalTauri": false,
|
||||
"macOSPrivateApi": true,
|
||||
"windows": [
|
||||
{
|
||||
"titleBarStyle": "Overlay",
|
||||
@ -91,8 +67,20 @@
|
||||
"minHeight": 700,
|
||||
"minWidth": 1100,
|
||||
"visible": false,
|
||||
"zoomHotkeysEnabled": true,
|
||||
"decorations": false
|
||||
}
|
||||
]
|
||||
],
|
||||
"security": {
|
||||
"assetProtocol": {
|
||||
"scope": [
|
||||
"$APPDATA/caches/icons/*",
|
||||
"$APPCONFIG/caches/icons/*",
|
||||
"$CONFIG/caches/icons/*"
|
||||
],
|
||||
"enable": true
|
||||
},
|
||||
"csp": "default-src 'self'; connect-src ipc: http://ipc.localhost https://modrinth.com https://*.modrinth.com https://mixpanel.com https://*.mixpanel.com https://*.cloudflare.com https://api.mclo.gs; font-src https://cdn-raw.modrinth.com/fonts/inter/; img-src tauri: https: data: blob: 'unsafe-inline' asset: https://asset.localhost; script-src https://*.cloudflare.com 'self'; frame-src https://*.cloudflare.com https://www.youtube.com https://www.youtube-nocookie.com https://discord.com 'self'; style-src unsafe-inline 'self'"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"tauri": {
|
||||
"app": {
|
||||
"windows": [
|
||||
{
|
||||
"titleBarStyle": "Overlay",
|
||||
@ -12,6 +12,7 @@
|
||||
"minHeight": 700,
|
||||
"minWidth": 1100,
|
||||
"visible": false,
|
||||
"zoomHotkeysEnabled": true,
|
||||
"decorations": true
|
||||
}
|
||||
]
|
||||
|
2
apps/frontend/.env.example
Normal file
2
apps/frontend/.env.example
Normal file
@ -0,0 +1,2 @@
|
||||
BASE_URL=https://api.modrinth.com/v2/
|
||||
BROWSER_BASE_URL=https://api.modrinth.com/v2/
|
@ -6,12 +6,12 @@
|
||||
{
|
||||
"name": "max_concurrent_writes",
|
||||
"ordinal": 0,
|
||||
"type_info": "Int64"
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "max_concurrent_downloads",
|
||||
"ordinal": 1,
|
||||
"type_info": "Int64"
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "theme",
|
||||
@ -26,37 +26,37 @@
|
||||
{
|
||||
"name": "collapsed_navigation",
|
||||
"ordinal": 4,
|
||||
"type_info": "Int64"
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "advanced_rendering",
|
||||
"ordinal": 5,
|
||||
"type_info": "Int64"
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "native_decorations",
|
||||
"ordinal": 6,
|
||||
"type_info": "Int64"
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "discord_rpc",
|
||||
"ordinal": 7,
|
||||
"type_info": "Int64"
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "developer_mode",
|
||||
"ordinal": 8,
|
||||
"type_info": "Int64"
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "telemetry",
|
||||
"ordinal": 9,
|
||||
"type_info": "Int64"
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "onboarded",
|
||||
"ordinal": 10,
|
||||
"type_info": "Int64"
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "extra_launch_args",
|
||||
@ -71,27 +71,27 @@
|
||||
{
|
||||
"name": "mc_memory_max",
|
||||
"ordinal": 13,
|
||||
"type_info": "Int64"
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "mc_force_fullscreen",
|
||||
"ordinal": 14,
|
||||
"type_info": "Int64"
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "mc_game_resolution_x",
|
||||
"ordinal": 15,
|
||||
"type_info": "Int64"
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "mc_game_resolution_y",
|
||||
"ordinal": 16,
|
||||
"type_info": "Int64"
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "hide_on_process_start",
|
||||
"ordinal": 17,
|
||||
"type_info": "Int64"
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "hook_pre_launch",
|
||||
@ -121,7 +121,7 @@
|
||||
{
|
||||
"name": "migrated",
|
||||
"ordinal": 23,
|
||||
"type_info": "Int64"
|
||||
"type_info": "Integer"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
|
@ -1,12 +0,0 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n DELETE FROM processes WHERE pid = $1\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "1769b7033985bfdd04ee8912d9f28e0d15a8b893db47aca3aec054c7134f1f3f"
|
||||
}
|
@ -11,7 +11,7 @@
|
||||
{
|
||||
"name": "active",
|
||||
"ordinal": 1,
|
||||
"type_info": "Int64"
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "session_id",
|
||||
@ -21,7 +21,7 @@
|
||||
{
|
||||
"name": "expires",
|
||||
"ordinal": 3,
|
||||
"type_info": "Int64"
|
||||
"type_info": "Integer"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
|
@ -6,7 +6,7 @@
|
||||
{
|
||||
"name": "major_version",
|
||||
"ordinal": 0,
|
||||
"type_info": "Int64"
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "full_version",
|
||||
|
@ -26,7 +26,7 @@
|
||||
{
|
||||
"name": "expires",
|
||||
"ordinal": 4,
|
||||
"type_info": "Int64"
|
||||
"type_info": "Integer"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
|
@ -1,50 +0,0 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n SELECT\n pid, start_time, name, executable, profile_path, post_exit_command\n FROM processes\n WHERE 1=$1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "pid",
|
||||
"ordinal": 0,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "start_time",
|
||||
"ordinal": 1,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"ordinal": 2,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "executable",
|
||||
"ordinal": 3,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "profile_path",
|
||||
"ordinal": 4,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "post_exit_command",
|
||||
"ordinal": 5,
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "3cac786ad15ef1167bc50ca846d98facb3dee35c9e421209c1161ee7380b7a74"
|
||||
}
|
@ -56,32 +56,32 @@
|
||||
{
|
||||
"name": "locked",
|
||||
"ordinal": 10,
|
||||
"type_info": "Int64"
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "created",
|
||||
"ordinal": 11,
|
||||
"type_info": "Int64"
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "modified",
|
||||
"ordinal": 12,
|
||||
"type_info": "Int64"
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "last_played",
|
||||
"ordinal": 13,
|
||||
"type_info": "Int64"
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "submitted_time_played",
|
||||
"ordinal": 14,
|
||||
"type_info": "Int64"
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "recent_time_played",
|
||||
"ordinal": 15,
|
||||
"type_info": "Int64"
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "override_java_path",
|
||||
@ -101,22 +101,22 @@
|
||||
{
|
||||
"name": "override_mc_memory_max",
|
||||
"ordinal": 19,
|
||||
"type_info": "Int64"
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "override_mc_force_fullscreen",
|
||||
"ordinal": 20,
|
||||
"type_info": "Int64"
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "override_mc_game_resolution_x",
|
||||
"ordinal": 21,
|
||||
"type_info": "Int64"
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "override_mc_game_resolution_y",
|
||||
"ordinal": 22,
|
||||
"type_info": "Int64"
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "override_hook_pre_launch",
|
||||
|
@ -56,32 +56,32 @@
|
||||
{
|
||||
"name": "locked",
|
||||
"ordinal": 10,
|
||||
"type_info": "Int64"
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "created",
|
||||
"ordinal": 11,
|
||||
"type_info": "Int64"
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "modified",
|
||||
"ordinal": 12,
|
||||
"type_info": "Int64"
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "last_played",
|
||||
"ordinal": 13,
|
||||
"type_info": "Int64"
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "submitted_time_played",
|
||||
"ordinal": 14,
|
||||
"type_info": "Int64"
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "recent_time_played",
|
||||
"ordinal": 15,
|
||||
"type_info": "Int64"
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "override_java_path",
|
||||
@ -101,22 +101,22 @@
|
||||
{
|
||||
"name": "override_mc_memory_max",
|
||||
"ordinal": 19,
|
||||
"type_info": "Int64"
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "override_mc_force_fullscreen",
|
||||
"ordinal": 20,
|
||||
"type_info": "Int64"
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "override_mc_game_resolution_x",
|
||||
"ordinal": 21,
|
||||
"type_info": "Int64"
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "override_mc_game_resolution_y",
|
||||
"ordinal": 22,
|
||||
"type_info": "Int64"
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "override_hook_pre_launch",
|
||||
|
@ -1,50 +0,0 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n SELECT\n pid, start_time, name, executable, profile_path, post_exit_command\n FROM processes\n WHERE profile_path = $1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "pid",
|
||||
"ordinal": 0,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "start_time",
|
||||
"ordinal": 1,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"ordinal": 2,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "executable",
|
||||
"ordinal": 3,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "profile_path",
|
||||
"ordinal": 4,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "post_exit_command",
|
||||
"ordinal": 5,
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "5f07a8b45063167074db8b3da51e220a7a0f5879fb8978d4033e259102ae3790"
|
||||
}
|
@ -11,7 +11,7 @@
|
||||
{
|
||||
"name": "active",
|
||||
"ordinal": 1,
|
||||
"type_info": "Int64"
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "session_id",
|
||||
@ -21,7 +21,7 @@
|
||||
{
|
||||
"name": "expires",
|
||||
"ordinal": 3,
|
||||
"type_info": "Int64"
|
||||
"type_info": "Integer"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
|
@ -26,12 +26,12 @@
|
||||
{
|
||||
"name": "issue_instant",
|
||||
"ordinal": 4,
|
||||
"type_info": "Int64"
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "not_after",
|
||||
"ordinal": 5,
|
||||
"type_info": "Int64"
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "token",
|
||||
@ -41,7 +41,7 @@
|
||||
{
|
||||
"name": "display_claims!: serde_json::Value",
|
||||
"ordinal": 7,
|
||||
"type_info": "Null"
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
|
@ -11,7 +11,7 @@
|
||||
{
|
||||
"name": "active",
|
||||
"ordinal": 1,
|
||||
"type_info": "Int64"
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "username",
|
||||
@ -31,7 +31,7 @@
|
||||
{
|
||||
"name": "expires",
|
||||
"ordinal": 5,
|
||||
"type_info": "Int64"
|
||||
"type_info": "Integer"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
|
@ -11,7 +11,7 @@
|
||||
{
|
||||
"name": "active",
|
||||
"ordinal": 1,
|
||||
"type_info": "Int64"
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "username",
|
||||
@ -31,7 +31,7 @@
|
||||
{
|
||||
"name": "expires",
|
||||
"ordinal": 5,
|
||||
"type_info": "Int64"
|
||||
"type_info": "Integer"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
|
@ -1,12 +0,0 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n INSERT INTO processes (pid, start_time, name, executable, profile_path, post_exit_command)\n VALUES ($1, $2, $3, $4, $5, $6)\n ON CONFLICT (pid) DO UPDATE SET\n start_time = $2,\n name = $3,\n executable = $4,\n profile_path = $5,\n post_exit_command = $6\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 6
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "d1b8f27c8150f9ae514a7c9ddc68f4a59f08b7df1c65758539220d7211ade682"
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n SELECT\n pid, start_time, name, executable, profile_path, post_exit_command\n FROM processes\n WHERE pid = $1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "pid",
|
||||
"ordinal": 0,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "start_time",
|
||||
"ordinal": 1,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"ordinal": 2,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "executable",
|
||||
"ordinal": 3,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "profile_path",
|
||||
"ordinal": 4,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "post_exit_command",
|
||||
"ordinal": 5,
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "e18e960d33a140e522ca20b91d63560b921b922701b69d868dc231f6b0f4cf1c"
|
||||
}
|
@ -2,9 +2,7 @@
|
||||
name = "theseus"
|
||||
version = "0.8.3-1"
|
||||
authors = ["Jai A <jaiagr+gpg@pm.me>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
bytes = "1"
|
||||
@ -38,14 +36,13 @@ tracing-error = "0.2.0"
|
||||
|
||||
paste = { version = "1.0" }
|
||||
|
||||
tauri = { version = "1.7.1", optional = true }
|
||||
tauri = { version = "2.0.0-rc.4", optional = true }
|
||||
indicatif = { version = "0.17.3", optional = true }
|
||||
|
||||
async-tungstenite = { version = "0.27.0", features = ["tokio-runtime", "tokio-rustls-webpki-roots"] }
|
||||
futures = "0.3"
|
||||
reqwest = { version = "0.12.3", features = ["json", "stream", "deflate", "gzip", "brotli", "rustls-tls", "charset", "http2", "macos-system-configuration"], default-features = false }
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
tokio-stream = { version = "0.1", features = ["fs"] }
|
||||
async-recursion = "1.0.4"
|
||||
|
||||
notify = { version = "6.1.1", default-features = false }
|
||||
@ -63,10 +60,7 @@ rand = "0.8"
|
||||
byteorder = "1.5.0"
|
||||
base64 = "0.22.0"
|
||||
|
||||
# TODO: Remove when new SQLX version is released
|
||||
# We force-upgrade SQLite so JSONB support is added (theseus)
|
||||
# https://github.com/launchbadge/sqlx/commit/352b02de6af70f1ff1bfbd15329120589a0f7337
|
||||
sqlx = { git = "https://github.com/launchbadge/sqlx.git", rev = "352b02de6af70f1ff1bfbd15329120589a0f7337", features = [ "runtime-tokio", "sqlite", "macros"] }
|
||||
sqlx = { version = "0.8.0", features = [ "runtime-tokio", "sqlite", "macros" ] }
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winreg = "0.52.0"
|
||||
|
@ -3,14 +3,13 @@ use crate::event::{
|
||||
CommandPayload, EventError, LoadingBar, LoadingBarType, ProcessPayloadType,
|
||||
ProfilePayloadType,
|
||||
};
|
||||
use futures::prelude::*;
|
||||
|
||||
#[cfg(feature = "tauri")]
|
||||
use crate::event::{
|
||||
LoadingPayload, ProcessPayload, ProfilePayload, WarningPayload,
|
||||
};
|
||||
use futures::prelude::*;
|
||||
#[cfg(feature = "tauri")]
|
||||
use tauri::Manager;
|
||||
use tauri::Emitter;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[cfg(feature = "cli")]
|
||||
@ -187,7 +186,7 @@ pub async fn emit_loading(
|
||||
#[cfg(feature = "tauri")]
|
||||
event_state
|
||||
.app
|
||||
.emit_all(
|
||||
.emit(
|
||||
"loading",
|
||||
LoadingPayload {
|
||||
fraction: opt_display_frac,
|
||||
@ -215,7 +214,7 @@ pub async fn emit_warning(message: &str) -> crate::Result<()> {
|
||||
let event_state = crate::EventState::get().await?;
|
||||
event_state
|
||||
.app
|
||||
.emit_all(
|
||||
.emit(
|
||||
"warning",
|
||||
WarningPayload {
|
||||
message: message.to_string(),
|
||||
@ -239,7 +238,7 @@ pub async fn emit_command(command: CommandPayload) -> crate::Result<()> {
|
||||
let event_state = crate::EventState::get().await?;
|
||||
event_state
|
||||
.app
|
||||
.emit_all("command", command)
|
||||
.emit("command", command)
|
||||
.map_err(EventError::from)?;
|
||||
}
|
||||
Ok(())
|
||||
@ -258,7 +257,7 @@ pub async fn emit_process(
|
||||
let event_state = crate::EventState::get().await?;
|
||||
event_state
|
||||
.app
|
||||
.emit_all(
|
||||
.emit(
|
||||
"process",
|
||||
ProcessPayload {
|
||||
profile_path_id: profile_path.to_string(),
|
||||
@ -283,7 +282,7 @@ pub async fn emit_profile(
|
||||
let event_state = crate::EventState::get().await?;
|
||||
event_state
|
||||
.app
|
||||
.emit_all(
|
||||
.emit(
|
||||
"profile",
|
||||
ProfilePayload {
|
||||
profile_path_id: profile_path_id.to_string(),
|
||||
|
@ -2,6 +2,8 @@
|
||||
use dashmap::DashMap;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{path::PathBuf, sync::Arc};
|
||||
#[cfg(feature = "tauri")]
|
||||
use tauri::Emitter;
|
||||
use tokio::sync::OnceCell;
|
||||
use uuid::Uuid;
|
||||
|
||||
@ -62,10 +64,11 @@ impl EventState {
|
||||
}
|
||||
|
||||
#[cfg(feature = "tauri")]
|
||||
pub async fn get_main_window() -> crate::Result<Option<tauri::Window>> {
|
||||
pub async fn get_main_window() -> crate::Result<Option<tauri::WebviewWindow>>
|
||||
{
|
||||
use tauri::Manager;
|
||||
let value = Self::get().await?;
|
||||
Ok(value.app.get_window("main"))
|
||||
Ok(value.app.get_webview_window("main"))
|
||||
}
|
||||
}
|
||||
|
||||
@ -103,8 +106,7 @@ impl Drop for LoadingBarId {
|
||||
let event = bar.bar_type.clone();
|
||||
let fraction = bar.current / bar.total;
|
||||
|
||||
use tauri::Manager;
|
||||
let _ = event_state.app.emit_all(
|
||||
let _ = event_state.app.emit(
|
||||
"loading",
|
||||
LoadingPayload {
|
||||
fraction: None,
|
||||
|
@ -217,7 +217,7 @@ where
|
||||
let file_name = format!(
|
||||
"{}/{}",
|
||||
profile.path,
|
||||
path.replace("\\", "/")
|
||||
path.replace('\\', "/")
|
||||
.replace(".disabled", "")
|
||||
);
|
||||
|
||||
|
@ -68,11 +68,13 @@ impl Settings {
|
||||
onboarded: res.onboarded == 1,
|
||||
extra_launch_args: res
|
||||
.extra_launch_args
|
||||
.and_then(|x| serde_json::from_str(&x).ok())
|
||||
.as_ref()
|
||||
.and_then(|x| serde_json::from_str(x).ok())
|
||||
.unwrap_or_default(),
|
||||
custom_env_vars: res
|
||||
.custom_env_vars
|
||||
.and_then(|x| serde_json::from_str(&x).ok())
|
||||
.as_ref()
|
||||
.and_then(|x| serde_json::from_str(x).ok())
|
||||
.unwrap_or_default(),
|
||||
memory: MemorySettings {
|
||||
maximum: res.mc_memory_max as u32,
|
||||
|
@ -3,18 +3,20 @@ import { DropdownIcon } from '@modrinth/assets'
|
||||
import { reactive } from 'vue'
|
||||
import Button from './Button.vue'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
collapsible: boolean
|
||||
defaultCollapsed: boolean
|
||||
noAutoBody: boolean
|
||||
}>(),
|
||||
{
|
||||
collapsible: false,
|
||||
defaultCollapsed: false,
|
||||
noAutoBody: false,
|
||||
const props = defineProps({
|
||||
collapsible: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
)
|
||||
defaultCollapsed: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
noAutoBody: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
})
|
||||
|
||||
const state = reactive({
|
||||
collapsed: props.defaultCollapsed,
|
||||
|
928
pnpm-lock.yaml
generated
928
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -22,7 +22,8 @@
|
||||
"dev": {
|
||||
"cache": false,
|
||||
"persistent": true,
|
||||
"inputs": ["$TURBO_DEFAULT$", ".env*"]
|
||||
"inputs": ["$TURBO_DEFAULT$", ".env*"],
|
||||
"env": ["DISPLAY", "WEBKIT_DISABLE_DMABUF_RENDERER"]
|
||||
},
|
||||
"test": {},
|
||||
"fix": {
|
||||
|
Loading…
x
Reference in New Issue
Block a user