diff --git a/packages/frontend/src/components/MkAutocomplete.vue b/packages/frontend/src/components/MkAutocomplete.vue
index 07747d3e5c3f..ea8005439def 100644
--- a/packages/frontend/src/components/MkAutocomplete.vue
+++ b/packages/frontend/src/components/MkAutocomplete.vue
@@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ hashtag }}
-
+
-
@@ -67,10 +67,16 @@ export type CompleteInfo = {
payload: string;
query: string;
},
+ // `:emo` -> `:emoji:` or some unicode emoji
emoji: {
payload: string;
query: string;
},
+ // like emoji but for `:emoji:` -> unicode emoji
+ emojiComplete: {
+ payload: string;
+ query: string;
+ },
mfmTag: {
payload: string;
query: string;
@@ -98,8 +104,7 @@ type EmojiDef = {
const lib = emojilist.filter(x => x.category !== 'flags');
-const emojiDb = computed(() => {
- //#region Unicode Emoji
+const unicodeEmojiDB = computed(() => {
const char2path = defaultStore.reactiveState.emojiStyle.value === 'twemoji' ? char2twemojiFilePath : char2fluentEmojiFilePath;
const unicodeEmojiDB: EmojiDef[] = lib.map(x => ({
@@ -122,6 +127,12 @@ const emojiDb = computed(() => {
}
unicodeEmojiDB.sort((a, b) => a.name.length - b.name.length);
+
+ return unicodeEmojiDB;
+});
+
+const emojiDb = computed(() => {
+ //#region Unicode Emoji
//#endregion
//#region Custom Emoji
@@ -149,7 +160,7 @@ const emojiDb = computed(() => {
customEmojiDB.sort((a, b) => a.name.length - b.name.length);
//#endregion
- return markRaw([...customEmojiDB, ...unicodeEmojiDB]);
+ return markRaw([...customEmojiDB, ...unicodeEmojiDB.value]);
});
export default {
@@ -171,7 +182,7 @@ type PropsType = {
//const props = defineProps>();
// ↑と同じだけど↓にしないとdiscriminated unionにならない。
// https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#discriminated-unions
-const props = defineProps | PropsType<'hashtag'> | PropsType<'emoji'> | PropsType<'mfmTag'> | PropsType<'mfmParam'>>();
+const props = defineProps | PropsType<'hashtag'> | PropsType<'emoji'> | PropsType<'emojiComplete'> | PropsType<'mfmTag'> | PropsType<'mfmParam'>>();
const emit = defineEmits<{
(event: 'done', value: { type: T; value: CompleteInfo[T]['payload'] }): void;
@@ -194,7 +205,7 @@ const zIndex = os.claimZIndex('high');
function complete(type: T, value: CompleteInfo[T]['payload']) {
emit('done', { type, value });
emit('closed');
- if (type === 'emoji') {
+ if (type === 'emoji' || type === 'emojiComplete') {
let recents = defaultStore.state.recentlyUsedEmojis;
recents = recents.filter((emoji: any) => emoji !== value);
recents.unshift(value);
@@ -281,6 +292,14 @@ function exec() {
}
emojis.value = emojiAutoComplete(props.q, emojiDb.value);
+ } else if (props.type === 'emojiComplete') {
+ if (!props.q || props.q === '') {
+ // 最近使った絵文字をサジェスト
+ emojis.value = defaultStore.state.recentlyUsedEmojis.map(emoji => unicodeEmojiDB.value.find(dbEmoji => dbEmoji.emoji === emoji)).filter(x => x) as EmojiDef[];
+ return;
+ }
+
+ emojis.value = emojiAutoComplete(props.q, unicodeEmojiDB.value);
} else if (props.type === 'mfmTag') {
if (!props.q || props.q === '') {
mfmTags.value = MFM_TAGS;
diff --git a/packages/frontend/src/scripts/autocomplete.ts b/packages/frontend/src/scripts/autocomplete.ts
index 3a4160a3d7ba..577f8c6ecd9c 100644
--- a/packages/frontend/src/scripts/autocomplete.ts
+++ b/packages/frontend/src/scripts/autocomplete.ts
@@ -99,6 +99,8 @@ export class Autocomplete {
const isMfmParam = mfmParamIndex !== -1 && text.split(/\$\[[a-zA-Z]+/).pop()?.includes('.');
const isMfmTag = mfmTagIndex !== -1 && !isMfmParam;
const isEmoji = emojiIndex !== -1 && text.split(/:[a-z0-9_+\-]+:/).pop()!.includes(':');
+ // :ok:などを🆗にするたいおぷ
+ const isEmojiCompleteToUnicode = !isEmoji && emojiIndex === text.length - 1;
let opened = false;
@@ -129,6 +131,14 @@ export class Autocomplete {
}
}
+ if (isEmojiCompleteToUnicode && !opened && this.onlyType.includes('emoji')) {
+ const emoji = text.substring(text.lastIndexOf(':', text.length - 2) + 1, text.length - 1);
+ if (!emoji.includes(' ')) {
+ this.open('emojiComplete', emoji);
+ opened = true;
+ }
+ }
+
if (isMfmTag && !opened && this.onlyType.includes('mfmTag')) {
const mfmTag = text.substring(mfmTagIndex + 1);
if (!mfmTag.includes(' ')) {
@@ -272,6 +282,22 @@ export class Autocomplete {
// 挿入
this.text = trimmedBefore + value + after;
+ // キャレットを戻す
+ nextTick(() => {
+ this.textarea.focus();
+ const pos = trimmedBefore.length + value.length;
+ this.textarea.setSelectionRange(pos, pos);
+ });
+ } else if (type === 'emojiComplete') {
+ const source = this.text;
+
+ const before = source.substring(0, caret);
+ const trimmedBefore = before.substring(0, before.lastIndexOf(':', before.length - 2));
+ const after = source.substring(caret);
+
+ // 挿入
+ this.text = trimmedBefore + value + after;
+
// キャレットを戻す
nextTick(() => {
this.textarea.focus();