diff --git a/locales/en-US.yml b/locales/en-US.yml
index b693f791bb..d448686187 100644
--- a/locales/en-US.yml
+++ b/locales/en-US.yml
@@ -307,6 +307,7 @@ folderName: "Folder name"
createFolder: "Create a folder"
renameFolder: "Rename this folder"
deleteFolder: "Delete this folder"
+folder: "Folder"
addFile: "Add a file"
emptyDrive: "Your Drive is empty"
emptyFolder: "This folder is empty"
diff --git a/locales/index.d.ts b/locales/index.d.ts
index c41f40bbcf..b0b8a256f2 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -310,6 +310,7 @@ export interface Locale {
"createFolder": string;
"renameFolder": string;
"deleteFolder": string;
+ "folder": string;
"addFile": string;
"emptyDrive": string;
"emptyFolder": string;
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 3464b1be40..979022e33a 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -307,6 +307,7 @@ folderName: "フォルダー名"
createFolder: "フォルダーを作成"
renameFolder: "フォルダー名を変更"
deleteFolder: "フォルダーを削除"
+folder: "フォルダー"
addFile: "ファイルを追加"
emptyDrive: "ドライブは空です"
emptyFolder: "フォルダーは空です"
diff --git a/packages/frontend/src/components/MkEmojiPicker.section.vue b/packages/frontend/src/components/MkEmojiPicker.section.vue
index 4c8c0c7902..fda847bef9 100644
--- a/packages/frontend/src/components/MkEmojiPicker.section.vue
+++ b/packages/frontend/src/components/MkEmojiPicker.section.vue
@@ -5,9 +5,10 @@ SPDX-License-Identifier: AGPL-3.0-only
-
+
+
- ({{ emojis.length }})
+ (:{{ emojis.length }})
+
+
+
+ (:{{ customEmojiTree.length }} :{{ emojis.length }})
+
+
+
+
+
+
+ {{ child.value || i18n.ts.other }}
+
+
+
diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue
index 169d0cb226..9d96ed3de6 100644
--- a/packages/frontend/src/components/MkEmojiPicker.vue
+++ b/packages/frontend/src/components/MkEmojiPicker.vue
@@ -73,18 +73,20 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ i18n.ts.customEmojis }}
- {{ category || i18n.ts.other }}
+ {{ child.value || i18n.ts.other }}
- {{ category }}
+ {{ category }}
@@ -100,7 +102,14 @@ SPDX-License-Identifier: AGPL-3.0-only
import { ref, shallowRef, computed, watch, onMounted } from 'vue';
import * as Misskey from 'misskey-js';
import XSection from '@/components/MkEmojiPicker.section.vue';
-import { emojilist, emojiCharByCategory, UnicodeEmojiDef, unicodeEmojiCategories as categories, getEmojiName } from '@/scripts/emojilist';
+import {
+ emojilist,
+ emojiCharByCategory,
+ UnicodeEmojiDef,
+ unicodeEmojiCategories as categories,
+ getEmojiName,
+ CustomEmojiFolderTree
+} from '@/scripts/emojilist.js';
import MkRippleEffect from '@/components/MkRippleEffect.vue';
import * as os from '@/os';
import { isTouchUsing } from '@/scripts/touch';
@@ -144,6 +153,34 @@ const searchResultCustom = ref([]);
const searchResultUnicode = ref([]);
const tab = ref<'index' | 'custom' | 'unicode' | 'tags'>('index');
+const customEmojiFolderRoot: CustomEmojiFolderTree = { value: "", category: "", children: [] };
+
+function parseAndMergeCategories(input: string, root: CustomEmojiFolderTree): CustomEmojiFolderTree {
+ const parts = input.split('/').map(p => p.trim());
+ let currentNode: CustomEmojiFolderTree = root;
+
+ for (const part of parts) {
+ let existingNode = currentNode.children.find((node) => node.value === part);
+ if (!existingNode) {
+ const newNode: CustomEmojiFolderTree = { value: part, category: input, children: [] };
+ currentNode.children.push(newNode);
+ existingNode = newNode;
+ }
+
+ currentNode = existingNode;
+ }
+
+ return currentNode;
+}
+
+customEmojiCategories.value.forEach(ec => {
+ if (ec !== null) {
+ parseAndMergeCategories(ec, customEmojiFolderRoot);
+ }
+});
+
+parseAndMergeCategories('', customEmojiFolderRoot);
+
watch(q, () => {
if (emojisEl.value) emojisEl.value.scrollTop = 0;
diff --git a/packages/frontend/src/scripts/emojilist.ts b/packages/frontend/src/scripts/emojilist.ts
index 4159da84c8..8885bf4b7f 100644
--- a/packages/frontend/src/scripts/emojilist.ts
+++ b/packages/frontend/src/scripts/emojilist.ts
@@ -43,3 +43,9 @@ export function getEmojiName(char: string): string | null {
return emojilist[idx].name;
}
}
+
+export interface CustomEmojiFolderTree {
+ value: string;
+ category: string;
+ children: CustomEmojiFolderTree[];
+}