merge from misskey-develop

This commit is contained in:
Hazelnoot 2025-04-02 22:29:14 -04:00
commit dab9b518e4
54 changed files with 614 additions and 118 deletions

View file

@ -5,7 +5,7 @@
import { watch, version as vueVersion } from 'vue';
import { compareVersions } from 'compare-versions';
import { version, lang, langsVersion, updateLocale, locale } from '@@/js/config.js';
import { version, lang, langsVersion, updateLocale, locale, apiUrl } from '@@/js/config.js';
import defaultLightTheme from '@@/themes/l-light.json5';
import defaultDarkTheme from '@@/themes/d-green-lime.json5';
import type { App } from 'vue';
@ -282,6 +282,41 @@ export async function common(createVue: () => Promise<App<Element>>) {
return root;
})();
if (instance.sentryForFrontend) {
const Sentry = await import('@sentry/vue');
Sentry.init({
app,
integrations: [
...(instance.sentryForFrontend.vueIntegration !== undefined ? [
Sentry.vueIntegration(instance.sentryForFrontend.vueIntegration ?? undefined),
] : []),
...(instance.sentryForFrontend.browserTracingIntegration !== undefined ? [
Sentry.browserTracingIntegration(instance.sentryForFrontend.browserTracingIntegration ?? undefined),
] : []),
...(instance.sentryForFrontend.replayIntegration !== undefined ? [
Sentry.replayIntegration(instance.sentryForFrontend.replayIntegration ?? undefined),
] : []),
],
// Set tracesSampleRate to 1.0 to capture 100%
tracesSampleRate: 1.0,
// Set `tracePropagationTargets` to control for which URLs distributed tracing should be enabled
...(instance.sentryForFrontend.browserTracingIntegration !== undefined ? {
tracePropagationTargets: [apiUrl],
} : {}),
// Capture Replay for 10% of all sessions,
// plus for 100% of sessions with an error
...(instance.sentryForFrontend.replayIntegration !== undefined ? {
replaysSessionSampleRate: 0.1,
replaysOnErrorSampleRate: 1.0,
} : {}),
...instance.sentryForFrontend.options,
});
}
app.mount(rootEl);
// boot.jsのやつを解除

View file

