fix(theseus): Fixed multiple bugs (#1304)

* fix(theseus): Resolve log tab freezing entire app (#1127, #1237)
Switched to `vue-typed-virtual-list` due to freezing issues in WebKit caused by `vue-virtual-scroller`, which were difficult to resolve with the previous library.

* fix(theseus): Double opening of Socials Share link (#1136, #1074)

* fix(theseus): Proper symlinks resolution (#1236)
Ensures correct symlink resolution for specific mods, the mods directory, and the entire profile directory.

* fix(theseus): Correctly recognize NeoForge mods (#1215)

* fix(theseus): Corrected `Environments` and `Loaders` filters (#899)

* fix(theseus): Remove `_JAVA_OPTIONS` when testing JRE (#1171)

* fix(theseus): Fixed opening files using `show_in_folder`
Previously, opening a mod would display the contents of the JAR file instead of its location.

* fix(theseus): Hide `.DS_Store` files (#1274, #1002, #1174)

* fix(theseus): Corrected tooltip color

* fix(theseus): Fixed white mode issues (#1144, #1010)

* fix: Corrected `Slider` min and max value handling (#1008)

* fix: Fixed rebase problems

* fix: Fixed `:deep` usage warning

* chore: Updated eslint plugins to fix conflicts with Prettier
This commit is contained in:
Norbiros
2024-07-20 02:20:12 +02:00
committed by GitHub
parent f1713647cf
commit 797e1f1f21
18 changed files with 441 additions and 426 deletions

View File

@@ -25,7 +25,7 @@
"vue": "^3.4.21", "vue": "^3.4.21",
"vue-multiselect": "3.0.0-beta.3", "vue-multiselect": "3.0.0-beta.3",
"vue-router": "4.3.0", "vue-router": "4.3.0",
"vue-virtual-scroller": "2.0.0-beta.8" "vue-typed-virtual-list": "^1.0.10"
}, },
"devDependencies": { "devDependencies": {
"@tauri-apps/cli": "^1.5.11", "@tauri-apps/cli": "^1.5.11",

View File

@@ -100,13 +100,13 @@ input {
* { * {
scrollbar-width: auto; scrollbar-width: auto;
scrollbar-color: var(--color-button-bg) var(--color-bg); scrollbar-color: var(--color-scrollbar) var(--color-bg);
} }
/* Chrome, Edge, and Safari */ /* Chrome, Edge, and Safari */
*::-webkit-scrollbar { *::-webkit-scrollbar {
width: var(--gap-md); width: var(--gap-md);
border: 3px solid var(--color-bg); border: 3px solid var(--color-scrollbar);
} }
*::-webkit-scrollbar-track { *::-webkit-scrollbar-track {
@@ -115,7 +115,7 @@ input {
} }
*::-webkit-scrollbar-thumb { *::-webkit-scrollbar-thumb {
background-color: var(--color-button-bg); background-color: var(--color-scrollbar);
border-radius: var(--radius-lg); border-radius: var(--radius-lg);
border: 3px solid var(--color-bg); border: 3px solid var(--color-bg);
} }

View File

@@ -50,9 +50,9 @@ const initFiles = async () => {
disabled: disabled:
folder === 'profile.json' || folder === 'profile.json' ||
folder.startsWith('modrinth_logs') || folder.startsWith('modrinth_logs') ||
folder.startsWith('.fabric') || folder.startsWith('.fabric'),
folder.includes('.DS_Store'),
})) }))
.filter((pathData) => !pathData.path.includes('.DS_Store'))
.forEach((pathData) => { .forEach((pathData) => {
const parent = pathData.path.split(sep).slice(0, -1).join(sep) const parent = pathData.path.split(sep).slice(0, -1).join(sep)
if (parent !== '') { if (parent !== '') {

View File

@@ -318,9 +318,7 @@ onMounted(() => {
} }
} }
:deep { :deep(.checkbox) {
.checkbox { border: none;
border: none;
}
} }
</style> </style>

View File

@@ -4,8 +4,8 @@ import App from '@/App.vue'
import { createPinia } from 'pinia' import { createPinia } from 'pinia'
import '@modrinth/assets/omorphia.scss' import '@modrinth/assets/omorphia.scss'
import '@/assets/stylesheets/global.scss' import '@/assets/stylesheets/global.scss'
import 'floating-vue/dist/style.css'
import FloatingVue from 'floating-vue' import FloatingVue from 'floating-vue'
import 'floating-vue/dist/style.css'
import { get_opening_command, initialize_state } from '@/helpers/state' import { get_opening_command, initialize_state } from '@/helpers/state'
import loadCssMixin from './mixins/macCssFix.js' import loadCssMixin from './mixins/macCssFix.js'
import { get } from '@/helpers/settings' import { get } from '@/helpers/settings'

View File

@@ -488,6 +488,14 @@ const [categories, loaders, availableGameVersions] = await Promise.all([
refreshSearch(), refreshSearch(),
]) ])
const filteredLoaders = computed(() => {
return loaders.value.filter((loader) => {
return projectType.value === 'mod' || projectType.value === 'modpack'
? loader.supported_project_types[0] === 'mod'
: loader.supported_project_types[0] === projectType.value
})
})
const selectableProjectTypes = computed(() => { const selectableProjectTypes = computed(() => {
const values = [ const values = [
{ label: 'Shaders', href: `/browse/shader` }, { label: 'Shaders', href: `/browse/shader` },
@@ -518,14 +526,8 @@ const selectableProjectTypes = computed(() => {
const showVersions = computed( const showVersions = computed(
() => instanceContext.value === null || ignoreInstanceGameVersions.value, () => instanceContext.value === null || ignoreInstanceGameVersions.value,
) )
const showLoaders = computed(
() => const isModProject = computed(() => ['modpack', 'mod'].includes(projectType.value))
(projectType.value !== 'datapack' &&
projectType.value !== 'resourcepack' &&
projectType.value !== 'shader' &&
instanceContext.value === null) ||
ignoreInstanceLoaders.value,
)
onUnmounted(() => unlistenOffline()) onUnmounted(() => unlistenOffline())
</script> </script>
@@ -596,17 +598,9 @@ onUnmounted(() => unlistenOffline())
> >
<ClearIcon /> Clear filters <ClearIcon /> Clear filters
</Button> </Button>
<div v-if="showLoaders" class="loaders"> <div v-if="isModProject || projectType === 'shader'" class="loaders">
<h2>Loaders</h2> <h2>Loaders</h2>
<div <div v-for="loader in filteredLoaders" :key="loader">
v-for="loader in loaders.filter(
(l) =>
(projectType !== 'mod' && l.supported_project_types?.includes(projectType)) ||
(projectType === 'mod' &&
['fabric', 'forge', 'quilt', 'neoforge'].includes(l.name)),
)"
:key="loader"
>
<SearchFilter <SearchFilter
:active-filters="orFacets" :active-filters="orFacets"
:icon="loader.icon" :icon="loader.icon"
@@ -656,7 +650,7 @@ onUnmounted(() => unlistenOffline())
/> />
</div> </div>
</div> </div>
<div v-if="projectType !== 'datapack'" class="environment"> <div v-if="isModProject" class="environment">
<h2>Environments</h2> <h2>Environments</h2>
<SearchFilter <SearchFilter
:active-filters="selectedEnvironments" :active-filters="selectedEnvironments"

View File

@@ -551,14 +551,12 @@ async function refreshDir() {
margin: 1rem 0; margin: 1rem 0;
} }
:deep { :deep(.login-screen-modal) {
.login-screen-modal { .modal-container .modal-body {
.modal-container .modal-body { width: auto;
width: auto;
.content { .content {
background: none; background: none;
}
} }
} }
} }

View File

@@ -59,28 +59,28 @@
</div> </div>
</div> </div>
<div class="log-text"> <div class="log-text">
<RecycleScroller <VirtualScroller
v-slot="{ item }"
ref="logContainer" ref="logContainer"
class="scroller" class="scroller"
:default-size="20"
:items="displayProcessedLogs" :items="displayProcessedLogs"
direction="vertical"
:item-size="20"
key-field="id"
> >
<div class="user no-wrap"> <template #item="{ ref }">
<span :style="{ color: item.prefixColor, 'font-weight': item.weight }">{{ <div class="user no-wrap">
item.prefix <span :style="{ color: ref.prefixColor, 'font-weight': ref.weight }">{{
}}</span> ref.prefix
<span :style="{ color: item.textColor }">{{ item.text }}</span> }}</span>
</div> <span :style="{ color: ref.textColor }">{{ ref.text }}</span>
</RecycleScroller> </div>
</template>
</VirtualScroller>
</div> </div>
<ShareModal <ShareModal
ref="shareModal" ref="shareModal"
header="Share Log" header="Share Log"
share-title="Instance Log" share-title="Instance Log"
share-text="Check out this log from an instance on the Modrinth App" share-text="Check out this log from an instance on the Modrinth App"
:open-in-new-tab="false"
link link
/> />
</Card> </Card>
@@ -104,13 +104,13 @@ import { useRoute } from 'vue-router'
import { process_listener } from '@/helpers/events.js' import { process_listener } from '@/helpers/events.js'
import { handleError } from '@/store/notifications.js' import { handleError } from '@/store/notifications.js'
import { ofetch } from 'ofetch' import { ofetch } from 'ofetch'
import { createVirtualScroller } from 'vue-typed-virtual-list'
import { RecycleScroller } from 'vue-virtual-scroller'
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'
dayjs.extend(isToday) dayjs.extend(isToday)
dayjs.extend(isYesterday) dayjs.extend(isYesterday)
const VirtualScroller = createVirtualScroller()
const route = useRoute() const route = useRoute()
const props = defineProps({ const props = defineProps({
@@ -412,15 +412,20 @@ function handleUserScroll() {
interval.value = setInterval(async () => { interval.value = setInterval(async () => {
if (logs.value.length > 0) { if (logs.value.length > 0) {
logs.value[0] = await getLiveStdLog() logs.value[0] = await getLiveStdLog()
const logContainerElement = logContainer.value.$el
const scroll =
logContainerElement.scrollHeight -
logContainerElement.scrollTop -
logContainerElement.clientHeight
// const scroll = logContainer.value.$el.scrollHeight - logContainer.value.$el.scrollTop - logContainer.value.$el.clientHeight
const scroll = logContainer.value.getScroll()
// Allow resetting of userScrolled if the user scrolls to the bottom // Allow resetting of userScrolled if the user scrolls to the bottom
if (selectedLogIndex.value === 0) { if (selectedLogIndex.value === 0) {
if (scroll.end >= logContainer.value.$el.scrollHeight - 10) userScrolled.value = false if (scroll <= 10) userScrolled.value = false
if (!userScrolled.value) { if (!userScrolled.value) {
await nextTick() await nextTick()
isAutoScrolling.value = true isAutoScrolling.value = true
logContainer.value.scrollToItem(displayProcessedLogs.value.length - 1) logContainer.value.scrollTo(displayProcessedLogs.value.length - 1)
setTimeout(() => (isAutoScrolling.value = false), 50) setTimeout(() => (isAutoScrolling.value = false), 50)
} }
} }
@@ -489,10 +494,6 @@ onUnmounted(() => {
white-space: nowrap; /* Keeps content on a single line */ white-space: nowrap; /* Keeps content on a single line */
white-space: normal; white-space: normal;
color-scheme: dark; color-scheme: dark;
.no-wrap {
white-space: pre;
}
} }
.filter-checkbox { .filter-checkbox {

View File

@@ -322,6 +322,7 @@
ref="shareModal" ref="shareModal"
share-title="Sharing modpack content" share-title="Sharing modpack content"
share-text="Check out the projects I'm using in my modpack!" share-text="Check out the projects I'm using in my modpack!"
:open-in-new-tab="false"
/> />
<ExportModal v-if="projects.length > 0" ref="exportModal" :instance="instance" /> <ExportModal v-if="projects.length > 0" ref="exportModal" :instance="instance" />
<ModpackVersionModal <ModpackVersionModal
@@ -1110,6 +1111,7 @@ onUnmounted(() => {
} }
} }
</style> </style>
<style lang="scss"> <style lang="scss">
.updating-indicator { .updating-indicator {
height: 2.25rem !important; height: 2.25rem !important;
@@ -1121,10 +1123,6 @@ onUnmounted(() => {
} }
} }
.v-popper--theme-tooltip .v-popper__inner {
background: #fff !important;
}
.select-checkbox { .select-checkbox {
button.checkbox { button.checkbox {
border: none; border: none;

View File

@@ -82,7 +82,7 @@ pub async fn should_disable_mouseover() -> bool {
} }
#[tauri::command] #[tauri::command]
pub fn show_in_folder(path: PathBuf) -> Result<()> { pub fn show_in_folder(mut path: PathBuf) -> Result<()> {
{ {
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
{ {
@@ -101,19 +101,26 @@ pub fn show_in_folder(path: PathBuf) -> Result<()> {
{ {
use std::fs::metadata; use std::fs::metadata;
if path.to_string_lossy().to_string().contains(',') { let path_string = path.to_string_lossy().to_string();
// see https://gitlab.freedesktop.org/dbus/dbus/-/issues/76
let new_path = match metadata(&path)?.is_dir() { if metadata(&path)?.is_dir() {
true => path,
false => {
let mut path2 = path.clone();
path2.pop();
path2
}
};
Command::new("xdg-open").arg(&new_path).spawn()?;
} else {
Command::new("xdg-open").arg(&path).spawn()?; Command::new("xdg-open").arg(&path).spawn()?;
} else if path_string.contains(',') {
// see https://gitlab.freedesktop.org/dbus/dbus/-/issues/76
path.pop();
Command::new("xdg-open").arg(&path).spawn()?;
} else {
Command::new("dbus-send")
.args([
"--session",
"--dest=org.freedesktop.FileManager1",
"--type=method_call",
"/org/freedesktop/FileManager1",
"org.freedesktop.FileManager1.ShowItems",
format!("array:string:file://{}", path_string).as_str(),
"string:\"\"",
])
.spawn()?;
} }
} }

View File

@@ -22,6 +22,8 @@ use serde::{Deserialize, Serialize};
use std::io::Cursor; use std::io::Cursor;
use std::{ use std::{
collections::HashMap, collections::HashMap,
ffi::OsStr,
path,
path::{Path, PathBuf}, path::{Path, PathBuf},
}; };
use uuid::Uuid; use uuid::Uuid;
@@ -65,7 +67,6 @@ impl ProfilePathId {
.ok_or_else(|| { .ok_or_else(|| {
crate::ErrorKind::FSError(format!( crate::ErrorKind::FSError(format!(
"Path {path:?} does not correspond to a profile", "Path {path:?} does not correspond to a profile",
path = path
)) ))
})?; })?;
Ok(Self(path)) Ok(Self(path))
@@ -150,14 +151,15 @@ impl From<InnerProjectPathUnix> for RawProjectPath {
pub struct ProjectPathId(pub PathBuf); pub struct ProjectPathId(pub PathBuf);
impl ProjectPathId { impl ProjectPathId {
// Create a new ProjectPathId from a full file path // Create a new ProjectPathId from a full file path
pub async fn from_fs_path(path: &PathBuf) -> crate::Result<Self> { pub async fn from_fs_path(path: &Path) -> crate::Result<Self> {
// This is avoiding dunce::canonicalize deliberately. On Windows, paths will always be convert to UNC, // This is avoiding dunce::canonicalize deliberately. On Windows, paths will always be convert to UNC,
// but this is ok because we are stripping that with the prefix. Using std::fs avoids different behaviors with dunce that // but this is ok because we are stripping that with the prefix. Using std::fs avoids different behaviors with dunce that
// come with too-long paths // come with too-long paths
let profiles_dir: PathBuf = std::fs::canonicalize( let profiles_dir: PathBuf = std::fs::canonicalize(
State::get().await?.directories.profiles_dir().await, State::get().await?.directories.profiles_dir().await,
)?; )?;
let path: PathBuf = std::fs::canonicalize(path)?; // Normal canonizing resolves symlinks, which results in "path not corresponding to profile" error
let path = path::absolute(path)?;
let path = path let path = path
.strip_prefix(profiles_dir) .strip_prefix(profiles_dir)
.ok() .ok()
@@ -165,7 +167,6 @@ impl ProjectPathId {
.ok_or_else(|| { .ok_or_else(|| {
crate::ErrorKind::FSError(format!( crate::ErrorKind::FSError(format!(
"Path {path:?} does not correspond to a profile", "Path {path:?} does not correspond to a profile",
path = path
)) ))
})?; })?;
Ok(Self(path)) Ok(Self(path))
@@ -485,7 +486,9 @@ impl Profile {
.map_err(|e| IOError::with_path(e, &new_path))? .map_err(|e| IOError::with_path(e, &new_path))?
{ {
let subpath = subpath.map_err(IOError::from)?.path(); let subpath = subpath.map_err(IOError::from)?.path();
if subpath.is_file() { if subpath.is_file()
&& subpath.file_name() != Some(OsStr::new(".DS_Store"))
{
files.push(subpath); files.push(subpath);
} }
} }
@@ -609,6 +612,7 @@ impl Profile {
})?; })?;
if archive.by_name("fabric.mod.json").is_ok() if archive.by_name("fabric.mod.json").is_ok()
|| archive.by_name("quilt.mod.json").is_ok() || archive.by_name("quilt.mod.json").is_ok()
|| archive.by_name("META-INF/neoforge.mods.toml").is_ok()
|| archive.by_name("META-INF/mods.toml").is_ok() || archive.by_name("META-INF/mods.toml").is_ok()
|| archive.by_name("mcmod.info").is_ok() || archive.by_name("mcmod.info").is_ok()
{ {

View File

@@ -33,7 +33,7 @@ impl ProjectType {
pub fn get_from_loaders(loaders: Vec<String>) -> Option<Self> { pub fn get_from_loaders(loaders: Vec<String>) -> Option<Self> {
if loaders if loaders
.iter() .iter()
.any(|x| ["fabric", "forge", "quilt"].contains(&&**x)) .any(|x| ["fabric", "forge", "quilt", "neoforge"].contains(&&**x))
{ {
Some(ProjectType::Mod) Some(ProjectType::Mod)
} else if loaders.iter().any(|x| x == "datapack") { } else if loaders.iter().any(|x| x == "datapack") {

View File

@@ -294,6 +294,7 @@ pub async fn check_java_at_filepath(path: &Path) -> Option<JavaVersion> {
.arg("-cp") .arg("-cp")
.arg(file_path.parent().unwrap()) .arg(file_path.parent().unwrap())
.arg("JavaInfo") .arg("JavaInfo")
.env_remove("_JAVA_OPTIONS")
.output() .output()
.ok()?; .ok()?;

View File

@@ -15,6 +15,9 @@ html {
--radius-lg: 1rem; --radius-lg: 1rem;
--radius-xl: 1.25rem; --radius-xl: 1.25rem;
--radius-max: 999999999px; --radius-max: 999999999px;
--color-tooltip-text: var(--color-base);
--color-tooltip-bg: var(--color-button-bg);
} }
.light-mode, .light-mode,
@@ -23,6 +26,7 @@ html {
--color-raised-bg: #ffffff; --color-raised-bg: #ffffff;
--color-super-raised-bg: #e9e9e9; --color-super-raised-bg: #e9e9e9;
--color-button-bg: hsl(220, 13%, 91%); --color-button-bg: hsl(220, 13%, 91%);
--color-scrollbar: #96a2b0;
--color-base: hsl(221, 39%, 11%); --color-base: hsl(221, 39%, 11%);
--color-secondary: #6b7280; --color-secondary: #6b7280;
@@ -40,9 +44,9 @@ html {
--color-brand-highlight: rgba(0, 175, 92, 0.25); --color-brand-highlight: rgba(0, 175, 92, 0.25);
--color-brand-shadow: rgba(0, 175, 92, 0.7); --color-brand-shadow: rgba(0, 175, 92, 0.7);
--shadow-inset-lg: inset 0px -2px 2px hsla(221, 39%, 11%, 0.1); --shadow-inset-lg: inset 0px -2px 2px hsla(221, 39%, 91%, 0.1);
--shadow-inset: inset 0px -2px 2px hsla(221, 39%, 11%, 0.05); --shadow-inset: inset 0px -2px 2px hsla(221, 39%, 91%, 0.05);
--shadow-inset-sm: inset 0px -1px 2px hsla(221, 39%, 11%, 0.15); --shadow-inset-sm: inset 0px -1px 2px hsla(221, 39%, 91%, 0.15);
--shadow-raised-lg: 0px 2px 4px hsla(221, 39%, 11%, 0.2); --shadow-raised-lg: 0px 2px 4px hsla(221, 39%, 11%, 0.2);
--shadow-raised: 0.3px 0.5px 0.6px hsl(var(--shadow-color) / 0.15), --shadow-raised: 0.3px 0.5px 0.6px hsl(var(--shadow-color) / 0.15),
@@ -53,9 +57,6 @@ html {
--shadow-card: rgba(50, 50, 100, 0.1) 0px 2px 4px 0px; --shadow-card: rgba(50, 50, 100, 0.1) 0px 2px 4px 0px;
--color-tooltip-text: var(--color-accent-contrast);
--color-tooltip-bg: var(--color-base);
--color-ad: #d6e6f9; --color-ad: #d6e6f9;
--color-ad-raised: #b1c8e4; --color-ad-raised: #b1c8e4;
--color-ad-contrast: var(--color-text); --color-ad-contrast: var(--color-text);
@@ -68,6 +69,7 @@ html {
--color-raised-bg: #26292f; --color-raised-bg: #26292f;
--color-super-raised-bg: #40434a; --color-super-raised-bg: #40434a;
--color-button-bg: hsl(222, 13%, 30%); --color-button-bg: hsl(222, 13%, 30%);
--color-scrollbar: var(--color-button-bg);
--color-base: var(--dark-color-base); --color-base: var(--dark-color-base);
--color-secondary: #96a2b0; --color-secondary: #96a2b0;
@@ -96,9 +98,6 @@ html {
--shadow-card: rgba(0, 0, 0, 0.25) 0px 2px 4px 0px; --shadow-card: rgba(0, 0, 0, 0.25) 0px 2px 4px 0px;
--color-tooltip-text: var(--color-base);
--color-tooltip-bg: var(--color-button-bg);
--color-ad: #1f324a; --color-ad: #1f324a;
--color-ad-raised: #2e4057; --color-ad-raised: #2e4057;
--color-ad-contrast: var(--color-text); --color-ad-contrast: var(--color-text);

View File

@@ -7,9 +7,9 @@
"@nuxtjs/eslint-config-typescript": "^12.1.0", "@nuxtjs/eslint-config-typescript": "^12.1.0",
"@vue/eslint-config-typescript": "^13.0.0", "@vue/eslint-config-typescript": "^13.0.0",
"eslint-config-prettier": "^9.1.0", "eslint-config-prettier": "^9.1.0",
"eslint-config-turbo": "^2.0.6", "eslint-config-turbo": "^2.0.7",
"eslint-plugin-prettier": "^5.1.3", "eslint-plugin-prettier": "^5.2.1",
"typescript": "^5.5.3", "eslint-plugin-unicorn": "^54.0.0",
"eslint-plugin-unicorn": "^54.0.0" "typescript": "^5.5.3"
} }
} }

View File

@@ -80,17 +80,15 @@ const props = withDefaults(defineProps<Props>(), {
const currentValue = ref(Math.max(props.min, props.modelValue)) const currentValue = ref(Math.max(props.min, props.modelValue))
const inputValueValid = (newValue: number) => { const inputValueValid = (inputValue: number) => {
if (newValue < props.min) { let newValue = inputValue || props.min
currentValue.value = props.min
} else if (newValue > props.max) {
currentValue.value = props.max
} else if (!newValue) {
currentValue.value = props.min
} else {
currentValue.value = newValue - (props.forceStep ? newValue % props.step : 0)
}
if (props.forceStep) {
newValue -= newValue % props.step
}
newValue = Math.max(props.min, Math.min(newValue, props.max))
currentValue.value = newValue
emit('update:modelValue', currentValue.value) emit('update:modelValue', currentValue.value)
} }

View File

@@ -30,6 +30,10 @@ const props = defineProps({
type: Boolean, type: Boolean,
default: false, default: false,
}, },
openInNewTab: {
type: Boolean,
default: true,
},
}) })
const shareModal = ref(null) const shareModal = ref(null)
@@ -90,6 +94,8 @@ const sendEmail = computed(
&body=${encodeURIComponent(content.value)}`, &body=${encodeURIComponent(content.value)}`,
) )
const targetParameter = computed(() => (props.openInNewTab ? '_blank' : '_self'))
const sendTweet = computed( const sendTweet = computed(
() => `https://twitter.com/intent/tweet?text=${encodeURIComponent(content.value)}`, () => `https://twitter.com/intent/tweet?text=${encodeURIComponent(content.value)}`,
) )
@@ -137,14 +143,19 @@ defineExpose({
<Button v-if="canShare" v-tooltip="'Share'" icon-only @click="share"> <Button v-if="canShare" v-tooltip="'Share'" icon-only @click="share">
<ShareIcon /> <ShareIcon />
</Button> </Button>
<a v-tooltip="'Send as an email'" class="btn icon-only" target="_blank" :href="sendEmail"> <a
v-tooltip="'Send as an email'"
class="btn icon-only"
:href="sendEmail"
:target="targetParameter"
>
<MailIcon /> <MailIcon />
</a> </a>
<a <a
v-if="link" v-if="link"
v-tooltip="'Open link in browser'" v-tooltip="'Open link in browser'"
class="btn icon-only" class="btn icon-only"
target="_blank" :target="targetParameter"
:href="url" :href="url"
> >
<GlobeIcon /> <GlobeIcon />
@@ -152,7 +163,7 @@ defineExpose({
<a <a
v-tooltip="'Toot about it'" v-tooltip="'Toot about it'"
class="btn mastodon icon-only" class="btn mastodon icon-only"
target="_blank" :target="targetParameter"
:href="sendToot" :href="sendToot"
> >
<MastodonIcon /> <MastodonIcon />
@@ -160,7 +171,7 @@ defineExpose({
<a <a
v-tooltip="'Tweet about it'" v-tooltip="'Tweet about it'"
class="btn twitter icon-only" class="btn twitter icon-only"
target="_blank" :target="targetParameter"
:href="sendTweet" :href="sendTweet"
> >
<TwitterIcon /> <TwitterIcon />
@@ -168,7 +179,7 @@ defineExpose({
<a <a
v-tooltip="'Share on Reddit'" v-tooltip="'Share on Reddit'"
class="btn reddit icon-only" class="btn reddit icon-only"
target="_blank" :target="targetParameter"
:href="postOnReddit" :href="postOnReddit"
> >
<RedditIcon /> <RedditIcon />

638
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff