Merge remote-tracking branch 'misskey-original/develop' into develop
This commit is contained in:
		
						commit
						b3084f45b4
					
				|  | @ -17,11 +17,16 @@ | |||
| ### General | ||||
| - Feat: アイコンデコレーション機能 | ||||
| - Enhance: すでにフォローしたすべての人の返信をTLに追加できるように | ||||
| - Enhance: ローカリゼーションの更新 | ||||
| - Enhance: 依存関係の更新 | ||||
| 
 | ||||
| ### Client | ||||
| - Feat: プラグイン・テーマを外部サイトから直接インストールできるようになりました | ||||
| 	- 外部サイトでの実装が必要です。詳細は Misskey Hub をご覧ください | ||||
| 	  https://misskey-hub.net/docs/advanced/publish-on-your-website.html | ||||
| - Enhance: コードのシンタックスハイライトエンジンをShikiに変更 | ||||
|   - AiScriptのシンタックスハイライトに対応 | ||||
|   - MFMでAiScriptをハイライトする場合、コードブロックの開始部分を ` ```is ` もしくは ` ```aiscript ` としてください | ||||
| - Enhance: データセーバー有効時はアニメーション付きのアバター画像が停止するように | ||||
| - Enhance: プラグインを削除した際には、使用されていたアクセストークンも同時に削除されるようになりました | ||||
| - Enhance: プラグインで`Plugin:register_note_view_interruptor`を用いてnoteの代わりにnullを返却することでノートを非表示にできるようになりました | ||||
|  | @ -30,6 +35,7 @@ | |||
| - Fix: ユーザーページの ノート > ファイル付き タブにリプライが表示されてしまう | ||||
| - Fix: 「検索」MFMにおいて一部の検索キーワードが正しく認識されない問題を修正 | ||||
| - Fix: 一部の言語でMisskey Webがクラッシュする問題を修正 | ||||
| - Fix: チャンネルの作成・更新時に失敗した場合何も表示されない問題を修正 #11983 | ||||
| 
 | ||||
| ### Server | ||||
| - Enhance: RedisへのTLのキャッシュをオフにできるように | ||||
|  | @ -40,6 +46,7 @@ | |||
| - Fix: RedisへのTLキャッシュが有効の場合にHTL/LTL/STLが空になることがある問題を修正 | ||||
| - Fix: STLでフォローしていないチャンネルが取得される問題を修正 | ||||
| - Fix: `hashtags/trend`にてRedisからトレンドの情報が取得できない際にInternal Server Errorになる問題を修正 | ||||
| - Fix: HTLをリロードまたは遡行したとき、フォローしているチャンネルのノートが含まれない問題を修正 #11765 | ||||
| 
 | ||||
| ## 2023.10.2 | ||||
| 
 | ||||