@ -39,6 +39,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkSwitch v-model="localOnly">{{ i18n.ts.localOnly }}</MkSwitch>
<MkSwitch v-model="caseSensitive">{{ i18n.ts.caseSensitive }}</MkSwitch>
<MkSwitch v-model="withFile">{{ i18n.ts.withFileAntenna }}</MkSwitch>
<MkSwitch v-model="hideNotesInSensitiveChannel">{{ i18n.ts.hideNotesInSensitiveChannel }}</MkSwitch>
</div>
<div :class="$style.actions">
<div class="_buttons">
@ -86,6 +87,7 @@ const initialAntenna = deepMerge<PartialAllowedAntenna>(props.antenna ?? {}, {
caseSensitive: false,
localOnly: false,
withFile: false,
hideNotesInSensitiveChannel: false,
isActive: true,
hasUnreadNote: false,
notify: false,
@ -108,6 +110,7 @@ const localOnly = ref<boolean>(initialAntenna.localOnly);
const excludeBots = ref<boolean>(initialAntenna.excludeBots);
const withReplies = ref<boolean>(initialAntenna.withReplies);
const withFile = ref<boolean>(initialAntenna.withFile);
const hideNotesInSensitiveChannel = ref<boolean>(initialAntenna.hideNotesInSensitiveChannel);
const userLists = ref<Misskey.entities.UserList[] | null>(null);
watch(() => src.value, async () => {
@ -124,6 +127,7 @@ async function saveAntenna() {
excludeBots: excludeBots.value,
withReplies: withReplies.value,
withFile: withFile.value,
hideNotesInSensitiveChannel: hideNotesInSensitiveChannel.value,
caseSensitive: caseSensitive.value,
localOnly: localOnly.value,
users: users.value.trim().split('\n').map(x => x.trim()),

View file

@ -249,6 +249,7 @@ async function close(skip: boolean) {
.pageFooter {
position: sticky;
z-index: 1;
bottom: 0;
left: 0;
flex-shrink: 0;

View file

@ -150,7 +150,7 @@ const headerActions = computed(() => clip.value && isOwned.value ? [{
},
}, {
icon: 'ti ti-code',
text: i18n.ts.genEmbedCode,
text: i18n.ts.embed,
action: () => {
genEmbedCode('clips', clip.value!.id);
},

View file

@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div class="_gaps_m">
<SearchMarker :keywords="['sync', 'profiles', 'devices']">
<MkSwitch :modelValue="profilesSyncEnabled" @update:modelValue="changeProfilesSyncEnabled">
<template #label><SearchLabel>{{ i18n.ts._deck.enableSyncBetweenDevicesForProfiles }}</SearchLabel></template>
<template #label><i class="ti ti-cloud-cog"></i> <SearchLabel>{{ i18n.ts._deck.enableSyncBetweenDevicesForProfiles }}</SearchLabel></template>
</MkSwitch>
</SearchMarker>

View file

@ -26,7 +26,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div class="_gaps_m">
<SearchMarker :keywords="['sync', 'palettes', 'devices']">
<MkSwitch :modelValue="palettesSyncEnabled" @update:modelValue="changePalettesSyncEnabled">
<template #label><SearchLabel>{{ i18n.ts._emojiPalette.enableSyncBetweenDevicesForPalettes }}</SearchLabel></template>
<template #label><i class="ti ti-cloud-cog"></i> <SearchLabel>{{ i18n.ts._emojiPalette.enableSyncBetweenDevicesForPalettes }}</SearchLabel></template>
</MkSwitch>
</SearchMarker>
</div>

View file

@ -181,6 +181,12 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
</div>
<SearchMarker :keywords="['sync', 'themes', 'devices']">
<MkSwitch :modelValue="themesSyncEnabled" @update:modelValue="changeThemesSyncEnabled">
<template #label><i class="ti ti-cloud-cog"></i> <SearchLabel>{{ i18n.ts._settings.enableSyncThemesBetweenDevices }}</SearchLabel></template>
</MkSwitch>
</SearchMarker>
<FormSection>
<div class="_formLinksGrid">
<FormLink to="/settings/theme/manage"><template #icon><i class="ti ti-tool"></i></template>{{ i18n.ts._theme.manage }}<template #suffix>{{ themesCount }}</template></FormLink>
@ -264,6 +270,20 @@ watch(syncDeviceDarkMode, () => {
}
});
const themesSyncEnabled = ref(prefer.isSyncEnabled('themes'));
function changeThemesSyncEnabled(value: boolean) {
if (value) {
prefer.enableSync('themes').then((res) => {
if (res == null) return;
if (res.enabled) themesSyncEnabled.value = true;
});
} else {
prefer.disableSync('themes');
themesSyncEnabled.value = false;
}
}
const headerActions = computed(() => []);
const headerTabs = computed(() => []);

View file

@ -56,7 +56,7 @@ const headerActions = computed(() => [{
label: i18n.ts.more,
handler: (ev: MouseEvent) => {
os.popupMenu([{
text: i18n.ts.genEmbedCode,
text: i18n.ts.embed,
icon: 'ti ti-code',
action: () => {
genEmbedCode('tags', props.tag);

View file

@ -12,15 +12,15 @@ SPDX-License-Identifier: AGPL-3.0-only
<XAnnouncements v-if="$i"/>
<XStatusBars/>
<div :class="$style.columnsWrapper">
<div ref="columnsEl" :class="[$style.columns, { [$style.center]: prefer.r['deck.columnAlign'].value === 'center', [$style.snapScroll]: snapScroll }]" @contextmenu.self.prevent="onContextmenu" @wheel.self="onWheel">
<!-- passive: https://bugs.webkit.org/show_bug.cgi?id=281300 -->
<div ref="columnsEl" :class="[$style.columns, { [$style.center]: prefer.r['deck.columnAlign'].value === 'center', [$style.snapScroll]: snapScroll }]" @contextmenu.self.prevent="onContextmenu" @wheel.passive.self="onWheel">
<!-- sectionを利用しているのはdeck.vue側でcolumnに対してfirst-of-typeを効かせるため -->
<section
v-for="ids in layout"
:class="$style.section"
:style="columns.filter(c => ids.includes(c.id)).some(c => c.flexible) ? { flex: 1, minWidth: '350px' } : { width: Math.max(...columns.filter(c => ids.includes(c.id)).map(c => c.width)) + 'px' }"
@wheel.self="onWheel"
@wheel.passive.self="onWheel"
>
<Suspense>
<component
@ -175,7 +175,8 @@ window.addEventListener('resize', () => {
isMobile.value = window.innerWidth <= 500;
});
const snapScroll = deviceKind === 'smartphone' || deviceKind === 'tablet';
// UA
const snapScroll = ref(deviceKind === 'smartphone' || deviceKind === 'tablet');
const withWallpaper = prefer.s['deck.wallpaper'] != null;
const drawerMenuShowing = ref(false);
const gap = prefer.r['deck.columnGap'];
@ -226,7 +227,16 @@ const onContextmenu = (ev) => {
}], ev);
};
//
function pointerEvent(ev: PointerEvent) {
snapScroll.value = ev.pointerType === 'touch';
}
window.document.addEventListener('pointerdown', pointerEvent, { passive: true });
function onWheel(ev: WheelEvent) {
// WheelEvent
snapScroll.value = false;
if (ev.deltaX === 0 && columnsEl.value != null) {
columnsEl.value.scrollLeft += ev.deltaY;
}

View file

@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only
@dragstart="onDragstart"
@dragend="onDragend"
@contextmenu.prevent.stop="onContextmenu"
@wheel="emit('headerWheel', $event)"
@wheel.passive="emit('headerWheel', $event)"
>
<svg viewBox="0 0 256 128" :class="$style.tabShape">
<g transform="matrix(6.2431,0,0,6.2431,-677.417,-29.3839)">

View file

@ -37,6 +37,11 @@ export const searchIndexes: SearchIndexItem[] = [
label: i18n.ts.themeForDarkMode,
keywords: ['dark', 'theme'],
},
{
id: 'jwW5HULqA',
label: i18n.ts._settings.enableSyncThemesBetweenDevices,
keywords: ['sync', 'themes', 'devices'],
},
],
label: i18n.ts.theme,
keywords: ['theme'],

View file

@ -342,7 +342,7 @@ export function getNoteMenu(props: {
},
});
} else {
menuItems.push(getNoteEmbedCodeMenu(appearNote, i18n.ts.genEmbedCode));
menuItems.push(getNoteEmbedCodeMenu(appearNote, i18n.ts.embed));
}
if (isSupportShare()) {
@ -506,7 +506,7 @@ export function getNoteMenu(props: {
},
});
} else {
menuItems.push(getNoteEmbedCodeMenu(appearNote, i18n.ts.genEmbedCode));
menuItems.push(getNoteEmbedCodeMenu(appearNote, i18n.ts.embed));
}
}

View file

@ -197,7 +197,7 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: Router
} else {
menuItems.push({
icon: 'ti ti-code',
text: i18n.ts.genEmbedCode,
text: i18n.ts.embed,
type: 'parent',
children: [{
text: i18n.ts.noteOfThisUser,