App fixes 0.9.0 (#3034)

* push fixes to test on windows

* Fix searching mods

* Fix search not saving, fix scrolling issues, etc
This commit is contained in:
Geometrically 2024-12-17 23:23:30 -07:00 committed by GitHub
parent 7e8ceadfd4
commit 6ceed4b226
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 280 additions and 178 deletions

124
Cargo.lock generated
View File

@ -8453,9 +8453,9 @@ dependencies = [
[[package]]
name = "tao"
version = "0.30.8"
version = "0.31.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6682a07cf5bab0b8a2bd20d0a542917ab928b5edb75ebd4eda6b05cbaab872da"
checksum = "cc6b53216f32e60efc27dfa111268481e4dfba53e553e4cdebcaed9db36c11bb"
dependencies = [
"bitflags 2.6.0",
"cocoa 0.26.0",
@ -8468,7 +8468,6 @@ dependencies = [
"gdkwayland-sys",
"gdkx11-sys",
"gtk",
"instant",
"jni",
"lazy_static",
"libc",
@ -8527,8 +8526,7 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
[[package]]
name = "tauri"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e545de0a2dfe296fa67db208266cd397c5a55ae782da77973ef4c4fac90e9f2c"
source = "git+https://github.com/modrinth/tauri?rev=9c36dd3#9c36dd30ad6d19832a5dbd7ac9af0a152bb9323a"
dependencies = [
"anyhow",
"bytes 1.7.2",
@ -8559,11 +8557,11 @@ dependencies = [
"serde_repr",
"serialize-to-javascript",
"swift-rs",
"tauri-build",
"tauri-build 2.0.3 (git+https://github.com/modrinth/tauri?rev=9c36dd3)",
"tauri-macros",
"tauri-runtime",
"tauri-runtime-wry",
"tauri-utils",
"tauri-utils 2.1.0 (git+https://github.com/modrinth/tauri?rev=9c36dd3)",
"thiserror 2.0.7",
"tokio 1.42.0",
"tray-icon",
@ -8592,8 +8590,29 @@ dependencies = [
"semver 1.0.23",
"serde",
"serde_json",
"tauri-codegen",
"tauri-utils",
"tauri-codegen 2.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"tauri-utils 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"tauri-winres",
"toml 0.8.19",
"walkdir",
]
[[package]]
name = "tauri-build"
version = "2.0.3"
source = "git+https://github.com/modrinth/tauri?rev=9c36dd3#9c36dd30ad6d19832a5dbd7ac9af0a152bb9323a"
dependencies = [
"anyhow",
"cargo_toml",
"dirs 5.0.1",
"glob",
"heck 0.5.0",
"json-patch",
"schemars",
"semver 1.0.23",
"serde",
"serde_json",
"tauri-utils 2.1.0 (git+https://github.com/modrinth/tauri?rev=9c36dd3)",
"tauri-winres",
"toml 0.8.19",
"walkdir",
@ -8604,6 +8623,31 @@ name = "tauri-codegen"
version = "2.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf79faeecf301d3e969b1fae977039edb77a4c1f25cc0a961be298b54bff97cf"
dependencies = [
"base64 0.22.1",
"ico",
"json-patch",
"plist",
"png",
"proc-macro2",
"quote",
"semver 1.0.23",
"serde",
"serde_json",
"sha2 0.10.8",
"syn 2.0.90",
"tauri-utils 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"thiserror 2.0.7",
"time",
"url",
"uuid 1.10.0",
"walkdir",
]
[[package]]
name = "tauri-codegen"
version = "2.0.3"
source = "git+https://github.com/modrinth/tauri?rev=9c36dd3#9c36dd30ad6d19832a5dbd7ac9af0a152bb9323a"
dependencies = [
"base64 0.22.1",
"brotli 7.0.0",
@ -8618,7 +8662,7 @@ dependencies = [
"serde_json",
"sha2 0.10.8",
"syn 2.0.90",
"tauri-utils",
"tauri-utils 2.1.0 (git+https://github.com/modrinth/tauri?rev=9c36dd3)",
"thiserror 2.0.7",
"time",
"url",
@ -8629,15 +8673,14 @@ dependencies = [
[[package]]
name = "tauri-macros"
version = "2.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c52027c8c5afb83166dacddc092ee8fff50772f9646d461d8c33ee887e447a03"
source = "git+https://github.com/modrinth/tauri?rev=9c36dd3#9c36dd30ad6d19832a5dbd7ac9af0a152bb9323a"
dependencies = [
"heck 0.5.0",
"proc-macro2",
"quote",
"syn 2.0.90",
"tauri-codegen",
"tauri-utils",
"tauri-codegen 2.0.3 (git+https://github.com/modrinth/tauri?rev=9c36dd3)",
"tauri-utils 2.1.0 (git+https://github.com/modrinth/tauri?rev=9c36dd3)",
]
[[package]]
@ -8652,7 +8695,7 @@ dependencies = [
"schemars",
"serde",
"serde_json",
"tauri-utils",
"tauri-utils 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"toml 0.8.19",
"walkdir",
]
@ -8669,7 +8712,7 @@ dependencies = [
"serde_json",
"tauri",
"tauri-plugin",
"tauri-utils",
"tauri-utils 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"thiserror 2.0.7",
"tracing",
"url",
@ -8711,7 +8754,7 @@ dependencies = [
"serde_repr",
"tauri",
"tauri-plugin",
"tauri-utils",
"tauri-utils 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"thiserror 2.0.7",
"toml 0.8.19",
"url",
@ -8821,8 +8864,7 @@ dependencies = [
[[package]]
name = "tauri-runtime"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cce18d43f80d4aba3aa8a0c953bbe835f3d0f2370aca75e8dbb14bd4bab27958"
source = "git+https://github.com/modrinth/tauri?rev=9c36dd3#9c36dd30ad6d19832a5dbd7ac9af0a152bb9323a"
dependencies = [
"dpi",
"gtk",
@ -8831,7 +8873,7 @@ dependencies = [
"raw-window-handle 0.6.2",
"serde",
"serde_json",
"tauri-utils",
"tauri-utils 2.1.0 (git+https://github.com/modrinth/tauri?rev=9c36dd3)",
"thiserror 2.0.7",
"url",
"windows 0.58.0",
@ -8840,8 +8882,7 @@ dependencies = [
[[package]]
name = "tauri-runtime-wry"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f442a38863e10129ffe2cec7bd09c2dcf8a098a3a27801a476a304d5bb991d2"
source = "git+https://github.com/modrinth/tauri?rev=9c36dd3#9c36dd30ad6d19832a5dbd7ac9af0a152bb9323a"
dependencies = [
"gtk",
"http 1.1.0",
@ -8855,7 +8896,7 @@ dependencies = [
"softbuffer",
"tao",
"tauri-runtime",
"tauri-utils",
"tauri-utils 2.1.0 (git+https://github.com/modrinth/tauri?rev=9c36dd3)",
"url",
"webkit2gtk",
"webview2-com",
@ -8868,6 +8909,41 @@ name = "tauri-utils"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9271a88f99b4adea0dc71d0baca4505475a0bbd139fb135f62958721aaa8fe54"
dependencies = [
"cargo_metadata",
"ctor",
"dunce",
"glob",
"html5ever",
"http 1.1.0",
"infer 0.16.0",
"json-patch",
"kuchikiki",
"log",
"memchr",
"phf 0.11.2",
"proc-macro2",
"quote",
"regex",
"schemars",
"semver 1.0.23",
"serde",
"serde-untagged",
"serde_json",
"serde_with",
"swift-rs",
"thiserror 2.0.7",
"toml 0.8.19",
"url",
"urlpattern",
"uuid 1.10.0",
"walkdir",
]
[[package]]
name = "tauri-utils"
version = "2.1.0"
source = "git+https://github.com/modrinth/tauri?rev=9c36dd3#9c36dd30ad6d19832a5dbd7ac9af0a152bb9323a"
dependencies = [
"brotli 7.0.0",
"cargo_metadata",
@ -9026,7 +9102,7 @@ dependencies = [
"serde",
"serde_json",
"tauri",
"tauri-build",
"tauri-build 2.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"tauri-plugin-deep-link",
"tauri-plugin-dialog",
"tauri-plugin-opener",

View File

@ -19,3 +19,6 @@ strip = true # Remove debug symbols
[profile.dev.package.sqlx-macros]
opt-level = 3
[patch.crates-io]
tauri = { git = "https://github.com/modrinth/tauri", rev = "9c36dd3" }

View File

@ -127,7 +127,6 @@ const os = ref('')
getOS().then((x) => (os.value = x))
loading_listener(async (e) => {
console.log(e)
if (e.event.type === 'directory_move') {
loadingProgress.value = 100 * (e.fraction ?? 1)
message.value = 'Updating app directory...'

View File

@ -49,17 +49,3 @@ export const releaseColor = (releaseType) => {
return ''
}
}
export function debounce(fn, wait) {
let timer
return function (...args) {
if (timer) {
clearTimeout(timer) // clear any pre-existing timer
}
const context = this // get the current context
timer = setTimeout(() => {
fn.apply(context, args) // call the function if time expires
}, wait)
}
}

View File

@ -21,7 +21,6 @@ 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 { get_search_results } from '@/helpers/cache.js'
import { debounce } from '@/helpers/utils.js'
import NavTabs from '@/components/ui/NavTabs.vue'
import type Instance from '@/components/ui/Instance.vue'
import InstanceIndicator from '@/components/ui/InstanceIndicator.vue'
@ -190,6 +189,7 @@ const pageCount = computed(() =>
)
watch(requestParams, () => {
if (!route.params.projectType) return
refreshSearch()
})
@ -214,19 +214,7 @@ async function refreshSearch() {
}
}
results.value = rawResults.result
}
function setPage(newPageNumber: number) {
currentPage.value = newPageNumber
updateSearchResults()
onSearchChangeToTop()
}
async function updateSearchResults() {
await refreshSearch()
if (import.meta.client) {
const persistentParams: LocationQuery = {}
for (const [key, value] of Object.entries(route.query)) {
@ -246,12 +234,19 @@ async function updateSearchResults() {
...createPageParams(),
}
breadcrumbs.setContext({
name: 'Discover content',
link: `/browse/${projectType.value}`,
query: params,
})
await router.replace({ path: route.path, query: params })
breadcrumbs.setContext({ name: 'Discover content', link: route.path, query: params })
}
}
const debouncedSearchChange = debounce(() => updateSearchResults(1), 200)
async function setPage(newPageNumber: number) {
currentPage.value = newPageNumber
await onSearchChangeToTop()
}
const searchWrapper: Ref<HTMLElement | null> = ref(null)
@ -261,13 +256,10 @@ async function onSearchChangeToTop() {
window.scrollTo({ top: 0, behavior: 'smooth' })
}
async function clearSearch() {
function clearSearch() {
query.value = ''
await updateSearchResults()
}
async function clearFilters() {}
watch(
() => route.params.projectType,
async (newType) => {
@ -275,14 +267,9 @@ watch(
if (!newType || newType === projectType.value) return
projectType.value = newType
breadcrumbs.setContext({ name: 'Discover content', link: `/browse/${projectType.value}` })
currentSortType.value = { display: 'Relevance', name: 'relevance' }
query.value = ''
loading.value = true
await clearFilters()
loading.value = false
},
)
@ -418,7 +405,6 @@ await refreshSearch()
spellcheck="false"
type="text"
:placeholder="`Search ${projectType}s...`"
@input="debouncedSearchChange()"
/>
<Button v-if="query" class="r-btn" @click="() => clearSearch()">
<XIcon />
@ -432,7 +418,6 @@ await refreshSearch()
name="Sort by"
:options="sortTypes as any"
:display-name="(option: SortType | undefined) => option?.display"
@change="updateSearchResults()"
>
<span class="font-semibold text-primary">Sort by: </span>
<span class="font-semibold text-secondary">{{ selected }}</span>
@ -443,7 +428,6 @@ await refreshSearch()
name="Max results"
:options="[5, 10, 15, 20, 50, 100]"
class="max-w-[9rem]"
@change="updateSearchResults()"
>
<span class="font-semibold text-primary">View: </span>
<span class="font-semibold text-secondary">{{ selected }}</span>

View File

@ -13,18 +13,26 @@
</template>
<template #summary> </template>
<template #stats>
<div class="flex items-center gap-2 font-semibold transform capitalize">
<div
class="flex items-center gap-2 font-semibold transform capitalize border-0 border-solid border-divider pr-4 md:border-r"
>
<GameIcon class="h-6 w-6 text-secondary" />
{{ instance.loader }} {{ instance.game_version }}
</div>
<div class="flex items-center gap-2 font-semibold">
<TimerIcon class="h-6 w-6 text-secondary" />
<template v-if="timePlayed > 0">
{{ timePlayedHumanized }}
</template>
<template v-else> Never played </template>
</div>
</template>
<template #actions>
<div class="flex gap-2">
<ButtonStyled v-if="instance.install_stage !== 'installed'" color="brand" size="large">
<button disabled>Installing...</button>
</ButtonStyled>
<template v-else>
<div class="flex gap-2">
<ButtonStyled v-if="playing === true" color="red" size="large">
<ButtonStyled v-else-if="playing === true" color="red" size="large">
<button @click="stopInstance('InstancePage')">
<StopCircleIcon />
Stop
@ -77,7 +85,6 @@
</ButtonStyled>
</div>
</template>
</template>
</ContentPageHeader>
</div>
<div class="px-6">
@ -106,15 +113,15 @@
<ContextMenu ref="options" @option-clicked="handleOptionsClick">
<template #play> <PlayIcon /> Play </template>
<template #stop> <StopCircleIcon /> Stop </template>
<template #add_content> <PlusIcon /> Add Content </template>
<template #add_content> <PlusIcon /> Add content </template>
<template #edit> <EditIcon /> Edit </template>
<template #copy_path> <ClipboardCopyIcon /> Copy Path </template>
<template #open_folder> <ClipboardCopyIcon /> Open Folder </template>
<template #copy_link> <ClipboardCopyIcon /> Copy Link </template>
<template #open_link> <ClipboardCopyIcon /> Open In Modrinth <ExternalIcon /> </template>
<template #copy_path> <ClipboardCopyIcon /> Copy path </template>
<template #open_folder> <ClipboardCopyIcon /> Open folder </template>
<template #copy_link> <ClipboardCopyIcon /> Copy link </template>
<template #open_link> <ClipboardCopyIcon /> Open in Modrinth <ExternalIcon /> </template>
<template #copy_names><EditIcon />Copy names</template>
<template #copy_slugs><HashIcon />Copy slugs</template>
<template #copy_links><GlobeIcon />Copy Links</template>
<template #copy_links><GlobeIcon />Copy links</template>
<template #toggle><EditIcon />Toggle selected</template>
<template #disable><XIcon />Disable selected</template>
<template #enable><CheckCircleIcon />Enable selected</template>
@ -153,8 +160,9 @@ import {
UpdatedIcon,
MoreVerticalIcon,
GameIcon,
TimerIcon,
} from '@modrinth/assets'
import { get, kill, run } from '@/helpers/profile'
import { get, get_full_path, kill, run } from '@/helpers/profile'
import { get_by_profile_path } from '@/helpers/process'
import { process_listener, profile_listener } from '@/helpers/events'
import { useRoute, useRouter } from 'vue-router'
@ -168,8 +176,13 @@ import { convertFileSrc } from '@tauri-apps/api/core'
import { handleSevereError } from '@/store/error.js'
import { get_project, get_version_many } from '@/helpers/cache.js'
import dayjs from 'dayjs'
import duration from 'dayjs/plugin/duration'
import relativeTime from 'dayjs/plugin/relativeTime'
import ExportModal from '@/components/ui/ExportModal.vue'
dayjs.extend(duration)
dayjs.extend(relativeTime)
const route = useRoute()
const router = useRouter()
@ -321,11 +334,13 @@ const handleOptionsClick = async (args) => {
case 'open_folder':
await showProfileInFolder(instance.value.path)
break
case 'copy_path':
await navigator.clipboard.writeText(instance.value.path)
case 'copy_path': {
const fullPath = await get_full_path(instance.value.path)
await navigator.clipboard.writeText(fullPath)
break
}
}
}
const unlistenProfiles = await profile_listener(async (event) => {
if (event.profile_path_id === route.params.id) {
@ -347,6 +362,26 @@ const icon = computed(() =>
instance.value.icon_path ? convertFileSrc(instance.value.icon_path) : null,
)
const timePlayed = computed(() => {
return instance.value.recent_time_played + instance.value.submitted_time_played
})
const timePlayedHumanized = computed(() => {
const duration = dayjs.duration(timePlayed.value, 'seconds')
const hours = Math.floor(duration.asHours())
if (hours >= 1) {
return hours + ' hour' + (hours > 1 ? 's' : '')
}
const minutes = Math.floor(duration.asMinutes())
if (minutes >= 1) {
return minutes + ' minute' + (minutes > 1 ? 's' : '')
}
const seconds = Math.floor(duration.asSeconds())
return seconds + ' second' + (seconds > 1 ? 's' : '')
})
onUnmounted(() => {
unlistenProcesses()
unlistenProfiles()

View File

@ -183,8 +183,8 @@
},
{
id: 'copy-link',
shown: item.project !== undefined,
action: () => toggleDisableMod(item.data),
shown: item.data !== undefined && item.data.slug !== undefined,
action: () => copyModLink(item),
},
{
divider: true,
@ -674,6 +674,12 @@ const removeMod = async (mod) => {
})
}
const copyModLink = async (mod) => {
await navigator.clipboard.writeText(
`https://modrinth.com/${mod.data.project_type}/${mod.data.slug}`,
)
}
const deleteSelected = async () => {
for (const project of functionValues.value) {
await remove_project(props.instance.path, project.path).catch(handleError)

View File

@ -149,9 +149,9 @@ 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')?.scrollTo(0, 0)
document.querySelector('.app-viewport')?.scrollTo(0, 0)
return {
el: '.router-view',
el: '.app-viewport',
top: 0,
}
},

View File

@ -16,7 +16,7 @@ theseus = { path = "../../packages/app-lib", features = ["tauri"] }
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
tauri = { version = "2.1.1", features = ["devtools", "macos-private-api", "protocol-asset", "unstable"] }
tauri = { git = "https://github.com/modrinth/tauri", rev = "9c36dd3", features = ["devtools", "macos-private-api", "protocol-asset", "unstable"] }
tauri-plugin-window-state = "2.2.0"
tauri-plugin-deep-link = "2.2.0"
tauri-plugin-os = "2.2.0"

View File

@ -103,7 +103,7 @@ pub async fn init_ads_window<R: Runtime>(
AD_LINK.parse().unwrap(),
),
)
.initialization_script(LINK_SCRIPT)
.initialization_script_for_main_only(LINK_SCRIPT, false)
.user_agent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36")
.zoom_hotkeys_enabled(false)
.transparent(true),

View File

@ -42,9 +42,7 @@
Install content to server
</h1>
</template>
<ContentPageHeader v-else>
<template #title> Discover content </template>
</ContentPageHeader>
<ContentPageHeader v-else></ContentPageHeader>
<NavTabs v-if="!server" :links="selectableProjectTypes" class="hidden md:flex" />
</section>
<aside
@ -342,7 +340,11 @@ const tags = useTags();
const flags = useFeatureFlags();
const auth = await useAuth();
const projectType = ref({ id: "mod", display: "mod", actual: "mod" });
const projectType = computed(() =>
tags.value.projectTypes.find(
(x) => x.id === route.path.replaceAll(/^\/|s\/?$/g, ""), // Removes prefix `/` and suffixes `s` and `s/`
),
);
const projectTypes = computed(() => [projectType.value.id]);
const server = ref();
@ -506,10 +508,6 @@ async function serverInstall(project) {
project.installing = false;
}
projectType.value = tags.value.projectTypes.find(
(x) => x.id === route.path.replaceAll(/^\/|s\/?$/g, ""), // Removes prefix `/` and suffixes `s` and `s/`
);
const noLoad = ref(false);
const {
data: rawResults,

View File

@ -118,7 +118,7 @@ pub async fn profile_create(
&state.file_watcher,
&state.directories,
)
.await?;
.await;
profile.upsert(&state.pool).await?;

View File

@ -87,7 +87,7 @@ pub async fn init_watcher() -> crate::Result<FileWatcher> {
pub(crate) async fn watch_profiles_init(
watcher: &FileWatcher,
dirs: &DirectoryInfo,
) -> crate::Result<()> {
) {
if let Ok(profiles_dir) = std::fs::read_dir(dirs.profiles_dir()) {
for profile_dir in profiles_dir {
if let Ok(file_name) = profile_dir.map(|x| x.file_name()) {
@ -96,20 +96,18 @@ pub(crate) async fn watch_profiles_init(
continue;
};
watch_profile(file_name, watcher, dirs).await?;
watch_profile(file_name, watcher, dirs).await;
}
}
}
}
Ok(())
}
pub(crate) async fn watch_profile(
profile_path: &str,
watcher: &FileWatcher,
dirs: &DirectoryInfo,
) -> crate::Result<()> {
) {
let profile_path = dirs.profiles_dir().join(profile_path);
if profile_path.exists() && profile_path.is_dir() {
@ -120,15 +118,25 @@ pub(crate) async fn watch_profile(
let path = profile_path.join(folder);
if !path.exists() && !path.is_symlink() {
crate::util::io::create_dir_all(&path).await?;
if let Err(e) = crate::util::io::create_dir_all(&path).await {
tracing::error!(
"Failed to create directory for watcher {path:?}: {e}"
);
return;
}
}
let mut watcher = watcher.write().await;
watcher.watcher().watch(&path, RecursiveMode::Recursive)?;
if let Err(e) =
watcher.watcher().watch(&path, RecursiveMode::Recursive)
{
tracing::error!(
"Failed to watch directory for watcher {path:?}: {e}"
);
return;
}
}
}
Ok(())
}
fn crash_task(path: String) {

View File

@ -146,7 +146,7 @@ impl State {
let discord_rpc = DiscordGuard::init()?;
let file_watcher = fs_watcher::init_watcher().await?;
fs_watcher::watch_profiles_init(&file_watcher, &directories).await?;
fs_watcher::watch_profiles_init(&file_watcher, &directories).await;
let process_manager = ProcessManager::new();

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="10" x2="14" y1="2" y2="2"/><line x1="12" x2="15" y1="14" y2="11"/><circle cx="12" cy="14" r="8"/></svg>

After

Width:  |  Height:  |  Size: 295 B

View File

@ -180,6 +180,7 @@ import _CPUIcon from './icons/cpu.svg?component'
import _DBIcon from './icons/db.svg?component'
import _LoaderIcon from './icons/loader.svg?component'
import _ImportIcon from './icons/import.svg?component'
import _TimerIcon from './icons/timer.svg?component'
// Editor Icons
import _BoldIcon from './icons/bold.svg?component'
@ -381,3 +382,4 @@ export const DBIcon = _DBIcon
export const LoaderIcon = _LoaderIcon
export const ImportIcon = _ImportIcon
export const CardIcon = _CardIcon
export const TimerIcon = _TimerIcon

View File

@ -3,7 +3,7 @@
ref="dropdown"
no-auto-focus
:aria-id="dropdownId || null"
@hide="focusTrigger"
@apply-hide="focusTrigger"
@apply-show="focusMenuChild"
>
<button ref="trigger" v-bind="$attrs" v-tooltip="tooltip">

View File

@ -83,7 +83,7 @@ function setSelected(value: boolean) {
<ContentListItem
v-model="selectionStates[ref.filename]"
:item="ref"
:last="false"
:last="ref === items.length - 1"
class="mb-2"
@update:model-value="updateSelection"
>

View File

@ -534,6 +534,10 @@ export function useSearch(
currentPage.value = Number(page)
readParams.add('page')
})
loadQueryParam(['q'], (queryVal) => {
query.value = String(queryVal)
readParams.add('q')
})
for (const key of Object.keys(route.query).filter((key) => !readParams.has(key))) {
const type = filters.value.find((type) => type.query_param === key)