|  |  | |||
|  | @ -1744,6 +1744,7 @@ export interface Locale { | |||
|         "donate": string; | ||||
|         "morePatrons": string; | ||||
|         "patrons": string; | ||||
|         "projectMembers": string; | ||||
|     }; | ||||
|     "_displayOfSensitiveMedia": { | ||||
|         "respect": string; | ||||
|  |  | |||
|  | @ -1653,7 +1653,7 @@ _registry: | |||
| 
 | ||||
| _aboutMisskey: | ||||
|   about: "Misskeyはsyuiloによって2014年から開発されている、オープンソースのソフトウェアです。" | ||||
|   contributors: "主なコントリビューター" | ||||
|   contributors: "コントリビューター" | ||||
|   allContributors: "全てのコントリビューター" | ||||
|   source: "ソースコード" | ||||
|   forksource: "当フォークのソースコード" | ||||
|  | @ -1661,6 +1661,7 @@ _aboutMisskey: | |||
|   donate: "Misskeyに寄付" | ||||
|   morePatrons: "他にも多くの方が支援してくれています。ありがとうございます🥰" | ||||
|   patrons: "支援者" | ||||
|   projectMembers: "プロジェクトメンバー" | ||||
| 
 | ||||
| _displayOfSensitiveMedia: | ||||
|   respect: "センシティブ設定されたメディアを隠す" | ||||
|  |  | |||
|  | @ -237,7 +237,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- | |||
| 		if (followingChannels.length > 0) { | ||||
| 			const followingChannelIds = followingChannels.map(x => x.followeeId); | ||||
| 
 | ||||
| 			query.andWhere('note.channelId IN (:...followingChannelIds) OR note.channelId IS NULL', { followingChannelIds }); | ||||
| 			query.andWhere(new Brackets(qb => { | ||||
| 				qb.where('note.channelId IN (:...followingChannelIds)', { followingChannelIds }); | ||||
| 				qb.orWhere('note.channelId IS NULL'); | ||||
| 			})); | ||||
| 		} else { | ||||
| 			query.andWhere('note.channelId IS NULL'); | ||||
| 		} | ||||
|  |  | |||
|  | @ -5,7 +5,7 @@ | |||
| 
 | ||||
| import { Brackets } from 'typeorm'; | ||||
| import { Inject, Injectable } from '@nestjs/common'; | ||||
| import type { MiNote, NotesRepository } from '@/models/_.js'; | ||||
| import type { MiNote, NotesRepository, ChannelFollowingsRepository } from '@/models/_.js'; | ||||
| import { Endpoint } from '@/server/api/endpoint-base.js'; | ||||
| import { QueryService } from '@/core/QueryService.js'; | ||||
| import ActiveUsersChart from '@/core/chart/charts/active-users.js'; | ||||
|  | @ -58,6 +58,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- | |||
| 		@Inject(DI.notesRepository) | ||||
| 		private notesRepository: NotesRepository, | ||||
| 
 | ||||
| 		@Inject(DI.channelFollowingsRepository) | ||||
| 		private channelFollowingsRepository: ChannelFollowingsRepository, | ||||
| 
 | ||||
| 		private noteEntityService: NoteEntityService, | ||||
| 		private activeUsersChart: ActiveUsersChart, | ||||
| 		private idService: IdService, | ||||
|  | @ -160,22 +163,48 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- | |||
| 
 | ||||
| 	private async getFromDb(ps: { untilId: string | null; sinceId: string | null; limit: number; includeMyRenotes: boolean; includeRenotedMyNotes: boolean; includeLocalRenotes: boolean; withFiles: boolean; withRenotes: boolean; }, me: MiLocalUser) { | ||||
| 		const followees = await this.userFollowingService.getFollowees(me.id); | ||||
| 		const followingChannels = await this.channelFollowingsRepository.find({ | ||||
| 			where: { | ||||
| 				followerId: me.id, | ||||
| 			}, | ||||
| 		}); | ||||
| 
 | ||||
| 		//#region Construct query
 | ||||
| 		const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId) | ||||
| 			.andWhere('note.channelId IS NULL') | ||||
| 			.innerJoinAndSelect('note.user', 'user') | ||||
| 			.leftJoinAndSelect('note.reply', 'reply') | ||||
| 			.leftJoinAndSelect('note.renote', 'renote') | ||||
| 			.leftJoinAndSelect('reply.user', 'replyUser') | ||||
| 			.leftJoinAndSelect('renote.user', 'renoteUser'); | ||||
| 
 | ||||
| 		if (followees.length > 0) { | ||||
| 		if (followees.length > 0 && followingChannels.length > 0) { | ||||
| 			// ユーザー・チャンネルともにフォローあり
 | ||||
| 			const meOrFolloweeIds = [me.id, ...followees.map(f => f.followeeId)]; | ||||
| 
 | ||||
| 			query.andWhere('note.userId IN (:...meOrFolloweeIds)', { meOrFolloweeIds: meOrFolloweeIds }); | ||||
| 			const followingChannelIds = followingChannels.map(x => x.followeeId); | ||||
| 			query.andWhere(new Brackets(qb => { | ||||
| 				qb | ||||
| 					.where('note.userId IN (:...meOrFolloweeIds)', { meOrFolloweeIds: meOrFolloweeIds }) | ||||
| 					.orWhere('note.channelId IN (:...followingChannelIds)', { followingChannelIds }); | ||||
| 			})); | ||||
| 		} else if (followees.length > 0) { | ||||
| 			// ユーザーフォローのみ(チャンネルフォローなし)
 | ||||
| 			const meOrFolloweeIds = [me.id, ...followees.map(f => f.followeeId)]; | ||||
| 			query | ||||
| 				.andWhere('note.channelId IS NULL') | ||||
| 				.andWhere('note.userId IN (:...meOrFolloweeIds)', { meOrFolloweeIds: meOrFolloweeIds }); | ||||
| 		} else if (followingChannels.length > 0) { | ||||
| 			// チャンネルフォローのみ(ユーザーフォローなし)
 | ||||
| 			const followingChannelIds = followingChannels.map(x => x.followeeId); | ||||
| 			query.andWhere(new Brackets(qb => { | ||||
| 				qb | ||||
| 					.where('note.channelId IN (:...followingChannelIds)', { followingChannelIds }) | ||||
| 					.orWhere('note.userId = :meId', { meId: me.id }); | ||||
| 			})); | ||||
| 		} else { | ||||
| 			query.andWhere('note.userId = :meId', { meId: me.id }); | ||||
| 			// フォローなし
 | ||||
| 			query | ||||
| 				.andWhere('note.channelId IS NULL') | ||||
| 				.andWhere('note.userId = :meId', { meId: me.id }); | ||||
| 		} | ||||
| 
 | ||||
| 		query.andWhere(new Brackets(qb => { | ||||
|  |  | |||
|  | @ -29,6 +29,7 @@ | |||
| 		"@vue/compiler-sfc": "3.3.7", | ||||
| 		"astring": "1.8.6", | ||||
| 		"autosize": "6.0.1", | ||||
| 		"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.0.5", | ||||
| 		"broadcast-channel": "5.5.1", | ||||
| 		"browser-image-resizer": "github:misskey-dev/browser-image-resizer#v2.2.1-misskey.3", | ||||
| 		"buraha": "0.0.1", | ||||
|  | @ -54,11 +55,11 @@ | |||
| 		"mfm-js": "0.23.3", | ||||
| 		"misskey-js": "workspace:*", | ||||
| 		"photoswipe": "5.4.2", | ||||
| 		"prismjs": "1.29.0", | ||||
| 		"punycode": "2.3.0", | ||||
| 		"querystring": "0.2.1", | ||||
| 		"rollup": "4.1.4", | ||||
| 		"sanitize-html": "2.11.0", | ||||
| 		"shiki": "^0.14.5", | ||||
| 		"sass": "1.69.5", | ||||
| 		"strict-event-emitter-types": "2.0.0", | ||||
| 		"textarea-caret": "3.1.0", | ||||
|  | @ -74,7 +75,6 @@ | |||
| 		"vanilla-tilt": "1.8.1", | ||||
| 		"vite": "4.5.0", | ||||
| 		"vue": "3.3.7", | ||||
| 		"vue-prism-editor": "2.0.0-alpha.2", | ||||
| 		"vuedraggable": "next" | ||||
| 	}, | ||||
| 	"devDependencies": { | ||||
|  |  | |||
|  | @ -5,21 +5,90 @@ SPDX-License-Identifier: AGPL-3.0-only | |||
| 
 | ||||
| <!-- eslint-disable vue/no-v-html --> | ||||
| <template> | ||||
| <code v-if="inline" :class="`language-${prismLang}`" style="overflow-wrap: anywhere;" v-html="html"></code> | ||||
| <pre v-else :class="`language-${prismLang}`"><code :class="`language-${prismLang}`" v-html="html"></code></pre> | ||||
| <div :class="['codeBlockRoot', { 'codeEditor': codeEditor }]" v-html="html"></div> | ||||
| </template> | ||||
| 
 | ||||
| <script lang="ts" setup> | ||||
| import { computed } from 'vue'; | ||||
| import Prism from 'prismjs'; | ||||
| import 'prismjs/themes/prism-okaidia.css'; | ||||
| import { ref, computed, watch } from 'vue'; | ||||
| import { BUNDLED_LANGUAGES } from 'shiki'; | ||||
| import type { Lang as ShikiLang } from 'shiki'; | ||||
| import { getHighlighter } from '@/scripts/code-highlighter.js'; | ||||
| 
 | ||||
| const props = defineProps<{ | ||||
| 	code: string; | ||||
| 	lang?: string; | ||||
| 	inline?: boolean; | ||||
| 	codeEditor?: boolean; | ||||
| }>(); | ||||
| 
 | ||||
| const prismLang = computed(() => Prism.languages[props.lang] ? props.lang : 'js'); | ||||
| const html = computed(() => Prism.highlight(props.code, Prism.languages[prismLang.value], prismLang.value)); | ||||
| const highlighter = await getHighlighter(); | ||||
| 
 | ||||
| const codeLang = ref<ShikiLang | 'aiscript'>('js'); | ||||
| const html = computed(() => highlighter.codeToHtml(props.code, { | ||||
| 	lang: codeLang.value, | ||||
| 	theme: 'dark-plus', | ||||
| })); | ||||
| 
 | ||||
| async function fetchLanguage(to: string): Promise<void> { | ||||
| 	const language = to as ShikiLang; | ||||
| 
 | ||||
| 	// Check for the loaded languages, and load the language if it's not loaded yet. | ||||
| 	if (!highlighter.getLoadedLanguages().includes(language)) { | ||||
| 		// Check if the language is supported by Shiki | ||||
| 		const bundles = BUNDLED_LANGUAGES.filter((bundle) => { | ||||
| 			// Languages are specified by their id, they can also have aliases (i. e. "js" and "javascript") | ||||
| 			return bundle.id === language || bundle.aliases?.includes(language); | ||||
| 		}); | ||||
| 		if (bundles.length > 0) { | ||||
| 			await highlighter.loadLanguage(language); | ||||
| 			codeLang.value = language; | ||||
| 		} else { | ||||
| 			codeLang.value = 'js'; | ||||
| 		} | ||||
| 	} else { | ||||
| 		codeLang.value = language; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| watch(() => props.lang, (to) => { | ||||
| 	if (codeLang.value === to || !to) return; | ||||
| 	return new Promise((resolve) => { | ||||
| 		fetchLanguage(to).then(() => resolve); | ||||
| 	}); | ||||
| }, { immediate: true, }); | ||||
| </script> | ||||
| 
 | ||||
| <style scoped lang="scss"> | ||||
| .codeBlockRoot :deep(.shiki) { | ||||
| 	padding: 1em; | ||||
| 	margin: .5em 0; | ||||
| 	overflow: auto; | ||||
| 	border-radius: .3em; | ||||
| 
 | ||||
| 	& pre, | ||||
| 	& code { | ||||
| 		font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| .codeBlockRoot.codeEditor { | ||||
| 	min-width: 100%; | ||||
| 	height: 100%; | ||||
| 
 | ||||
| 	& :deep(.shiki) { | ||||
| 		padding: 12px; | ||||
| 		margin: 0; | ||||
| 		border-radius: 6px; | ||||
| 		min-height: 130px; | ||||
| 		pointer-events: none; | ||||
| 		min-width: calc(100% - 24px); | ||||
| 		height: 100%; | ||||
| 		display: inline-block; | ||||
| 		line-height: 1.5em; | ||||
| 		font-size: 1em; | ||||
| 		overflow: visible; | ||||
| 		text-rendering: inherit; | ||||
|     text-transform: inherit; | ||||
|     white-space: pre; | ||||
| 	} | ||||
| } | ||||
| </style> | ||||
|  |  | |||
|  | @ -4,11 +4,18 @@ SPDX-License-Identifier: AGPL-3.0-only | |||
| --> | ||||
| 
 | ||||
| <template> | ||||
| <XCode :code="code" :lang="lang" :inline="inline"/> | ||||
| 	<Suspense> | ||||
| 		<template #fallback> | ||||
| 			<MkLoading v-if="!inline ?? true" /> | ||||
| 		</template> | ||||
| 		<code v-if="inline" :class="$style.codeInlineRoot">{{ code }}</code> | ||||
| 		<XCode v-else :code="code" :lang="lang"/> | ||||
| 	</Suspense> | ||||
| </template> | ||||
| 
 | ||||
| <script lang="ts" setup> | ||||
| import { defineAsyncComponent } from 'vue'; | ||||
| import MkLoading from '@/components/global/MkLoading.vue'; | ||||
| 
 | ||||
| defineProps<{ | ||||
| 	code: string; | ||||
|  | @ -18,3 +25,15 @@ defineProps<{ | |||
| 
 | ||||
| const XCode = defineAsyncComponent(() => import('@/components/MkCode.core.vue')); | ||||
| </script> | ||||
| 
 | ||||
| <style module lang="scss"> | ||||
| .codeInlineRoot { | ||||
| 	display: inline-block; | ||||
| 	font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace; | ||||
| 	overflow-wrap: anywhere; | ||||
| 	color: #D4D4D4; | ||||
| 	background: #1E1E1E; | ||||
| 	padding: .1em; | ||||
| 	border-radius: .3em; | ||||
| } | ||||
| </style> | ||||
|  |  | |||
|  | @ -0,0 +1,166 @@ | |||
| <!-- | ||||
| SPDX-FileCopyrightText: syuilo and other misskey contributors | ||||
| SPDX-License-Identifier: AGPL-3.0-only | ||||
| --> | ||||
| 
 | ||||
| <template> | ||||
| <div :class="[$style.codeEditorRoot, { [$style.disabled]: disabled, [$style.focused]: focused }]"> | ||||
| 	<div :class="$style.codeEditorScroller"> | ||||
| 		<textarea | ||||
| 			ref="inputEl" | ||||
| 			v-model="vModel" | ||||
| 			:class="[$style.textarea]" | ||||
| 			:disabled="disabled" | ||||
| 			:required="required" | ||||
| 			:readonly="readonly" | ||||
| 			autocomplete="off" | ||||
| 			wrap="off" | ||||
| 			spellcheck="false" | ||||
| 			@focus="focused = true" | ||||
| 			@blur="focused = false" | ||||
| 			@keydown="onKeydown($event)" | ||||
| 			@input="onInput" | ||||
| 		></textarea> | ||||
| 		<XCode :class="$style.codeEditorHighlighter" :codeEditor="true" :code="v" :lang="lang"/> | ||||
| 	</div> | ||||
| </div> | ||||
| </template> | ||||
| 
 | ||||
| <script lang="ts" setup> | ||||
| import { ref, watch, toRefs, shallowRef, nextTick } from 'vue'; | ||||
| import XCode from '@/components/MkCode.core.vue'; | ||||
| 
 | ||||
| const props = withDefaults(defineProps<{ | ||||
| 	modelValue: string | null; | ||||
| 	lang: string; | ||||
| 	required?: boolean; | ||||
| 	readonly?: boolean; | ||||
| 	disabled?: boolean; | ||||
| }>(), { | ||||
| 	lang: 'js', | ||||
| }); | ||||
| 
 | ||||
| const emit = defineEmits<{ | ||||
| 	(ev: 'change', _ev: KeyboardEvent): void; | ||||
| 	(ev: 'keydown', _ev: KeyboardEvent): void; | ||||
| 	(ev: 'enter'): void; | ||||
| 	(ev: 'update:modelValue', value: string): void; | ||||
| }>(); | ||||
| 
 | ||||
| const { modelValue } = toRefs(props); | ||||
| const vModel = ref<string>(modelValue.value ?? ''); | ||||
| const v = ref<string>(modelValue.value ?? ''); | ||||
| const focused = ref(false); | ||||
| const changed = ref(false); | ||||
| const inputEl = shallowRef<HTMLTextAreaElement>(); | ||||
| 
 | ||||
| const onInput = (ev) => { | ||||
| 	v.value = ev.target?.value ?? v.value; | ||||
| 	changed.value = true; | ||||
| 	emit('change', ev); | ||||
| }; | ||||
| 
 | ||||
| const onKeydown = (ev: KeyboardEvent) => { | ||||
| 	if (ev.isComposing || ev.key === 'Process' || ev.keyCode === 229) return; | ||||
| 
 | ||||
| 	emit('keydown', ev); | ||||
| 
 | ||||
| 	if (ev.code === 'Enter') { | ||||
| 		const pos = inputEl.value?.selectionStart ?? 0; | ||||
| 		const posEnd = inputEl.value?.selectionEnd ?? vModel.value.length; | ||||
| 		if (pos === posEnd) { | ||||
| 			const lines = vModel.value.slice(0, pos).split('\n'); | ||||
| 			const currentLine = lines[lines.length - 1]; | ||||
| 			const currentLineSpaces = currentLine.match(/^\s+/); | ||||
| 			const posDelta = currentLineSpaces ? currentLineSpaces[0].length : 0; | ||||
| 			ev.preventDefault(); | ||||
| 			vModel.value = vModel.value.slice(0, pos) + '\n' + (currentLineSpaces ? currentLineSpaces[0] : '') + vModel.value.slice(pos); | ||||
| 			v.value = vModel.value; | ||||
| 			nextTick(() => { | ||||
| 				inputEl.value?.setSelectionRange(pos + 1 + posDelta, pos + 1 + posDelta); | ||||
| 			}); | ||||
| 		} | ||||
| 		emit('enter'); | ||||
| 	} | ||||
| 
 | ||||
| 	if (ev.key === 'Tab') { | ||||
| 		const pos = inputEl.value?.selectionStart ?? 0; | ||||
| 		const posEnd = inputEl.value?.selectionEnd ?? vModel.value.length; | ||||
| 		vModel.value = vModel.value.slice(0, pos) + '\t' + vModel.value.slice(posEnd); | ||||
| 		v.value = vModel.value; | ||||
| 		nextTick(() => { | ||||
| 			inputEl.value?.setSelectionRange(pos + 1, pos + 1); | ||||
| 		}); | ||||
| 		ev.preventDefault(); | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| const updated = () => { | ||||
| 	changed.value = false; | ||||
| 	emit('update:modelValue', v.value); | ||||
| }; | ||||
| 
 | ||||
| watch(modelValue, newValue => { | ||||
| 	v.value = newValue ?? ''; | ||||
| }); | ||||
| 
 | ||||
| watch(v, () => { | ||||
| 	updated(); | ||||
| }); | ||||
| </script> | ||||
| 
 | ||||
| <style lang="scss" module> | ||||
| .codeEditorRoot { | ||||
| 	min-width: 100%; | ||||
| 	max-width: 100%; | ||||
| 	overflow-x: auto; | ||||
| 	overflow-y: hidden; | ||||
| 	box-sizing: border-box; | ||||
| 	margin: 0; | ||||
| 	padding: 0; | ||||
| 	color: var(--fg); | ||||
| 	border: solid 1px var(--panel); | ||||
| 	transition: border-color 0.1s ease-out; | ||||
| 	font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace; | ||||
| 	&:hover { | ||||
| 		border-color: var(--inputBorderHover) !important; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| .focused.codeEditorRoot { | ||||
| 	border-color: var(--accent) !important; | ||||
| 	border-radius: 6px; | ||||
| } | ||||
| 
 | ||||
| .codeEditorScroller { | ||||
| 	position: relative; | ||||
| 	display: inline-block; | ||||
| 	min-width: 100%; | ||||
| 	height: 100%; | ||||
| } | ||||
| 
 | ||||
| .textarea { | ||||
| 	position: absolute; | ||||
| 	top: 0; | ||||
| 	left: 0; | ||||
| 	right: 0; | ||||
| 	bottom: 0; | ||||
| 	display: inline-block; | ||||
| 	appearance: none; | ||||
| 	resize: none; | ||||
| 	text-align: left; | ||||
| 	color: transparent; | ||||
| 	caret-color: rgb(225, 228, 232); | ||||
| 	background-color: transparent; | ||||
| 	border: 0; | ||||
| 	outline: 0; | ||||
| 	padding: 12px; | ||||
| 	line-height: 1.5em; | ||||
| 	font-size: 1em; | ||||
| 	font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace; | ||||
| } | ||||
| 
 | ||||
| .textarea::selection { | ||||
| 	color: #fff; | ||||
| } | ||||
| </style> | ||||
|  | @ -42,6 +42,7 @@ export default defineComponent({ | |||
| 
 | ||||
| 	setup(props, { slots, expose }) { | ||||
| 		const $style = useCssModule(); // カスタムレンダラなので使っても大丈夫 | ||||
| 
 | ||||
| 		function getDateText(time: string) { | ||||
| 			const date = new Date(time).getDate(); | ||||
| 			const month = new Date(time).getMonth() + 1; | ||||
|  | @ -121,6 +122,7 @@ export default defineComponent({ | |||
| 			el.style.top = `${el.offsetTop}px`; | ||||
| 			el.style.left = `${el.offsetLeft}px`; | ||||
| 		} | ||||
| 
 | ||||
| 		function onLeaveCanceled(el: HTMLElement) { | ||||
| 			el.style.top = ''; | ||||
| 			el.style.left = ''; | ||||
|  |  | |||
|  | @ -165,6 +165,7 @@ async function ok() { | |||
| function cancel() { | ||||
| 	done(true); | ||||
| } | ||||
| 
 | ||||
| /* | ||||
| function onBgClick() { | ||||
| 	if (props.cancelableByBgClick) cancel(); | ||||
|  |  | |||
|  | @ -84,6 +84,7 @@ onMounted(() => { | |||
| 			return getParentBg(el.parentElement); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	const rawBg = getParentBg(el.value); | ||||
| 	const _bg = tinycolor(rawBg.startsWith('var(') ? getComputedStyle(document.documentElement).getPropertyValue(rawBg.slice(4, -1)) : rawBg); | ||||
| 	_bg.setAlpha(0.85); | ||||
|  |  | |||
|  | @ -147,11 +147,13 @@ const onGlobalMousedown = (event: MouseEvent) => { | |||
| }; | ||||
| 
 | ||||
| let childCloseTimer: null | number = null; | ||||
| 
 | ||||
| function onItemMouseEnter(item) { | ||||
| 	childCloseTimer = window.setTimeout(() => { | ||||
| 		closeChild(); | ||||
| 	}, 300); | ||||
| } | ||||
| 
 | ||||
| function onItemMouseLeave(item) { | ||||
| 	if (childCloseTimer) window.clearTimeout(childCloseTimer); | ||||
| } | ||||
|  |  | |||
|  | @ -526,6 +526,7 @@ function blur() { | |||
| } | ||||
| 
 | ||||
| const repliesLoaded = ref(false); | ||||
| 
 | ||||
| function loadReplies() { | ||||
| 	repliesLoaded.value = true; | ||||
| 	os.api('notes/children', { | ||||
|  | @ -537,6 +538,7 @@ function loadReplies() { | |||
| } | ||||
| 
 | ||||
| const conversationLoaded = ref(false); | ||||
| 
 | ||||
| function loadConversation() { | ||||
| 	conversationLoaded.value = true; | ||||
| 	os.api('notes/conversation', { | ||||
|  |  | |||
|  | @ -87,6 +87,7 @@ function arrayToEntries(entities: MisskeyEntity[]): [string, MisskeyEntity][] { | |||
| function concatMapWithArray(map: MisskeyEntityMap, entities: MisskeyEntity[]): MisskeyEntityMap { | ||||
| 	return new Map([...map, ...arrayToEntries(entities)]); | ||||
| } | ||||
| 
 | ||||
| </script> | ||||
| <script lang="ts" setup> | ||||
| import { infoImageUrl } from '@/instance.js'; | ||||
|  |  | |||
|  | @ -59,6 +59,7 @@ function toggleSensitive(file) { | |||
| 		emit('changeSensitive', file, !file.isSensitive); | ||||
| 	}); | ||||
| } | ||||
| 
 | ||||
| async function rename(file) { | ||||
| 	const { canceled, result } = await os.inputText({ | ||||
| 		title: i18n.ts.enterFileName, | ||||
|  |  | |||
|  | @ -136,9 +136,11 @@ async function enter(el: HTMLElement) { | |||
| 
 | ||||
| 	setTimeout(renderTab, 170); | ||||
| } | ||||
| 
 | ||||
| function afterEnter(el: HTMLElement) { | ||||
| 	//el.style.width = ''; | ||||
| } | ||||
| 
 | ||||
| async function leave(el: HTMLElement) { | ||||
| 	const elementWidth = el.getBoundingClientRect().width; | ||||
| 	el.style.width = elementWidth + 'px'; | ||||
|  | @ -147,6 +149,7 @@ async function leave(el: HTMLElement) { | |||
| 	el.style.width = '0'; | ||||
| 	el.style.paddingLeft = '0'; | ||||
| } | ||||
| 
 | ||||
| function afterLeave(el: HTMLElement) { | ||||
| 	el.style.width = ''; | ||||
| } | ||||
|  |  | |||
|  | @ -47,7 +47,7 @@ SPDX-License-Identifier: AGPL-3.0-only | |||
| 					</div> | ||||
| 				</FormSection> | ||||
| 				<FormSection> | ||||
| 					<template #label>{{ i18n.ts._aboutMisskey.contributors }}</template> | ||||
| 					<template #label>{{ i18n.ts._aboutMisskey.projectMembers }}</template> | ||||
| 					<div :class="$style.contributors"> | ||||
| 						<a href="https://github.com/syuilo" target="_blank" :class="$style.contributor"> | ||||
| 							<img src="https://avatars.githubusercontent.com/u/4439005?v=4" :class="$style.contributorAvatar"> | ||||
|  | @ -61,20 +61,19 @@ SPDX-License-Identifier: AGPL-3.0-only | |||
| 							<img src="https://avatars.githubusercontent.com/u/20679825?v=4" :class="$style.contributorAvatar"> | ||||
| 							<span :class="$style.contributorUsername">@acid-chicken</span> | ||||
| 						</a> | ||||
| 						<a href="https://github.com/rinsuki" target="_blank" :class="$style.contributor"> | ||||
| 							<img src="https://avatars.githubusercontent.com/u/6533808?v=4" :class="$style.contributorAvatar"> | ||||
| 							<span :class="$style.contributorUsername">@rinsuki</span> | ||||
| 						<a href="https://github.com/kakkokari-gtyih" target="_blank" :class="$style.contributor"> | ||||
| 							<img src="https://avatars.githubusercontent.com/u/67428053?v=4" :class="$style.contributorAvatar"> | ||||
| 							<span :class="$style.contributorUsername">@kakkokari-gtyih</span> | ||||
| 						</a> | ||||
| 						<a href="https://github.com/mei23" target="_blank" :class="$style.contributor"> | ||||
| 							<img src="https://avatars.githubusercontent.com/u/30769358?v=4" :class="$style.contributorAvatar"> | ||||
| 							<span :class="$style.contributorUsername">@mei23</span> | ||||
| 						</a> | ||||
| 						<a href="https://github.com/robflop" target="_blank" :class="$style.contributor"> | ||||
| 							<img src="https://avatars.githubusercontent.com/u/8159402?v=4" :class="$style.contributorAvatar"> | ||||
| 							<span :class="$style.contributorUsername">@robflop</span> | ||||
| 						<a href="https://github.com/taichanNE30" target="_blank" :class="$style.contributor"> | ||||
| 							<img src="https://avatars.githubusercontent.com/u/40626578?v=4" :class="$style.contributorAvatar"> | ||||
| 							<span :class="$style.contributorUsername">@taichanNE30</span> | ||||
| 						</a> | ||||
| 					</div> | ||||
| 					<template #caption><MkLink url="https://github.com/misskey-dev/misskey/graphs/contributors">{{ i18n.ts._aboutMisskey.allContributors }}</MkLink></template> | ||||
| 				</FormSection> | ||||
| 				<FormSection> | ||||
| 					<template #label>{{ i18n.ts._aboutMisskey.contributors }}</template> | ||||
| 					<MkLink url="https://github.com/misskey-dev/misskey/graphs/contributors">{{ i18n.ts._aboutMisskey.allContributors }}</MkLink> | ||||
| 				</FormSection> | ||||
| 				<FormSection> | ||||
| 					<template #label><Mfm text="$[jelly ❤]"/> {{ i18n.ts._aboutMisskey.patrons }}</template> | ||||
|  | @ -95,6 +94,9 @@ SPDX-License-Identifier: AGPL-3.0-only | |||
| 						<div> | ||||
| 							<a style="display: inline-block;" class="masknetwork" title="Mask Network" href="https://mask.io/" target="_blank"><img width="180" src="https://misskey-hub.net/sponsors/masknetwork.png" alt="Mask Network"></a> | ||||
| 						</div> | ||||
| 						<div> | ||||
| 							<a style="display: inline-block;" class="xserver" title="XServer" href="https://www.xserver.ne.jp/" target="_blank"><img width="180" src="https://misskey-hub.net/sponsors/xserver.png" alt="XServer"></a> | ||||
| 						</div> | ||||
| 						<div> | ||||
| 							<a style="display: inline-block;" class="skeb" title="Skeb" href="https://skeb.jp/" target="_blank"><img width="180" src="https://misskey-hub.net/sponsors/skeb.svg" alt="Skeb"></a> | ||||
| 						</div> | ||||
|  |  | |||
|  | @ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only | |||
| <template> | ||||
| <MkStickyContainer> | ||||
| 	<template #header> | ||||
| 		<XHeader :actions="headerActions" :tabs="headerTabs" /> | ||||
| 		<XHeader :actions="headerActions" :tabs="headerTabs"/> | ||||
| 	</template> | ||||
| 	<MkSpacer :contentMax="900"> | ||||
| 		<MkSwitch :modelValue="publishing" @update:modelValue="onChangePublishing"> | ||||
|  | @ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only | |||
| 		</MkSwitch> | ||||
| 		<div> | ||||
| 			<div v-for="ad in ads" class="_panel _gaps_m" :class="$style.ad"> | ||||
| 				<MkAd v-if="ad.url" :specify="ad" /> | ||||
| 				<MkAd v-if="ad.url" :specify="ad"/> | ||||
| 				<MkInput v-model="ad.url" type="url"> | ||||
| 					<template #label>URL</template> | ||||
| 				</MkInput> | ||||
|  | @ -51,8 +51,10 @@ SPDX-License-Identifier: AGPL-3.0-only | |||
| 					<span> | ||||
| 						{{ i18n.ts._ad.timezoneinfo }} | ||||
| 						<div v-for="(day, index) in daysOfWeek" :key="index"> | ||||
| 							<input :id="`ad${ad.id}-${index}`" type="checkbox" :checked="(ad.dayOfWeek & (1 << index)) !== 0" | ||||
| 								@change="toggleDayOfWeek(ad, index)"> | ||||
| 							<input | ||||
| 								:id="`ad${ad.id}-${index}`" type="checkbox" :checked="(ad.dayOfWeek & (1 << index)) !== 0" | ||||
| 								@change="toggleDayOfWeek(ad, index)" | ||||
| 							> | ||||
| 							<label :for="`ad${ad.id}-${index}`">{{ day }}</label> | ||||
| 						</div> | ||||
| 					</span> | ||||
|  | @ -61,9 +63,13 @@ SPDX-License-Identifier: AGPL-3.0-only | |||
| 					<template #label>{{ i18n.ts.memo }}</template> | ||||
| 				</MkTextarea> | ||||
| 				<div class="buttons"> | ||||
| 					<MkButton class="button" inline primary style="margin-right: 12px;" @click="save(ad)"><i | ||||
| 							class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton> | ||||
| 					<MkButton class="button" inline danger @click="remove(ad)"><i class="ti ti-trash"></i> {{ i18n.ts.remove }} | ||||
| 					<MkButton class="button" inline primary style="margin-right: 12px;" @click="save(ad)"> | ||||
| 						<i | ||||
| 							class="ti ti-device-floppy" | ||||
| 						></i> {{ i18n.ts.save }} | ||||
| 					</MkButton> | ||||
| 					<MkButton class="button" inline danger @click="remove(ad)"> | ||||
| 						<i class="ti ti-trash"></i> {{ i18n.ts.remove }} | ||||
| 					</MkButton> | ||||
| 				</div> | ||||
| 			</div> | ||||
|  | @ -115,6 +121,7 @@ const onChangePublishing = (v) => { | |||
| 	publishing = v; | ||||
| 	refresh(); | ||||
| }; | ||||
| 
 | ||||
| // 選択された曜日(index)のビットフラグを操作する | ||||
| function toggleDayOfWeek(ad, index) { | ||||
| 	ad.dayOfWeek ^= 1 << index; | ||||
|  | @ -187,6 +194,7 @@ function save(ad) { | |||
| 		}); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| function more() { | ||||
| 	os.api('admin/ad/list', { untilId: ads.reduce((acc, ad) => ad.id != null ? ad : acc).id, publishing: publishing }).then(adsResponse => { | ||||
| 		ads = ads.concat(adsResponse.map(r => { | ||||
|  |  | |||
|  | @ -154,12 +154,9 @@ function save() { | |||
| 
 | ||||
| 	if (props.channelId) { | ||||
| 		params.channelId = props.channelId; | ||||
| 		os.api('channels/update', params).then(() => { | ||||
| 			os.success(); | ||||
| 		}); | ||||
| 		os.apiWithDialog('channels/update', params); | ||||
| 	} else { | ||||
| 		os.api('channels/create', params).then(created => { | ||||
| 			os.success(); | ||||
| 		os.apiWithDialog('channels/create', params).then(created => { | ||||
| 			router.push(`/channels/${created.id}`); | ||||
| 		}); | ||||
| 	} | ||||
|  |  | |||
|  | @ -36,7 +36,7 @@ SPDX-License-Identifier: AGPL-3.0-only | |||
| 					<template #icon><i class="ti ti-code"></i></template> | ||||
| 					<template #label>{{ i18n.ts._play.viewSource }}</template> | ||||
| 
 | ||||
| 					<MkCode :code="flash.script" :inline="false" class="_monospace"/> | ||||
| 					<MkCode :code="flash.script" lang="is" :inline="false" class="_monospace"/> | ||||
| 				</MkFolder> | ||||
| 				<div :class="$style.footer"> | ||||
| 					<Mfm :text="`By @${flash.user.username}`"/> | ||||
|  |  | |||
|  | @ -36,8 +36,8 @@ SPDX-License-Identifier: AGPL-3.0-only | |||
| 				<div class="_gaps_s"> | ||||
| 					<MkSwitch v-model="suspended" :disabled="!instance" @update:modelValue="toggleSuspend">{{ i18n.ts.stopActivityDelivery }}</MkSwitch> | ||||
| 					<MkSwitch v-model="isBlocked" :disabled="!meta || !instance" @update:modelValue="toggleBlock">{{ i18n.ts.blockThisInstance }}</MkSwitch> | ||||
|                     <MkSwitch v-model="isSilenced" :disabled="!meta || !instance" @update:modelValue="toggleSilenced">{{ i18n.ts.silenceThisInstance }}</MkSwitch> | ||||
|                     <MkButton @click="refreshMetadata"><i class="ti ti-refresh"></i> Refresh metadata</MkButton> | ||||
| 					<MkSwitch v-model="isSilenced" :disabled="!meta || !instance" @update:modelValue="toggleSilenced">{{ i18n.ts.silenceThisInstance }}</MkSwitch> | ||||
| 					<MkButton @click="refreshMetadata"><i class="ti ti-refresh"></i> Refresh metadata</MkButton> | ||||
| 				</div> | ||||
| 			</FormSection> | ||||
| 
 | ||||
|  | @ -171,8 +171,8 @@ async function fetch(): Promise<void> { | |||
| 	}); | ||||
| 	suspended = instance.isSuspended; | ||||
| 	isBlocked = instance.isBlocked; | ||||
|     isSilenced = instance.isSilenced; | ||||
|     faviconUrl = getProxiedImageUrlNullable(instance.faviconUrl, 'preview') ?? getProxiedImageUrlNullable(instance.iconUrl, 'preview'); | ||||
| 	isSilenced = instance.isSilenced; | ||||
| 	faviconUrl = getProxiedImageUrlNullable(instance.faviconUrl, 'preview') ?? getProxiedImageUrlNullable(instance.iconUrl, 'preview'); | ||||
| } | ||||
| 
 | ||||
| async function toggleBlock(): Promise<void> { | ||||
|  | @ -183,14 +183,16 @@ async function toggleBlock(): Promise<void> { | |||
| 		blockedHosts: isBlocked ? meta.blockedHosts.concat([host]) : meta.blockedHosts.filter(x => x !== host), | ||||
| 	}); | ||||
| } | ||||
| 
 | ||||
| async function toggleSilenced(): Promise<void> { | ||||
|     if (!meta) throw new Error('No meta?'); | ||||
|     if (!instance) throw new Error('No instance?'); | ||||
|     const { host } = instance; | ||||
|     await os.api('admin/update-meta', { | ||||
|         silencedHosts: isSilenced ? meta.silencedHosts.concat([host]) : meta.silencedHosts.filter(x => x !== host), | ||||
|     }); | ||||
| 	if (!meta) throw new Error('No meta?'); | ||||
| 	if (!instance) throw new Error('No instance?'); | ||||
| 	const { host } = instance; | ||||
| 	await os.api('admin/update-meta', { | ||||
| 		silencedHosts: isSilenced ? meta.silencedHosts.concat([host]) : meta.silencedHosts.filter(x => x !== host), | ||||
| 	}); | ||||
| } | ||||
| 
 | ||||
| async function toggleSuspend(): Promise<void> { | ||||
| 	if (!instance) throw new Error('No instance?'); | ||||
| 	await os.api('admin/federation/update-instance', { | ||||
|  |  | |||
|  | @ -4,46 +4,46 @@ SPDX-License-Identifier: AGPL-3.0-only | |||
| --> | ||||
| 
 | ||||
| <template> | ||||
| <MkSpacer :contentMax="800"> | ||||
| 	<div :class="$style.root"> | ||||
| 		<div :class="$style.editor" class="_panel"> | ||||
| 			<PrismEditor v-model="code" class="_monospace" :class="$style.code" :highlight="highlighter" :lineNumbers="false"/> | ||||
| 			<MkButton style="position: absolute; top: 8px; right: 8px;" primary @click="run()"><i class="ti ti-player-play"></i></MkButton> | ||||
| 		</div> | ||||
| <MkStickyContainer> | ||||
| 	<template #header><MkPageHeader/></template> | ||||
| 
 | ||||
| 		<MkContainer v-if="root && components.length > 1" :key="uiKey" :foldable="true"> | ||||
| 			<template #header>UI</template> | ||||
| 			<div :class="$style.ui"> | ||||
| 				<MkAsUi :component="root" :components="components" size="small"/> | ||||
| 	<MkSpacer :contentMax="800"> | ||||
| 		<div :class="$style.root"> | ||||
| 			<div class="_gaps_s"> | ||||
| 				<div :class="$style.editor" class="_panel"> | ||||
| 					<MkCodeEditor v-model="code" lang="aiscript"/> | ||||
| 				</div> | ||||
| 				<MkButton primary @click="run()"><i class="ti ti-player-play"></i></MkButton> | ||||
| 			</div> | ||||
| 		</MkContainer> | ||||
| 
 | ||||
| 		<MkContainer :foldable="true" class=""> | ||||
| 			<template #header>{{ i18n.ts.output }}</template> | ||||
| 			<div :class="$style.logs"> | ||||
| 				<div v-for="log in logs" :key="log.id" class="log" :class="{ print: log.print }">{{ log.text }}</div> | ||||
| 			<MkContainer v-if="root && components.length > 1" :key="uiKey" :foldable="true"> | ||||
| 				<template #header>UI</template> | ||||
| 				<div :class="$style.ui"> | ||||
| 					<MkAsUi :component="root" :components="components" size="small"/> | ||||
| 				</div> | ||||
| 			</MkContainer> | ||||
| 
 | ||||
| 			<MkContainer :foldable="true" class=""> | ||||
| 				<template #header>{{ i18n.ts.output }}</template> | ||||
| 				<div :class="$style.logs"> | ||||
| 					<div v-for="log in logs" :key="log.id" class="log" :class="{ print: log.print }">{{ log.text }}</div> | ||||
| 				</div> | ||||
| 			</MkContainer> | ||||
| 
 | ||||
| 			<div class=""> | ||||
| 				{{ i18n.ts.scratchpadDescription }} | ||||
| 			</div> | ||||
| 		</MkContainer> | ||||
| 
 | ||||
| 		<div class=""> | ||||
| 			{{ i18n.ts.scratchpadDescription }} | ||||
| 		</div> | ||||
| 	</div> | ||||
| </MkSpacer> | ||||
| 	</MkSpacer> | ||||
| </MkStickyContainer> | ||||
| </template> | ||||
| 
 | ||||
| <script lang="ts" setup> | ||||
| import { onDeactivated, onUnmounted, Ref, ref, watch } from 'vue'; | ||||
| import 'prismjs'; | ||||
| import { highlight, languages } from 'prismjs/components/prism-core'; | ||||
| import 'prismjs/components/prism-clike'; | ||||
| import 'prismjs/components/prism-javascript'; | ||||
| import 'prismjs/themes/prism-okaidia.css'; | ||||
| import { PrismEditor } from 'vue-prism-editor'; | ||||
| import 'vue-prism-editor/dist/prismeditor.min.css'; | ||||
| import { Interpreter, Parser, utils } from '@syuilo/aiscript'; | ||||
| import MkContainer from '@/components/MkContainer.vue'; | ||||
| import MkButton from '@/components/MkButton.vue'; | ||||
| import MkCodeEditor from '@/components/MkCodeEditor.vue'; | ||||
| import { createAiScriptEnv } from '@/scripts/aiscript/api.js'; | ||||
| import * as os from '@/os.js'; | ||||
| import { $i } from '@/account.js'; | ||||
|  | @ -152,10 +152,6 @@ async function run() { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| function highlighter(code) { | ||||
| 	return highlight(code, languages.js, 'javascript'); | ||||
| } | ||||
| 
 | ||||
| onDeactivated(() => { | ||||
| 	if (aiscript) aiscript.abort(); | ||||
| }); | ||||
|  |  | |||
|  | @ -368,12 +368,14 @@ const emojiIndexLangs = ['en-US']; | |||
| function downloadEmojiIndex(lang: string) { | ||||
| 	async function main() { | ||||
| 		const currentIndexes = defaultStore.state.additionalUnicodeEmojiIndexes; | ||||
| 
 | ||||
| 		function download() { | ||||
| 			switch (lang) { | ||||
| 				case 'en-US': return import('../../unicode-emoji-indexes/en-US.json').then(x => x.default); | ||||
| 				default: throw new Error('unrecognized lang: ' + lang); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		currentIndexes[lang] = await download(); | ||||
| 		await defaultStore.set('additionalUnicodeEmojiIndexes', currentIndexes); | ||||
| 	} | ||||
|  | @ -410,6 +412,7 @@ function removePinnedList() { | |||
| 
 | ||||
| let smashCount = 0; | ||||
| let smashTimer: number | null = null; | ||||
| 
 | ||||
| function testNotification(): void { | ||||
| 	const notification: Misskey.entities.Notification = { | ||||
| 		id: Math.random().toString(), | ||||
|  |  | |||
|  | @ -50,7 +50,7 @@ SPDX-License-Identifier: AGPL-3.0-only | |||
| 							<MkButton inline @click="copy(plugin)"><i class="ti ti-copy"></i> {{ i18n.ts.copy }}</MkButton> | ||||
| 						</div> | ||||
| 
 | ||||
| 						<MkCode :code="plugin.src ?? ''"/> | ||||
| 						<MkCode :code="plugin.src ?? ''" lang="is"/> | ||||
| 					</div> | ||||
| 				</MkFolder> | ||||
| 			</div> | ||||
|  |  | |||
|  | @ -108,6 +108,7 @@ async function del(): Promise<void> { | |||
| 
 | ||||
| 	router.push('/settings/webhook'); | ||||
| } | ||||
| 
 | ||||
| const headerActions = $computed(() => []); | ||||
| 
 | ||||
| const headerTabs = $computed(() => []); | ||||
|  |  | |||
|  | @ -0,0 +1,31 @@ | |||
| import { setWasm, setCDN, Highlighter, getHighlighter as _getHighlighter } from 'shiki'; | ||||
| 
 | ||||
| setWasm('/assets/shiki/dist/onig.wasm'); | ||||
| setCDN('/assets/shiki/'); | ||||
| 
 | ||||
| let _highlighter: Highlighter | null = null; | ||||
| 
 | ||||
| export async function getHighlighter(): Promise<Highlighter> { | ||||
| 	if (!_highlighter) { | ||||
| 		return await initHighlighter(); | ||||
| 	} | ||||
| 	return _highlighter; | ||||
| } | ||||
| 
 | ||||
| export async function initHighlighter() { | ||||
| 	const highlighter = await _getHighlighter({ | ||||
| 		theme: 'dark-plus', | ||||
| 		langs: ['js'], | ||||
| 	}); | ||||
| 
 | ||||
| 	await highlighter.loadLanguage({ | ||||
| 		path: 'languages/aiscript.tmLanguage.json', | ||||
| 		id: 'aiscript', | ||||
| 		scopeName: 'source.aiscript', | ||||
| 		aliases: ['is', 'ais'], | ||||
| 	}); | ||||
| 
 | ||||
| 	_highlighter = highlighter; | ||||
| 
 | ||||
| 	return highlighter; | ||||
| } | ||||
|  | @ -56,6 +56,7 @@ function copyUrl(file: Misskey.entities.DriveFile) { | |||
| 	copyToClipboard(file.url); | ||||
| 	os.success(); | ||||
| } | ||||
| 
 | ||||
| /* | ||||
| function addApp() { | ||||
| 	alert('not implemented yet'); | ||||
|  |  | |||
|  | @ -114,6 +114,12 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: Router | |||
| 		return !confirm.canceled; | ||||
| 	} | ||||
| 
 | ||||
| 	async function userInfoUpdate() { | ||||
| 		os.apiWithDialog('federation/update-remote-user', { | ||||
| 			userId: user.id, | ||||
| 		}); | ||||
| 	} | ||||
| 	 | ||||
| 	async function invalidateFollow() { | ||||
| 		if (!await getConfirmed(i18n.ts.breakFollowConfirm)) return; | ||||
| 
 | ||||
|  | @ -330,6 +336,14 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: Router | |||
| 		}]); | ||||
| 	} | ||||
| 
 | ||||
| 	if (user.host !== null) { | ||||
| 		menu = menu.concat([null, { | ||||
| 			icon: 'ti ti-refresh', | ||||
| 			text: i18n.ts.updateRemoteUser, | ||||
| 			action: userInfoUpdate, | ||||
| 		}]); | ||||
| 	} | ||||
| 	 | ||||
| 	if (defaultStore.state.devMode) { | ||||
| 		menu = menu.concat([null, { | ||||
| 			icon: 'ti ti-id', | ||||
|  |  | |||
|  | @ -46,6 +46,7 @@ export function onScrollTop(el: HTMLElement, cb: () => unknown, tolerance = 1, o | |||
| 	}; | ||||
| 
 | ||||
| 	function removeListener() { container.removeEventListener('scroll', onScroll); } | ||||
| 
 | ||||
| 	container.addEventListener('scroll', onScroll, { passive: true }); | ||||
| 	return removeListener; | ||||
| } | ||||
|  | @ -71,6 +72,7 @@ export function onScrollBottom(el: HTMLElement, cb: () => unknown, tolerance = 1 | |||
| 	function removeListener() { | ||||
| 		containerOrWindow.removeEventListener('scroll', onScroll); | ||||
| 	} | ||||
| 
 | ||||
| 	containerOrWindow.addEventListener('scroll', onScroll, { passive: true }); | ||||
| 	return removeListener; | ||||
| } | ||||
|  |  | |||
|  | @ -408,10 +408,6 @@ hr { | |||
| 	font-family: Fira code, Fira Mono, Consolas, Menlo, Courier, monospace !important; | ||||
| } | ||||
| 
 | ||||
| .prism-editor__textarea:focus { | ||||
| 	outline: none; | ||||
| } | ||||
| 
 | ||||
| ._zoom { | ||||
| 	transition-duration: 0.5s, 0.5s; | ||||
| 	transition-property: opacity, transform; | ||||
|  |  | |||
|  | @ -67,6 +67,11 @@ module.exports = { | |||
| 		'object-curly-spacing': ['error', 'always'], | ||||
| 		'space-infix-ops': ['error'], | ||||
| 		'space-before-blocks': ['error', 'always'], | ||||
| 		'padding-line-between-statements': [ | ||||
| 			'error', | ||||
| 			{ 'blankLine': 'always', 'prev': 'function', 'next': '*' }, | ||||
| 			{ 'blankLine': 'always', 'prev': '*', 'next': 'function' }, | ||||
| 		], | ||||
| 		'@typescript-eslint/func-call-spacing': ['error', 'never'], | ||||
| 		'@typescript-eslint/no-explicit-any': ['warn'], | ||||
| 		'@typescript-eslint/no-unused-vars': ['warn'], | ||||
|  |  | |||
|  | @ -673,6 +673,9 @@ importers: | |||
|       '@vue/compiler-sfc': | ||||
|         specifier: 3.3.7 | ||||
|         version: 3.3.7 | ||||
|       aiscript-vscode: | ||||
|         specifier: github:aiscript-dev/aiscript-vscode#v0.0.5 | ||||
|         version: github.com/aiscript-dev/aiscript-vscode/a8fa5bb41885391cdb6a6e3165eaa6e4868da86e | ||||
|       astring: | ||||
|         specifier: 1.8.6 | ||||
|         version: 1.8.6 | ||||
|  | @ -754,9 +757,6 @@ importers: | |||
|       photoswipe: | ||||
|         specifier: 5.4.2 | ||||
|         version: 5.4.2 | ||||
|       prismjs: | ||||
|         specifier: 1.29.0 | ||||
|         version: 1.29.0 | ||||
|       punycode: | ||||
|         specifier: 2.3.0 | ||||
|         version: 2.3.0 | ||||
|  | @ -772,6 +772,9 @@ importers: | |||
|       sass: | ||||
|         specifier: 1.69.5 | ||||
|         version: 1.69.5 | ||||
|       shiki: | ||||
|         specifier: ^0.14.5 | ||||
|         version: 0.14.5 | ||||
|       strict-event-emitter-types: | ||||
|         specifier: 2.0.0 | ||||
|         version: 2.0.0 | ||||
|  | @ -814,9 +817,6 @@ importers: | |||
|       vue: | ||||
|         specifier: 3.3.7 | ||||
|         version: 3.3.7(typescript@5.2.2) | ||||
|       vue-prism-editor: | ||||
|         specifier: 2.0.0-alpha.2 | ||||
|         version: 2.0.0-alpha.2(vue@3.3.7) | ||||
|       vuedraggable: | ||||
|         specifier: next | ||||
|         version: 4.1.0(vue@3.3.7) | ||||
|  | @ -871,10 +871,10 @@ importers: | |||
|         version: 7.5.1 | ||||
|       '@storybook/vue3': | ||||
|         specifier: 7.5.1 | ||||
|         version: 7.5.1(@vue/compiler-core@3.3.6)(vue@3.3.7) | ||||
|         version: 7.5.1(@vue/compiler-core@3.3.7)(vue@3.3.7) | ||||
|       '@storybook/vue3-vite': | ||||
|         specifier: 7.5.1 | ||||
|         version: 7.5.1(@vue/compiler-core@3.3.6)(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2)(vite@4.5.0)(vue@3.3.7) | ||||
|         version: 7.5.1(@vue/compiler-core@3.3.7)(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2)(vite@4.5.0)(vue@3.3.7) | ||||
|       '@testing-library/vue': | ||||
|         specifier: 7.0.0 | ||||
|         version: 7.0.0(@vue/compiler-sfc@3.3.7)(vue@3.3.7) | ||||
|  | @ -6867,7 +6867,7 @@ packages: | |||
|       file-system-cache: 2.3.0 | ||||
|     dev: true | ||||
| 
 | ||||
|   /@storybook/vue3-vite@7.5.1(@vue/compiler-core@3.3.6)(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2)(vite@4.5.0)(vue@3.3.7): | ||||
|   /@storybook/vue3-vite@7.5.1(@vue/compiler-core@3.3.7)(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2)(vite@4.5.0)(vue@3.3.7): | ||||
|     resolution: {integrity: sha512-5bO5BactTbyOxxeRw8U6t3FqqfTvVLTefzg1NLDkKt2iAL6lGBSsPTKMgpy3dt+cxdiqEis67niQL68ZtW02Zw==} | ||||
|     engines: {node: ^14.18 || >=16} | ||||
|     peerDependencies: | ||||
|  | @ -6877,7 +6877,7 @@ packages: | |||
|     dependencies: | ||||
|       '@storybook/builder-vite': 7.5.1(typescript@5.2.2)(vite@4.5.0) | ||||
|       '@storybook/core-server': 7.5.1 | ||||
|       '@storybook/vue3': 7.5.1(@vue/compiler-core@3.3.6)(vue@3.3.7) | ||||
|       '@storybook/vue3': 7.5.1(@vue/compiler-core@3.3.7)(vue@3.3.7) | ||||
|       '@vitejs/plugin-vue': 4.4.0(vite@4.5.0)(vue@3.3.7) | ||||
|       magic-string: 0.30.3 | ||||
|       react: 18.2.0 | ||||
|  | @ -6896,7 +6896,7 @@ packages: | |||
|       - vue | ||||
|     dev: true | ||||
| 
 | ||||
|   /@storybook/vue3@7.5.1(@vue/compiler-core@3.3.6)(vue@3.3.7): | ||||
|   /@storybook/vue3@7.5.1(@vue/compiler-core@3.3.7)(vue@3.3.7): | ||||
|     resolution: {integrity: sha512-9srw2rnSYaU45kkunXT8+bX3QMO2QPV6MCWRayKo7Pl+B0H/euHvxPSZb1X8mRpgLtYgVgSNJFoNbk/2Fn8z8g==} | ||||
|     engines: {node: '>=16.0.0'} | ||||
|     peerDependencies: | ||||
|  | @ -6908,7 +6908,7 @@ packages: | |||
|       '@storybook/global': 5.0.0 | ||||
|       '@storybook/preview-api': 7.5.1 | ||||
|       '@storybook/types': 7.5.1 | ||||
|       '@vue/compiler-core': 3.3.6 | ||||
|       '@vue/compiler-core': 3.3.7 | ||||
|       lodash: 4.17.21 | ||||
|       ts-dedent: 2.2.0 | ||||
|       type-fest: 2.19.0 | ||||
|  | @ -8367,15 +8367,6 @@ packages: | |||
|       postcss: 8.4.31 | ||||
|       source-map-js: 1.0.2 | ||||
| 
 | ||||
|   /@vue/compiler-ssr@3.3.6: | ||||
|     resolution: {integrity: sha512-QTIHAfDCHhjXlYGkUg5KH7YwYtdUM1vcFl/FxFDlD6d0nXAmnjizka3HITp8DGudzHndv2PjKVS44vqqy0vP4w==} | ||||
|     requiresBuild: true | ||||
|     dependencies: | ||||
|       '@vue/compiler-dom': 3.3.6 | ||||
|       '@vue/shared': 3.3.6 | ||||
|     dev: true | ||||
|     optional: true | ||||
| 
 | ||||
|   /@vue/compiler-ssr@3.3.7: | ||||
|     resolution: {integrity: sha512-TxOfNVVeH3zgBc82kcUv+emNHo+vKnlRrkv8YvQU5+Y5LJGJwSNzcmLUoxD/dNzv0bhQ/F0s+InlgV0NrApJZg==} | ||||
|     dependencies: | ||||
|  | @ -8428,17 +8419,6 @@ packages: | |||
|       '@vue/shared': 3.3.7 | ||||
|       csstype: 3.1.2 | ||||
| 
 | ||||
|   /@vue/server-renderer@3.3.6(vue@3.3.7): | ||||
|     resolution: {integrity: sha512-kgLoN43W4ERdZ6dpyy+gnk2ZHtcOaIr5Uc/WUP5DRwutgvluzu2pudsZGoD2b7AEJHByUVMa9k6Sho5lLRCykw==} | ||||
|     peerDependencies: | ||||
|       vue: 3.3.6 | ||||
|     dependencies: | ||||
|       '@vue/compiler-ssr': 3.3.6 | ||||
|       '@vue/shared': 3.3.6 | ||||
|       vue: 3.3.7(typescript@5.2.2) | ||||
|     dev: true | ||||
|     optional: true | ||||
| 
 | ||||
|   /@vue/server-renderer@3.3.7(vue@3.3.7): | ||||
|     resolution: {integrity: sha512-UlpKDInd1hIZiNuVVVvLgxpfnSouxKQOSE2bOfQpBuGwxRV/JqqTCyyjXUWiwtVMyeRaZhOYYqntxElk8FhBhw==} | ||||
|     peerDependencies: | ||||
|  | @ -8466,8 +8446,8 @@ packages: | |||
|       js-beautify: 1.14.6 | ||||
|       vue: 3.3.7(typescript@5.2.2) | ||||
|     optionalDependencies: | ||||
|       '@vue/compiler-dom': 3.3.6 | ||||
|       '@vue/server-renderer': 3.3.6(vue@3.3.7) | ||||
|       '@vue/compiler-dom': 3.3.7 | ||||
|       '@vue/server-renderer': 3.3.7(vue@3.3.7) | ||||
|     dev: true | ||||
| 
 | ||||
|   /@webgpu/types@0.1.30: | ||||
|  | @ -8687,6 +8667,10 @@ packages: | |||
|     resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} | ||||
|     engines: {node: '>=12'} | ||||
| 
 | ||||
|   /ansi-sequence-parser@1.1.1: | ||||
|     resolution: {integrity: sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg==} | ||||
|     dev: false | ||||
| 
 | ||||
|   /ansi-styles@3.2.1: | ||||
|     resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} | ||||
|     engines: {node: '>=4'} | ||||
|  | @ -13942,7 +13926,6 @@ packages: | |||
| 
 | ||||
|   /jsonc-parser@3.2.0: | ||||
|     resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==} | ||||
|     dev: true | ||||
| 
 | ||||
|   /jsonfile@4.0.0: | ||||
|     resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} | ||||
|  | @ -16251,6 +16234,7 @@ packages: | |||
|   /prismjs@1.29.0: | ||||
|     resolution: {integrity: sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==} | ||||
|     engines: {node: '>=6'} | ||||
|     dev: true | ||||
| 
 | ||||
|   /private-ip@2.3.3: | ||||
|     resolution: {integrity: sha512-5zyFfekIVUOTVbL92hc8LJOtE/gyGHeREHkJ2yTyByP8Q2YZVoBqLg3EfYLeF0oVvGqtaEX2t2Qovja0/gStXw==} | ||||
|  | @ -17480,6 +17464,15 @@ packages: | |||
|     resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} | ||||
|     engines: {node: '>=8'} | ||||
| 
 | ||||
|   /shiki@0.14.5: | ||||
|     resolution: {integrity: sha512-1gCAYOcmCFONmErGTrS1fjzJLA7MGZmKzrBNX7apqSwhyITJg2O102uFzXUeBxNnEkDA9vHIKLyeKq0V083vIw==} | ||||
|     dependencies: | ||||
|       ansi-sequence-parser: 1.1.1 | ||||
|       jsonc-parser: 3.2.0 | ||||
|       vscode-oniguruma: 1.7.0 | ||||
|       vscode-textmate: 8.0.0 | ||||
|     dev: false | ||||
| 
 | ||||
|   /side-channel@1.0.4: | ||||
|     resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} | ||||
|     dependencies: | ||||
|  | @ -19232,6 +19225,14 @@ packages: | |||
|     resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==} | ||||
|     engines: {node: '>=0.10.0'} | ||||
| 
 | ||||
|   /vscode-oniguruma@1.7.0: | ||||
|     resolution: {integrity: sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==} | ||||
|     dev: false | ||||
| 
 | ||||
|   /vscode-textmate@8.0.0: | ||||
|     resolution: {integrity: sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==} | ||||
|     dev: false | ||||
| 
 | ||||
|   /vue-component-type-helpers@1.8.22: | ||||
|     resolution: {integrity: sha512-LK3wJHs3vJxHG292C8cnsRusgyC5SEZDCzDCD01mdE/AoREFMl2tzLRuzwyuEsOIz13tqgBcnvysN3Lxsa14Fw==} | ||||
|     dev: true | ||||
|  | @ -19295,15 +19296,6 @@ packages: | |||
|       vue: 3.3.7(typescript@5.2.2) | ||||
|     dev: true | ||||
| 
 | ||||
|   /vue-prism-editor@2.0.0-alpha.2(vue@3.3.7): | ||||
|     resolution: {integrity: sha512-Gu42ba9nosrE+gJpnAEuEkDMqG9zSUysIR8SdXUw8MQKDjBnnNR9lHC18uOr/ICz7yrA/5c7jHJr9lpElODC7w==} | ||||
|     engines: {node: '>=10'} | ||||
|     peerDependencies: | ||||
|       vue: ^3.0.0 | ||||
|     dependencies: | ||||
|       vue: 3.3.7(typescript@5.2.2) | ||||
|     dev: false | ||||
| 
 | ||||
|   /vue-template-compiler@2.7.14: | ||||
|     resolution: {integrity: sha512-zyA5Y3ArvVG0NacJDkkzJuPQDF8RFeRlzV2vLeSnhSpieO6LK2OVbdLPi5MPPs09Ii+gMO8nY4S3iKQxBxDmWQ==} | ||||
|     dependencies: | ||||
|  | @ -19765,6 +19757,13 @@ packages: | |||
|       readable-stream: 3.6.0 | ||||
|     dev: false | ||||
| 
 | ||||
|   github.com/aiscript-dev/aiscript-vscode/a8fa5bb41885391cdb6a6e3165eaa6e4868da86e: | ||||
|     resolution: {tarball: https://codeload.github.com/aiscript-dev/aiscript-vscode/tar.gz/a8fa5bb41885391cdb6a6e3165eaa6e4868da86e} | ||||
|     name: aiscript-vscode | ||||
|     version: 0.0.5 | ||||
|     engines: {vscode: ^1.83.0} | ||||
|     dev: false | ||||
| 
 | ||||
|   github.com/misskey-dev/browser-image-resizer/0227e860621e55cbed0aabe6dc601096a7748c4a: | ||||
|     resolution: {tarball: https://codeload.github.com/misskey-dev/browser-image-resizer/tar.gz/0227e860621e55cbed0aabe6dc601096a7748c4a} | ||||
|     name: browser-image-resizer | ||||
|  |  | |||
|  | @ -33,6 +33,13 @@ async function copyFrontendLocales() { | |||
|   } | ||||
| } | ||||
| 
 | ||||
| async function copyFrontendShikiAssets() { | ||||
|   await fs.cp('./packages/frontend/node_modules/shiki/dist', './built/_frontend_dist_/shiki/dist', { dereference: true, recursive: true }); | ||||
|   await fs.cp('./packages/frontend/node_modules/shiki/languages', './built/_frontend_dist_/shiki/languages', { dereference: true, recursive: true }); | ||||
|   await fs.cp('./packages/frontend/node_modules/aiscript-vscode/aiscript/syntaxes', './built/_frontend_dist_/shiki/languages', { dereference: true, recursive: true }); | ||||
|   await fs.cp('./packages/frontend/node_modules/shiki/themes', './built/_frontend_dist_/shiki/themes', { dereference: true, recursive: true }); | ||||
| } | ||||
| 
 | ||||
| async function copyBackendViews() { | ||||
|   await fs.cp('./packages/backend/src/server/web/views', './packages/backend/built/server/web/views', { recursive: true }); | ||||
| } | ||||
|  | @ -72,6 +79,7 @@ async function build() { | |||
|     copyFrontendFonts(), | ||||
|     copyFrontendTablerIcons(), | ||||
|     copyFrontendLocales(), | ||||
|     copyFrontendShikiAssets(), | ||||
|     copyBackendViews(), | ||||
|     buildBackendScript(), | ||||
|     buildBackendStyle(), | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue