From 605efa75785e1efaa7fb1672a3f5c5dc50dae2d7 Mon Sep 17 00:00:00 2001 From: hitalin Date: Wed, 4 Nov 2026 09:14:01 +0900 Subject: [PATCH] =?UTF-8?q?feat(plugins):=20mfm-search-engine=20=E3=82=92?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0=20(MFM=20=E6=A4=9C=E7=B4=A2=E3=83=96?= =?UTF-8?q?=E3=83=AD=E3=83=83=E3=82=AF=E3=81=AE=E9=81=B7=E7=A7=BB=E5=85=88?= =?UTF-8?q?=E3=82=92=E5=A5=BD=E3=81=8D=E3=81=AA=E6=A4=9C=E7=B4=A2=E3=82=A8?= =?UTF-8?q?=E3=83=B3=E3=82=B8=E3=83=B3=E3=81=AB=E3=81=99=E3=82=8B)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Misskey Web も NoteDeck も MFM の検索ブロック「foo 検索」の遷移先は Google 固定で、 設定もプラグインのフックも無い。note_view_interruptor で表示時に検索行だけを サイレントリンク `?[foo 検索 (duckduckgo.com)](https://duckduckgo.com/?q=foo)` に 書き換える。投稿そのものは変更しない。本家 Misskey Web にも同じフックがあるので両対応。 安全側の設計: - 遷移先はプリセット名 (duckduckgo / brave / startpage / searx / google / bing) か https:// の URL だけ。http / javascript / 未知の名前は書き換えを止めて起動時に警告 - リンクのラベルに遷移先ホストを出す - コードブロック内・引用行・検索語に [ ] を含む行は触らない AiScript の "\n" はエスケープにならない (`\n` → `n`) ので改行・タブは Str:lf / Str:from_codepoint から作る。受け入れテスト 11 件 (4 記法・CRLF・コードブロック・ 括弧・プリセット・https 限定・リノート・3000 字で 9k steps) を mfm-js 0.27 で裏取り。 Co-Authored-By: Claude Fable 5.1 --- public/registry/index.json | 14 +- public/registry/plugins.json | 49 ++++++- .../plugins/mfm-search-engine/api.json | 1 + .../plugins/mfm-search-engine/icon.svg | 18 +++ .../plugins/mfm-search-engine/locales/en.json | 5 + .../plugins/mfm-search-engine/meta.json | 14 ++ .../plugins/mfm-search-engine/plugin.is | 138 ++++++++++++++++++ public/registry/queries.json | 2 +- public/registry/skills.json | 2 +- public/registry/themes.json | 2 +- public/registry/widgets.json | 4 +- scripts/sync-icons.js | 1 + 12 files changed, 236 insertions(+), 14 deletions(-) create mode 100644 public/registry/plugins/mfm-search-engine/api.json create mode 100644 public/registry/plugins/mfm-search-engine/icon.svg create mode 100644 public/registry/plugins/mfm-search-engine/locales/en.json create mode 100644 public/registry/plugins/mfm-search-engine/meta.json create mode 100644 public/registry/plugins/mfm-search-engine/plugin.is diff --git a/public/registry/index.json b/public/registry/index.json index 3c53a86..dd02a08 100644 --- a/public/registry/index.json +++ b/public/registry/index.json @@ -1,24 +1,24 @@ { "version": 1, - "updatedAt": "2026-10-29T19:40:04.494Z", + "updatedAt": "2026-11-04T00:13:26.700Z", "plugins": { - "count": 43, - "updatedAt": "2026-10-29T19:40:04.494Z" + "count": 44, + "updatedAt": "2026-11-04T00:13:26.700Z" }, "themes": { "count": 77, - "updatedAt": "2026-10-29T19:40:04.494Z" + "updatedAt": "2026-11-04T00:13:26.700Z" }, "widgets": { "count": 70, - "updatedAt": "2026-10-29T19:40:04.494Z" + "updatedAt": "2026-11-04T00:13:26.700Z" }, "skills": { "count": 44, - "updatedAt": "2026-10-29T19:40:04.494Z" + "updatedAt": "2026-11-04T00:13:26.700Z" }, "queries": { "count": 35, - "updatedAt": "2026-10-29T19:40:04.494Z" + "updatedAt": "2026-11-04T00:13:26.700Z" } } diff --git a/public/registry/plugins.json b/public/registry/plugins.json index 0f12b11..a77a9c8 100644 --- a/public/registry/plugins.json +++ b/public/registry/plugins.json @@ -1,6 +1,6 @@ { "version": 1, - "updatedAt": "2026-10-29T19:40:04.494Z", + "updatedAt": "2026-11-04T00:13:26.700Z", "plugins": [ { "id": "ai-actions", @@ -113,6 +113,51 @@ "misskeyScopes": [] } }, + { + "id": "mfm-search-engine", + "name": "MFM 検索の検索エンジン変更", + "version": "1.0.0", + "author": "hitalin", + "description": "MFM の検索ブロック (foo 検索) の遷移先を DuckDuckGo などの好きな検索エンジンにする。表示時に検索行をリンクへ書き換えるだけで、投稿そのものは変更しない。プリセット名か https の URL だけを受け付け、リンクに遷移先ホストを表示", + "category": "utility", + "capabilities": [], + "tags": [ + "search", + "mfm", + "note-filter", + "privacy" + ], + "sourceUrl": "https://store.notedeck.io/registry/plugins/mfm-search-engine/plugin.is", + "apiUrl": "https://store.notedeck.io/registry/plugins/mfm-search-engine/api.json", + "sha512": "7d1e5ca3fc6f99c231f6fd4de7cb146c8284dd0210662069fd88a1c4b833f47a6fb9bd2c6f70182f48f68481840984559d955c3be9033855eb5e7f02fbd3f90d", + "createdAt": "2026-11-04T00:13:27.209Z", + "updatedAt": "2026-11-04T00:13:27.209Z", + "authorUrl": "https://github.com/hitalin", + "license": "MIT", + "locales": { + "en": { + "name": "MFM Search Engine Switcher", + "description": "Sends MFM search blocks (foo 検索 / foo search) to the search engine of your choice, such as DuckDuckGo. Only rewrites the search line into a link at display time; the note itself is untouched. Accepts preset names or https URLs only, and shows the destination host on the link." + } + }, + "iconUrl": "https://store.notedeck.io/registry/plugins/mfm-search-engine/icon.svg", + "contract": { + "runsOn": [ + "notedeck", + "misskey-web" + ], + "profile": "readonly", + "permissions": [], + "ndCalls": [], + "mkApi": [], + "hooks": [ + "note_view_interruptor" + ], + "external": false, + "unresolved": [], + "misskeyScopes": [] + } + }, { "id": "plugin-smith", "name": "Plugin Smith", @@ -340,7 +385,7 @@ "apiUrl": "https://store.notedeck.io/registry/plugins/url-safety-check/api.json", "sha512": "461a8a39058c16cd51c333fd8ce071191ab86b698a3249305d8ec127c693293505fa6586742883373617fdab7017a2b2ab8364ed849f91e78942967dca380c4a", "createdAt": "2026-07-14T15:09:18+09:00", - "updatedAt": "2026-09-25T18:20:03+09:00", + "updatedAt": "2026-10-30T04:41:03+09:00", "authorUrl": "https://github.com/hitalin", "license": "MIT", "locales": { diff --git a/public/registry/plugins/mfm-search-engine/api.json b/public/registry/plugins/mfm-search-engine/api.json new file mode 100644 index 0000000..864cc48 --- /dev/null +++ b/public/registry/plugins/mfm-search-engine/api.json @@ -0,0 +1 @@ +{"type":"plugin","data":"/// @ 1.2.1\n\n// MFM 検索の検索エンジン変更 — 「foo 検索」の遷移先を好きな検索エンジンにする\n//\n// Misskey Web も NoteDeck も MFM の検索ブロック (「foo 検索」「foo [search]」など)\n// の遷移先は Google 固定で、設定もプラグインのフックも無い。そこで\n// Plugin:register_note_view_interruptor で表示時に検索行だけをサイレントリンク\n// `?[foo 検索 (duckduckgo.com)](https://duckduckgo.com/?q=foo)` に書き換える。\n// 投稿そのものは変更しない (表示専用)。見た目は検索ボックスではなく通常の\n// リンクになる。\n//\n// 遷移先はプリセット名か https:// の URL だけを受け付け、それ以外 (http /\n// javascript / 未知の名前) は書き換えを止めて起動時に警告する。リンクのラベルに\n// 遷移先ホストを出すので、押す前にどこへ飛ぶか分かる。\n//\n// 書き換えない行: コードブロック内、引用 (> で始まる行)、検索語に [ ] を含む行\n// (リンクのラベルが壊れる)。\n//\n// interruptor は同期実行なので、この中では Mk:api / Nd:call / ダイアログを使わない。\n// 本家 Misskey Web にも同じフックがあるので NoteDeck 専用ではない。\n\n### {\n\tname: \"MFM 検索の検索エンジン変更\"\n\tversion: \"1.0.0\"\n\tauthor: \"@hitalin\"\n\tdescription: \"MFM の検索ブロック (foo 検索) の遷移先を DuckDuckGo などの好きな検索エンジンにします。表示時に検索行をリンクへ書き換えるだけで、投稿そのものは変更しません\"\n\tconfig: {\n\t\tengine: {\n\t\t\ttype: \"string\"\n\t\t\tlabel: \"検索エンジン\"\n\t\t\tdescription: \"プリセット名 (duckduckgo / brave / startpage / searx / google / bing) か、https:// で始まる URL。URL は末尾に検索語が付きます。{query} を書くとその位置に入ります\"\n\t\t\tdefault: \"duckduckgo\"\n\t\t}\n\t}\n}\n\nlet PRESETS = {\n\tduckduckgo: \"https://duckduckgo.com/?q={query}\"\n\tbrave: \"https://search.brave.com/search?q={query}\"\n\tstartpage: \"https://www.startpage.com/do/search?q={query}\"\n\tsearx: \"https://search.disroot.org/search?q={query}\"\n\tgoogle: \"https://www.google.com/search?q={query}\"\n\tbing: \"https://www.bing.com/search?q={query}\"\n}\n\n// 設定値を URL テンプレートに。プリセット名でも https URL でもなければ null\n@resolveTemplate(raw) {\n\tlet s = raw.trim()\n\tlet key = s.lower()\n\tif (Obj:has(PRESETS, key)) return Obj:get(PRESETS, key)\n\tif (!s.starts_with(\"https://\")) return null\n\tif (s.index_of(\"{query}\") != -1) return s\n\t[s, \"{query}\"].join(\"\")\n}\n\n// テンプレートのホスト部 (https:// の後から / ? # の手前まで)\n@hostOf(template) {\n\tlet rest = template.slice(8, template.len)\n\tvar end = rest.len\n\teach (let c, [\"/\", \"?\", \"#\"]) {\n\t\tlet i = rest.index_of(c)\n\t\tif (i != -1 && i < end) end = i\n\t}\n\trest.slice(0, end)\n}\n\nlet TEMPLATE = resolveTemplate(Plugin:config.engine)\nlet HOST = if (TEMPLATE != null) hostOf(TEMPLATE) else \"\"\n\nif (TEMPLATE == null) {\n\tMk:dialog(\"warning\", \"MFM 検索の検索エンジン変更: 設定「検索エンジン」はプリセット名 (duckduckgo / brave / startpage / searx / google / bing) か https:// で始まる URL にしてください。それまで検索行は書き換えません\")\n}\n\n// mfm-js の検索ブロック: 行頭から「検索語 + 空白 1 文字 + ボタン」で行末。大小無視\nlet BUTTONS = [\"[検索]\", \"[search]\", \"検索\", \"search\"]\n// AiScript の \"\\n\" はエスケープにならないので制御文字はコードポイントから作る\nlet LF = Str:lf\nlet CRLF = [Str:from_codepoint(13), LF].join(\"\")\nlet SPACES = [\" \", \" \", Str:from_codepoint(9)]\n\n// 行末のボタン部分の長さ。無ければ 0\n@trailingButton(line) {\n\tlet lower = line.lower()\n\teach (let b, BUTTONS) {\n\t\tif (lower.ends_with(b)) return b.len\n\t}\n\t0\n}\n\n// 検索行なら { query, button } を返す。違う行や、リンクにできない行は null。\n// button は行末の「検索」「search」(角括弧は外す。ラベルに入れると壊れる)\n@parseSearchLine(line) {\n\tlet bl = trailingButton(line)\n\tif (bl == 0) return null\n\tlet head = line.slice(0, line.len - bl)\n\tif (head.len < 2) return null\n\tif (!SPACES.incl(head.slice(head.len - 1, head.len))) return null\n\tlet query = head.slice(0, head.len - 1)\n\tif (query.index_of(\"[\") != -1) return null\n\tif (query.index_of(\"]\") != -1) return null\n\tlet button = line.slice(line.len - bl, line.len).replace(\"[\", \"\").replace(\"]\", \"\")\n\t{ query: query, button: button }\n}\n\n@buildUrl(query) {\n\t// encodeURIComponent は ( ) を残すが、リンク構文の括弧と衝突するので潰す\n\tlet q = Uri:encode_component(query).replace(\"(\", \"%28\").replace(\")\", \"%29\")\n\tTEMPLATE.replace(\"{query}\", q)\n}\n\n@rewrite(text) {\n\tlet lines = text.replace(CRLF, LF).split(LF)\n\tvar changed = false\n\tvar inCode = false\n\tvar parts = []\n\teach (let line, lines) {\n\t\tvar line2 = line\n\t\tif (line.starts_with(\"```\")) {\n\t\t\tinCode = !inCode\n\t\t} elif (!inCode && !line.starts_with(\">\")) {\n\t\t\tlet hit = parseSearchLine(line)\n\t\t\tif (hit != null) {\n\t\t\t\tline2 = `?[{hit.query} {hit.button} ({HOST})]({buildUrl(hit.query)})`\n\t\t\t\tchanged = true\n\t\t\t}\n\t\t}\n\t\tparts.push(line2)\n\t}\n\tif (changed) parts.join(LF) else text\n}\n\nPlugin:register_note_view_interruptor(@(note) {\n\tif (TEMPLATE != null) {\n\t\tif (Core:type(note.text) == \"str\") note.text = rewrite(note.text)\n\t\tif (Core:type(note.renote) == \"obj\" && Core:type(note.renote.text) == \"str\") note.renote.text = rewrite(note.renote.text)\n\t}\n\tnote\n})\n"} diff --git a/public/registry/plugins/mfm-search-engine/icon.svg b/public/registry/plugins/mfm-search-engine/icon.svg new file mode 100644 index 0000000..0e785fe --- /dev/null +++ b/public/registry/plugins/mfm-search-engine/icon.svg @@ -0,0 +1,18 @@ + + + + + + + diff --git a/public/registry/plugins/mfm-search-engine/locales/en.json b/public/registry/plugins/mfm-search-engine/locales/en.json new file mode 100644 index 0000000..ba19008 --- /dev/null +++ b/public/registry/plugins/mfm-search-engine/locales/en.json @@ -0,0 +1,5 @@ +{ + "sourceHash": "e76c1b570d55", + "name": "MFM Search Engine Switcher", + "description": "Sends MFM search blocks (foo 検索 / foo search) to the search engine of your choice, such as DuckDuckGo. Only rewrites the search line into a link at display time; the note itself is untouched. Accepts preset names or https URLs only, and shows the destination host on the link." +} diff --git a/public/registry/plugins/mfm-search-engine/meta.json b/public/registry/plugins/mfm-search-engine/meta.json new file mode 100644 index 0000000..9451f2b --- /dev/null +++ b/public/registry/plugins/mfm-search-engine/meta.json @@ -0,0 +1,14 @@ +{ + "id": "mfm-search-engine", + "name": "MFM 検索の検索エンジン変更", + "version": "1.0.0", + "author": "hitalin", + "authorUrl": "https://github.com/hitalin", + "description": "MFM の検索ブロック (foo 検索) の遷移先を DuckDuckGo などの好きな検索エンジンにする。表示時に検索行をリンクへ書き換えるだけで、投稿そのものは変更しない。プリセット名か https の URL だけを受け付け、リンクに遷移先ホストを表示", + "license": "MIT", + "category": "utility", + "capabilities": [], + "tags": ["search", "mfm", "note-filter", "privacy"], + "permissions": [], + "minNoteDeckVersion": "1.11.0" +} diff --git a/public/registry/plugins/mfm-search-engine/plugin.is b/public/registry/plugins/mfm-search-engine/plugin.is new file mode 100644 index 0000000..43a7faf --- /dev/null +++ b/public/registry/plugins/mfm-search-engine/plugin.is @@ -0,0 +1,138 @@ +/// @ 1.2.1 + +// MFM 検索の検索エンジン変更 — 「foo 検索」の遷移先を好きな検索エンジンにする +// +// Misskey Web も NoteDeck も MFM の検索ブロック (「foo 検索」「foo [search]」など) +// の遷移先は Google 固定で、設定もプラグインのフックも無い。そこで +// Plugin:register_note_view_interruptor で表示時に検索行だけをサイレントリンク +// `?[foo 検索 (duckduckgo.com)](https://duckduckgo.com/?q=foo)` に書き換える。 +// 投稿そのものは変更しない (表示専用)。見た目は検索ボックスではなく通常の +// リンクになる。 +// +// 遷移先はプリセット名か https:// の URL だけを受け付け、それ以外 (http / +// javascript / 未知の名前) は書き換えを止めて起動時に警告する。リンクのラベルに +// 遷移先ホストを出すので、押す前にどこへ飛ぶか分かる。 +// +// 書き換えない行: コードブロック内、引用 (> で始まる行)、検索語に [ ] を含む行 +// (リンクのラベルが壊れる)。 +// +// interruptor は同期実行なので、この中では Mk:api / Nd:call / ダイアログを使わない。 +// 本家 Misskey Web にも同じフックがあるので NoteDeck 専用ではない。 + +### { + name: "MFM 検索の検索エンジン変更" + version: "1.0.0" + author: "@hitalin" + description: "MFM の検索ブロック (foo 検索) の遷移先を DuckDuckGo などの好きな検索エンジンにします。表示時に検索行をリンクへ書き換えるだけで、投稿そのものは変更しません" + config: { + engine: { + type: "string" + label: "検索エンジン" + description: "プリセット名 (duckduckgo / brave / startpage / searx / google / bing) か、https:// で始まる URL。URL は末尾に検索語が付きます。{query} を書くとその位置に入ります" + default: "duckduckgo" + } + } +} + +let PRESETS = { + duckduckgo: "https://duckduckgo.com/?q={query}" + brave: "https://search.brave.com/search?q={query}" + startpage: "https://www.startpage.com/do/search?q={query}" + searx: "https://search.disroot.org/search?q={query}" + google: "https://www.google.com/search?q={query}" + bing: "https://www.bing.com/search?q={query}" +} + +// 設定値を URL テンプレートに。プリセット名でも https URL でもなければ null +@resolveTemplate(raw) { + let s = raw.trim() + let key = s.lower() + if (Obj:has(PRESETS, key)) return Obj:get(PRESETS, key) + if (!s.starts_with("https://")) return null + if (s.index_of("{query}") != -1) return s + [s, "{query}"].join("") +} + +// テンプレートのホスト部 (https:// の後から / ? # の手前まで) +@hostOf(template) { + let rest = template.slice(8, template.len) + var end = rest.len + each (let c, ["/", "?", "#"]) { + let i = rest.index_of(c) + if (i != -1 && i < end) end = i + } + rest.slice(0, end) +} + +let TEMPLATE = resolveTemplate(Plugin:config.engine) +let HOST = if (TEMPLATE != null) hostOf(TEMPLATE) else "" + +if (TEMPLATE == null) { + Mk:dialog("warning", "MFM 検索の検索エンジン変更: 設定「検索エンジン」はプリセット名 (duckduckgo / brave / startpage / searx / google / bing) か https:// で始まる URL にしてください。それまで検索行は書き換えません") +} + +// mfm-js の検索ブロック: 行頭から「検索語 + 空白 1 文字 + ボタン」で行末。大小無視 +let BUTTONS = ["[検索]", "[search]", "検索", "search"] +// AiScript の "\n" はエスケープにならないので制御文字はコードポイントから作る +let LF = Str:lf +let CRLF = [Str:from_codepoint(13), LF].join("") +let SPACES = [" ", " ", Str:from_codepoint(9)] + +// 行末のボタン部分の長さ。無ければ 0 +@trailingButton(line) { + let lower = line.lower() + each (let b, BUTTONS) { + if (lower.ends_with(b)) return b.len + } + 0 +} + +// 検索行なら { query, button } を返す。違う行や、リンクにできない行は null。 +// button は行末の「検索」「search」(角括弧は外す。ラベルに入れると壊れる) +@parseSearchLine(line) { + let bl = trailingButton(line) + if (bl == 0) return null + let head = line.slice(0, line.len - bl) + if (head.len < 2) return null + if (!SPACES.incl(head.slice(head.len - 1, head.len))) return null + let query = head.slice(0, head.len - 1) + if (query.index_of("[") != -1) return null + if (query.index_of("]") != -1) return null + let button = line.slice(line.len - bl, line.len).replace("[", "").replace("]", "") + { query: query, button: button } +} + +@buildUrl(query) { + // encodeURIComponent は ( ) を残すが、リンク構文の括弧と衝突するので潰す + let q = Uri:encode_component(query).replace("(", "%28").replace(")", "%29") + TEMPLATE.replace("{query}", q) +} + +@rewrite(text) { + let lines = text.replace(CRLF, LF).split(LF) + var changed = false + var inCode = false + var parts = [] + each (let line, lines) { + var line2 = line + if (line.starts_with("```")) { + inCode = !inCode + } elif (!inCode && !line.starts_with(">")) { + let hit = parseSearchLine(line) + if (hit != null) { + line2 = `?[{hit.query} {hit.button} ({HOST})]({buildUrl(hit.query)})` + changed = true + } + } + parts.push(line2) + } + if (changed) parts.join(LF) else text +} + +Plugin:register_note_view_interruptor(@(note) { + if (TEMPLATE != null) { + if (Core:type(note.text) == "str") note.text = rewrite(note.text) + if (Core:type(note.renote) == "obj" && Core:type(note.renote.text) == "str") note.renote.text = rewrite(note.renote.text) + } + note +}) diff --git a/public/registry/queries.json b/public/registry/queries.json index 8d543ac..a2ce64c 100644 --- a/public/registry/queries.json +++ b/public/registry/queries.json @@ -1,6 +1,6 @@ { "version": 1, - "updatedAt": "2026-10-29T19:40:04.494Z", + "updatedAt": "2026-11-04T00:13:26.700Z", "queries": [ { "id": "bot-only", diff --git a/public/registry/skills.json b/public/registry/skills.json index 6913fe9..0f6ec7b 100644 --- a/public/registry/skills.json +++ b/public/registry/skills.json @@ -1,6 +1,6 @@ { "version": 1, - "updatedAt": "2026-10-29T19:40:04.494Z", + "updatedAt": "2026-11-04T00:13:26.700Z", "skills": [ { "id": "aiscript-surgeon", diff --git a/public/registry/themes.json b/public/registry/themes.json index 66eaf7a..7e7668d 100644 --- a/public/registry/themes.json +++ b/public/registry/themes.json @@ -1,6 +1,6 @@ { "version": 1, - "updatedAt": "2026-10-29T19:40:04.494Z", + "updatedAt": "2026-11-04T00:13:26.700Z", "themes": [ { "id": "adwaita-dark", diff --git a/public/registry/widgets.json b/public/registry/widgets.json index fdfc12e..7fd8a88 100644 --- a/public/registry/widgets.json +++ b/public/registry/widgets.json @@ -1,6 +1,6 @@ { "version": 1, - "updatedAt": "2026-10-29T20:42:21.909Z", + "updatedAt": "2026-11-04T00:13:26.700Z", "widgets": [ { "id": "aiscript-health", @@ -2485,7 +2485,7 @@ "apiUrl": "https://store.notedeck.io/registry/widgets/emoji-usage/api.json", "sha512": "af81b93456a39950440310815fe23d8b05d97b2399f4474033d0a112e1d5ef1f6d4eb63aa2123fb5ff767ae1a71cfea8005699248dfe0c401f929203c82fd692", "createdAt": "2026-10-12T19:09:59+09:00", - "updatedAt": "2026-10-30T05:16:25+09:00", + "updatedAt": "2026-10-30T05:42:56+09:00", "authorUrl": "https://github.com/hitalin", "license": "MIT", "locales": { diff --git a/scripts/sync-icons.js b/scripts/sync-icons.js index 297ca3f..e5ad462 100644 --- a/scripts/sync-icons.js +++ b/scripts/sync-icons.js @@ -35,6 +35,7 @@ const ICON_MAP = { 'plugins/quick-clip': { pack: 'lucide', name: 'paperclip' }, 'plugins/vip-notify': { pack: 'lucide', name: 'bell-plus' }, 'plugins/spoiler-guard': { pack: 'lucide', name: 'eye-off' }, + 'plugins/mfm-search-engine': { pack: 'lucide', name: 'text-search' }, 'plugins/misskey-translate': { pack: 'lucide', name: 'languages' }, 'plugins/thread-to-memo': { pack: 'lucide', name: 'messages-square' }, 'plugins/column-presets': { pack: 'lucide', name: 'columns-3' },