add embed code
This commit is contained in:
parent
ecaf152b4a
commit
b8f9130386
|
@ -476,6 +476,36 @@ export class ClientServerService {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Note Embed
|
||||||
|
fastify.get<{ Params: { note: string; } }>('/notes/:note/embed', async (request, reply) => {
|
||||||
|
reply.removeHeader('X-Frame-Options');
|
||||||
|
reply.header("X-Robots-Tag", "noindex");
|
||||||
|
|
||||||
|
const note = await this.notesRepository.findOneBy({
|
||||||
|
id: request.params.note,
|
||||||
|
visibility: In(['public', 'home']),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (note) {
|
||||||
|
const _note = await this.noteEntityService.pack(note);
|
||||||
|
const profile = await this.userProfilesRepository.findOneByOrFail({ userId: note.userId });
|
||||||
|
const meta = await this.metaService.fetch();
|
||||||
|
reply.header('Cache-Control', 'public, max-age=15');
|
||||||
|
return await reply.view('note', {
|
||||||
|
note: _note,
|
||||||
|
profile,
|
||||||
|
avatarUrl: await this.userEntityService.getAvatarUrl(await this.usersRepository.findOneByOrFail({ id: note.userId })),
|
||||||
|
// TODO: Let locale changeable by instance setting
|
||||||
|
summary: getNoteSummary(_note),
|
||||||
|
instanceName: meta.name ?? 'Misskey',
|
||||||
|
icon: meta.iconUrl,
|
||||||
|
themeColor: meta.themeColor,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return await renderBase(reply);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Page
|
// Page
|
||||||
fastify.get<{ Params: { user: string; page: string; } }>('/@:user/pages/:page', async (request, reply) => {
|
fastify.get<{ Params: { user: string; page: string; } }>('/@:user/pages/:page', async (request, reply) => {
|
||||||
const { username, host } = Acct.parse(request.params.user);
|
const { username, host } = Acct.parse(request.params.user);
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
"eventemitter3": "5.0.0",
|
"eventemitter3": "5.0.0",
|
||||||
"gsap": "3.11.5",
|
"gsap": "3.11.5",
|
||||||
"idb-keyval": "6.2.0",
|
"idb-keyval": "6.2.0",
|
||||||
|
"iframe-resizer": "^4.3.6",
|
||||||
"insert-text-at-cursor": "0.3.0",
|
"insert-text-at-cursor": "0.3.0",
|
||||||
"is-file-animated": "1.0.2",
|
"is-file-animated": "1.0.2",
|
||||||
"json5": "2.2.3",
|
"json5": "2.2.3",
|
||||||
|
@ -97,6 +98,7 @@
|
||||||
"@types/estree": "^1.0.0",
|
"@types/estree": "^1.0.0",
|
||||||
"@types/gulp": "4.0.10",
|
"@types/gulp": "4.0.10",
|
||||||
"@types/gulp-rename": "2.0.1",
|
"@types/gulp-rename": "2.0.1",
|
||||||
|
"@types/iframe-resizer": "^3.5.9",
|
||||||
"@types/matter-js": "0.18.2",
|
"@types/matter-js": "0.18.2",
|
||||||
"@types/node": "18.15.11",
|
"@types/node": "18.15.11",
|
||||||
"@types/punycode": "2.1.0",
|
"@types/punycode": "2.1.0",
|
||||||
|
|
|
@ -54,6 +54,11 @@
|
||||||
<div class="username"><MkAcct :user="appearNote.user"/></div>
|
<div class="username"><MkAcct :user="appearNote.user"/></div>
|
||||||
<MkInstanceTicker v-if="showTicker" class="ticker" :instance="appearNote.user.instance"/>
|
<MkInstanceTicker v-if="showTicker" class="ticker" :instance="appearNote.user.instance"/>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="embed" class="instance-info">
|
||||||
|
<button class="_button" v-click-anime @click="openInstanceMenu">
|
||||||
|
<img :src="instance.iconUrl ?? instance.faviconUrl ?? '/favicon.ico'" alt="" class="icon"/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<div class="main">
|
<div class="main">
|
||||||
<div class="body">
|
<div class="body">
|
||||||
|
@ -91,6 +96,27 @@
|
||||||
</MkA>
|
</MkA>
|
||||||
</div>
|
</div>
|
||||||
<MkReactionsViewer ref="reactionsViewer" :note="appearNote"/>
|
<MkReactionsViewer ref="reactionsViewer" :note="appearNote"/>
|
||||||
|
<template v-if="embed">
|
||||||
|
<MkA class="button _button" :to="notePage(appearNote)">
|
||||||
|
<i class="ti ti-arrow-back-up"></i>
|
||||||
|
<p v-if="appearNote.repliesCount > 0" class="count">{{ appearNote.repliesCount }}</p>
|
||||||
|
</MkA>
|
||||||
|
<MkA v-if="canRenote" class="button _button" :to="notePage(appearNote)">
|
||||||
|
<i class="ti ti-repeat"></i>
|
||||||
|
<p v-if="appearNote.renoteCount > 0" class="count">{{ appearNote.renoteCount }}</p>
|
||||||
|
</MkA>
|
||||||
|
<MkA v-if="appearNote.myReaction == null" class="button _button" :to="notePage(appearNote)">
|
||||||
|
<i v-if="appearNote.reactionAcceptance === 'likeOnly'" class="ti ti-heart"></i>
|
||||||
|
<i v-else class="ti ti-plus"></i>
|
||||||
|
</MkA>
|
||||||
|
<MkA v-if="appearNote.myReaction != null" class="button _button reacted" :to="notePage(appearNote)">
|
||||||
|
<i class="ti ti-minus"></i>
|
||||||
|
</MkA>
|
||||||
|
<MkA v-if="defaultStore.state.showClipButtonInNoteFooter" class="button _button" :to="notePage(appearNote)">
|
||||||
|
<i class="ti ti-paperclip"></i>
|
||||||
|
</MkA>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
<button class="button _button" @click="reply()">
|
<button class="button _button" @click="reply()">
|
||||||
<i class="ti ti-arrow-back-up"></i>
|
<i class="ti ti-arrow-back-up"></i>
|
||||||
<p v-if="appearNote.repliesCount > 0" class="count">{{ appearNote.repliesCount }}</p>
|
<p v-if="appearNote.repliesCount > 0" class="count">{{ appearNote.repliesCount }}</p>
|
||||||
|
@ -117,6 +143,7 @@
|
||||||
<button v-if="defaultStore.state.showClipButtonInNoteFooter" ref="clipButton" class="button _button" @mousedown="clip()">
|
<button v-if="defaultStore.state.showClipButtonInNoteFooter" ref="clipButton" class="button _button" @mousedown="clip()">
|
||||||
<i class="ti ti-paperclip"></i>
|
<i class="ti ti-paperclip"></i>
|
||||||
</button>
|
</button>
|
||||||
|
</template>
|
||||||
<button ref="menuButton" class="button _button" @mousedown="menu()">
|
<button ref="menuButton" class="button _button" @mousedown="menu()">
|
||||||
<i class="ti ti-dots"></i>
|
<i class="ti ti-dots"></i>
|
||||||
</button>
|
</button>
|
||||||
|
@ -158,6 +185,8 @@ import { defaultStore, noteViewInterruptors } from '@/store';
|
||||||
import { reactionPicker } from '@/scripts/reaction-picker';
|
import { reactionPicker } from '@/scripts/reaction-picker';
|
||||||
import { extractUrlFromMfm } from '@/scripts/extract-url-from-mfm';
|
import { extractUrlFromMfm } from '@/scripts/extract-url-from-mfm';
|
||||||
import { $i } from '@/account';
|
import { $i } from '@/account';
|
||||||
|
import { instance } from '@/instance';
|
||||||
|
import { openInstanceMenu } from '@/ui/_common_/common';
|
||||||
import { i18n } from '@/i18n';
|
import { i18n } from '@/i18n';
|
||||||
import { getNoteClipMenu, getNoteMenu } from '@/scripts/get-note-menu';
|
import { getNoteClipMenu, getNoteMenu } from '@/scripts/get-note-menu';
|
||||||
import { useNoteCapture } from '@/scripts/use-note-capture';
|
import { useNoteCapture } from '@/scripts/use-note-capture';
|
||||||
|
@ -170,6 +199,7 @@ import MkRippleEffect from '@/components/MkRippleEffect.vue';
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
note: misskey.entities.Note;
|
note: misskey.entities.Note;
|
||||||
pinned?: boolean;
|
pinned?: boolean;
|
||||||
|
embed?: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const inChannel = inject('inChannel', null);
|
const inChannel = inject('inChannel', null);
|
||||||
|
@ -378,12 +408,12 @@ function onContextmenu(ev: MouseEvent): void {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
react();
|
react();
|
||||||
} else {
|
} else {
|
||||||
os.contextMenu(getNoteMenu({ note: note, translating, translation, menuButton, isDeleted }), ev).then(focus);
|
os.contextMenu(getNoteMenu({ note: note, translating, translation, menuButton, isDeleted, embed: props.embed }), ev).then(focus);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function menu(viaKeyboard = false): void {
|
function menu(viaKeyboard = false): void {
|
||||||
os.popupMenu(getNoteMenu({ note: note, translating, translation, menuButton, isDeleted }), menuButton.value, {
|
os.popupMenu(getNoteMenu({ note: note, translating, translation, menuButton, isDeleted, embed: props.embed }), menuButton.value, {
|
||||||
viaKeyboard,
|
viaKeyboard,
|
||||||
}).then(focus);
|
}).then(focus);
|
||||||
}
|
}
|
||||||
|
@ -464,6 +494,10 @@ if (appearNote.replyId) {
|
||||||
|
|
||||||
&:hover > .article > .main > .footer > .button {
|
&:hover > .article > .main > .footer > .button {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
> .reply-to {
|
> .reply-to {
|
||||||
|
@ -578,6 +612,19 @@ if (appearNote.replyId) {
|
||||||
word-wrap: anywhere;
|
word-wrap: anywhere;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
> .instance-info {
|
||||||
|
flex-shrink: 0;
|
||||||
|
padding-left: 16px;
|
||||||
|
width: 39px;
|
||||||
|
height: 39px;
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
> .main {
|
> .main {
|
||||||
|
@ -734,6 +781,10 @@ if (appearNote.replyId) {
|
||||||
width: 50px;
|
width: 50px;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
}
|
}
|
||||||
|
> .instance-info {
|
||||||
|
width: 33px;
|
||||||
|
height: 33px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
> .main {
|
> .main {
|
||||||
|
|
|
@ -79,6 +79,11 @@ function popout() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function nav(ev: MouseEvent) {
|
function nav(ev: MouseEvent) {
|
||||||
|
if (router.currentRoute.value.name?.toLowerCase().includes("embed")) {
|
||||||
|
window.open(props.to, '_blank');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (props.behavior === 'browser') {
|
if (props.behavior === 'browser') {
|
||||||
location.href = props.to;
|
location.href = props.to;
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -187,6 +187,7 @@ try {
|
||||||
} catch (err) {}
|
} catch (err) {}
|
||||||
|
|
||||||
const app = createApp(
|
const app = createApp(
|
||||||
|
window.location.href.includes("/embed") ? defineAsyncComponent(() => import('@/ui/embed.vue')) :
|
||||||
window.location.search === '?zen' ? defineAsyncComponent(() => import('@/ui/zen.vue')) :
|
window.location.search === '?zen' ? defineAsyncComponent(() => import('@/ui/zen.vue')) :
|
||||||
!$i ? defineAsyncComponent(() => import('@/ui/visitor.vue')) :
|
!$i ? defineAsyncComponent(() => import('@/ui/visitor.vue')) :
|
||||||
ui === 'deck' ? defineAsyncComponent(() => import('@/ui/deck.vue')) :
|
ui === 'deck' ? defineAsyncComponent(() => import('@/ui/deck.vue')) :
|
||||||
|
|
|
@ -0,0 +1,145 @@
|
||||||
|
<template>
|
||||||
|
<div class="fcuexfpr">
|
||||||
|
<Transition :name="defaultStore.state.animation ? 'fade' : ''" mode="out-in">
|
||||||
|
<div v-if="note" class="note">
|
||||||
|
<div class="main">
|
||||||
|
<div class="note _gaps_s">
|
||||||
|
<MkRemoteCaution v-if="note.user.host != null" :href="notePage(note)"/>
|
||||||
|
<MkNoteDetailed :key="note.id" v-model:note="note" :embed="true" class="note"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<MkError v-else-if="error" @retry="fetchNote()"/>
|
||||||
|
<MkLoading v-else/>
|
||||||
|
</Transition>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, watch } from 'vue';
|
||||||
|
import * as misskey from 'misskey-js';
|
||||||
|
import MkNoteDetailed from '@/components/MkNoteDetailed.vue';
|
||||||
|
import MkRemoteCaution from '@/components/MkRemoteCaution.vue';
|
||||||
|
import * as os from '@/os';
|
||||||
|
import { definePageMetadata } from '@/scripts/page-metadata';
|
||||||
|
import { i18n } from '@/i18n';
|
||||||
|
import { dateString } from '@/filters/date';
|
||||||
|
import MkClipPreview from '@/components/MkClipPreview.vue';
|
||||||
|
import { defaultStore } from '@/store';
|
||||||
|
import { notePage } from '@/filters/note';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
noteId: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
let note = $ref<null | misskey.entities.Note>();
|
||||||
|
let error = $ref();
|
||||||
|
|
||||||
|
function fetchNote() {
|
||||||
|
note = null;
|
||||||
|
os.api('notes/show', {
|
||||||
|
noteId: props.noteId,
|
||||||
|
}).then(res => {
|
||||||
|
note = res;
|
||||||
|
Promise.all([
|
||||||
|
os.api('users/notes', {
|
||||||
|
userId: note.userId,
|
||||||
|
untilId: note.id,
|
||||||
|
limit: 1,
|
||||||
|
}),
|
||||||
|
os.api('users/notes', {
|
||||||
|
userId: note.userId,
|
||||||
|
sinceId: note.id,
|
||||||
|
limit: 1,
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
}).catch(err => {
|
||||||
|
error = err;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function goNotePage() {
|
||||||
|
window.open(notePage(note), "_blank");
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(() => props.noteId, fetchNote, {
|
||||||
|
immediate: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const headerActions = $computed(() => []);
|
||||||
|
|
||||||
|
const headerTabs = $computed(() => []);
|
||||||
|
|
||||||
|
definePageMetadata(computed(() => note ? {
|
||||||
|
title: i18n.ts.note,
|
||||||
|
subtitle: dateString(note.createdAt),
|
||||||
|
avatar: note.user,
|
||||||
|
path: `/notes/${note.id}/embed`,
|
||||||
|
share: {
|
||||||
|
title: i18n.t('noteOf', { user: note.user.name }),
|
||||||
|
text: note.text,
|
||||||
|
},
|
||||||
|
} : null));
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.fade-enter-active,
|
||||||
|
.fade-leave-active {
|
||||||
|
transition: opacity 0.125s ease;
|
||||||
|
}
|
||||||
|
.fade-enter-from,
|
||||||
|
.fade-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fcuexfpr {
|
||||||
|
background: var(--bg);
|
||||||
|
|
||||||
|
> .note {
|
||||||
|
height: 100%;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
> a {
|
||||||
|
z-index: 0;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .main {
|
||||||
|
z-index: 1;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
> .load {
|
||||||
|
min-width: 0;
|
||||||
|
margin: 0 auto;
|
||||||
|
border-radius: 999px;
|
||||||
|
|
||||||
|
&.next {
|
||||||
|
margin-bottom: var(--margin);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.prev {
|
||||||
|
margin-top: var(--margin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> .note {
|
||||||
|
> .note {
|
||||||
|
border-radius: var(--radius);
|
||||||
|
background: var(--panel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> .clips {
|
||||||
|
> .title {
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -30,6 +30,10 @@ export const routes = [{
|
||||||
name: 'note',
|
name: 'note',
|
||||||
path: '/notes/:noteId',
|
path: '/notes/:noteId',
|
||||||
component: page(() => import('./pages/note.vue')),
|
component: page(() => import('./pages/note.vue')),
|
||||||
|
}, {
|
||||||
|
name: 'noteEmbed',
|
||||||
|
path: '/notes/:noteId/embed',
|
||||||
|
component: page(() => import('./pages/note-embed.vue')),
|
||||||
}, {
|
}, {
|
||||||
path: '/clips/:clipId',
|
path: '/clips/:clipId',
|
||||||
component: page(() => import('./pages/clip.vue')),
|
component: page(() => import('./pages/clip.vue')),
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
import { url } from '@/config';
|
||||||
|
import { v4 as uuid } from 'uuid';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 埋め込みコードを出力します。
|
||||||
|
*/
|
||||||
|
export function getEmbedCode(props: {
|
||||||
|
entityType: 'notes';
|
||||||
|
id: string;
|
||||||
|
}): string | null {
|
||||||
|
let src: string | undefined = "";
|
||||||
|
|
||||||
|
switch (props.entityType) {
|
||||||
|
case 'notes':
|
||||||
|
src = `${url}/notes/${props.id}/embed`;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
src = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (src !== undefined) {
|
||||||
|
const id = uuid();
|
||||||
|
return `<iframe src="${src}" title="Misskey Note" style="border:none; width:100%; max-width: 650px; min-height: 300px;" data-msky-embed="${id}"></iframe>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/iframe-resizer@4.3.6/js/iframeResizer.min.js" integrity="sha256-86F9vrEnnd2apFWVo5sNxArab6T8L048fPPkYONBDHY=" crossorigin="anonymous"></script>
|
||||||
|
<script>iFrameResize({}, 'iframe[data-msky-embed="${id}"]');</script>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
|
@ -11,6 +11,7 @@ import { noteActions } from '@/store';
|
||||||
import { miLocalStorage } from '@/local-storage';
|
import { miLocalStorage } from '@/local-storage';
|
||||||
import { getUserMenu } from '@/scripts/get-user-menu';
|
import { getUserMenu } from '@/scripts/get-user-menu';
|
||||||
import { clipsCache } from '@/cache';
|
import { clipsCache } from '@/cache';
|
||||||
|
import { getEmbedCode } from './get-embed-code';
|
||||||
|
|
||||||
export async function getNoteClipMenu(props: {
|
export async function getNoteClipMenu(props: {
|
||||||
note: misskey.entities.Note;
|
note: misskey.entities.Note;
|
||||||
|
@ -93,6 +94,7 @@ export function getNoteMenu(props: {
|
||||||
translating: Ref<boolean>;
|
translating: Ref<boolean>;
|
||||||
isDeleted: Ref<boolean>;
|
isDeleted: Ref<boolean>;
|
||||||
currentClip?: misskey.entities.Clip;
|
currentClip?: misskey.entities.Clip;
|
||||||
|
embed?: boolean;
|
||||||
}) {
|
}) {
|
||||||
const isRenote = (
|
const isRenote = (
|
||||||
props.note.renote != null &&
|
props.note.renote != null &&
|
||||||
|
@ -202,8 +204,18 @@ export function getNoteMenu(props: {
|
||||||
}
|
}
|
||||||
|
|
||||||
function openDetail(): void {
|
function openDetail(): void {
|
||||||
|
if (props.embed) {
|
||||||
|
window.open(`/notes/${appearNote.id}`, "_blank");
|
||||||
|
} else {
|
||||||
os.pageWindow(`/notes/${appearNote.id}`);
|
os.pageWindow(`/notes/${appearNote.id}`);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function copyEmbedCode(): void {
|
||||||
|
console.log(getEmbedCode({ entityType: 'notes', id: appearNote.id }));
|
||||||
|
copyToClipboard(getEmbedCode({entityType: 'notes', id: appearNote.id}));
|
||||||
|
os.success();
|
||||||
|
}
|
||||||
|
|
||||||
function showReactions(): void {
|
function showReactions(): void {
|
||||||
os.popup(defineAsyncComponent(() => import('@/components/MkReactedUsersDialog.vue')), {
|
os.popup(defineAsyncComponent(() => import('@/components/MkReactedUsersDialog.vue')), {
|
||||||
|
@ -223,7 +235,7 @@ export function getNoteMenu(props: {
|
||||||
}
|
}
|
||||||
|
|
||||||
let menu;
|
let menu;
|
||||||
if ($i) {
|
if ($i && !props.embed) {
|
||||||
const statePromise = os.api('notes/state', {
|
const statePromise = os.api('notes/state', {
|
||||||
noteId: appearNote.id,
|
noteId: appearNote.id,
|
||||||
});
|
});
|
||||||
|
@ -264,6 +276,11 @@ export function getNoteMenu(props: {
|
||||||
text: i18n.ts.share,
|
text: i18n.ts.share,
|
||||||
action: share,
|
action: share,
|
||||||
},
|
},
|
||||||
|
(!props.embed) ? {
|
||||||
|
icon: 'ti ti-code',
|
||||||
|
text: "Embed",
|
||||||
|
action: copyEmbedCode,
|
||||||
|
} : undefined,
|
||||||
instance.translatorAvailable ? {
|
instance.translatorAvailable ? {
|
||||||
icon: 'ti ti-language-hiragana',
|
icon: 'ti ti-language-hiragana',
|
||||||
text: i18n.ts.translate,
|
text: i18n.ts.translate,
|
||||||
|
@ -356,7 +373,7 @@ export function getNoteMenu(props: {
|
||||||
} else {
|
} else {
|
||||||
menu = [{
|
menu = [{
|
||||||
icon: 'ti ti-external-link',
|
icon: 'ti ti-external-link',
|
||||||
text: i18n.ts.detailed,
|
text: i18n.ts.details,
|
||||||
action: openDetail,
|
action: openDetail,
|
||||||
}, {
|
}, {
|
||||||
icon: 'ti ti-copy',
|
icon: 'ti ti-copy',
|
||||||
|
@ -372,7 +389,11 @@ export function getNoteMenu(props: {
|
||||||
action: () => {
|
action: () => {
|
||||||
window.open(appearNote.url ?? appearNote.uri, '_blank');
|
window.open(appearNote.url ?? appearNote.uri, '_blank');
|
||||||
},
|
},
|
||||||
} : undefined]
|
} : undefined, (!props.embed) ? {
|
||||||
|
icon: 'ti ti-code',
|
||||||
|
text: "Embed",
|
||||||
|
action: copyEmbedCode,
|
||||||
|
} : undefined,]
|
||||||
.filter(x => x !== undefined);
|
.filter(x => x !== undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
<template>
|
||||||
|
<div class="mk-app" style="container-type: inline-size;">
|
||||||
|
<component
|
||||||
|
:is="popup.component"
|
||||||
|
v-for="popup in popups"
|
||||||
|
:key="popup.id"
|
||||||
|
v-bind="popup.props"
|
||||||
|
v-on="popup.events"
|
||||||
|
/>
|
||||||
|
<RouterView/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { provide, ComputedRef } from 'vue';
|
||||||
|
import { mainRouter } from '@/router';
|
||||||
|
import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata';
|
||||||
|
import { instanceName } from '@/config';
|
||||||
|
import { popups } from '@/os';
|
||||||
|
import 'iframe-resizer/js/iframeResizer.contentWindow'
|
||||||
|
|
||||||
|
let pageMetadata = $ref<null | ComputedRef<PageMetadata>>();
|
||||||
|
|
||||||
|
provide('router', mainRouter);
|
||||||
|
provideMetadataReceiver((info) => {
|
||||||
|
pageMetadata = info;
|
||||||
|
if (pageMetadata.value) {
|
||||||
|
document.title = `${pageMetadata.value.title} | ${instanceName}`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
document.documentElement.style.backgroundColor = "transparent";
|
||||||
|
document.documentElement.style.maxWidth = "650px";
|
||||||
|
document.documentElement.style.minHeight = "300px";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.mk-app {
|
||||||
|
max-width: 650px;
|
||||||
|
min-width: 0;
|
||||||
|
min-height: 300px;
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: var(--radius);
|
||||||
|
border: 1px solid var(--divider);
|
||||||
|
box-sizing: border-box;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -663,6 +663,9 @@ importers:
|
||||||
idb-keyval:
|
idb-keyval:
|
||||||
specifier: 6.2.0
|
specifier: 6.2.0
|
||||||
version: 6.2.0
|
version: 6.2.0
|
||||||
|
iframe-resizer:
|
||||||
|
specifier: ^4.3.6
|
||||||
|
version: 4.3.6
|
||||||
insert-text-at-cursor:
|
insert-text-at-cursor:
|
||||||
specifier: 0.3.0
|
specifier: 0.3.0
|
||||||
version: 0.3.0
|
version: 0.3.0
|
||||||
|
@ -832,6 +835,9 @@ importers:
|
||||||
'@types/gulp-rename':
|
'@types/gulp-rename':
|
||||||
specifier: 2.0.1
|
specifier: 2.0.1
|
||||||
version: 2.0.1
|
version: 2.0.1
|
||||||
|
'@types/iframe-resizer':
|
||||||
|
specifier: ^3.5.9
|
||||||
|
version: 3.5.9
|
||||||
'@types/matter-js':
|
'@types/matter-js':
|
||||||
specifier: 0.18.2
|
specifier: 0.18.2
|
||||||
version: 0.18.2
|
version: 0.18.2
|
||||||
|
@ -6614,6 +6620,10 @@ packages:
|
||||||
/@types/http-cache-semantics@4.0.1:
|
/@types/http-cache-semantics@4.0.1:
|
||||||
resolution: {integrity: sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==}
|
resolution: {integrity: sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==}
|
||||||
|
|
||||||
|
/@types/iframe-resizer@3.5.9:
|
||||||
|
resolution: {integrity: sha512-RQUBI75F+uXruB95BFpC/8V8lPgJg4MQ6HxOCtAZYBB/h0FNCfrFfb4I+u2pZJIV7sKeszZbFqy1UnGeBMrvsA==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@types/ioredis@4.28.10:
|
/@types/ioredis@4.28.10:
|
||||||
resolution: {integrity: sha512-69LyhUgrXdgcNDv7ogs1qXZomnfOEnSmrmMFqKgt1XMJxmoOSG/u3wYy13yACIfKuMJ8IhKgHafDO3sx19zVQQ==}
|
resolution: {integrity: sha512-69LyhUgrXdgcNDv7ogs1qXZomnfOEnSmrmMFqKgt1XMJxmoOSG/u3wYy13yACIfKuMJ8IhKgHafDO3sx19zVQQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -12590,6 +12600,11 @@ packages:
|
||||||
/ieee754@1.2.1:
|
/ieee754@1.2.1:
|
||||||
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
|
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
|
||||||
|
|
||||||
|
/iframe-resizer@4.3.6:
|
||||||
|
resolution: {integrity: sha512-wz0WodRIF6eP0oGQa5NIP1yrITAZ59ZJvVaVJqJRjaeCtfm461vy2C3us6CKx0e7pooqpIGLpVMSTzrfAjX9Sg==}
|
||||||
|
engines: {node: '>=0.8.0'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/ignore@5.2.4:
|
/ignore@5.2.4:
|
||||||
resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==}
|
resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==}
|
||||||
engines: {node: '>= 4'}
|
engines: {node: '>= 4'}
|
||||||
|
|
Loading…
Reference in New Issue