Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions public/registry/index.json
Original file line number Diff line number Diff line change
@@ -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"
}
}
49 changes: 47 additions & 2 deletions public/registry/plugins.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"version": 1,
"updatedAt": "2026-10-29T19:40:04.494Z",
"updatedAt": "2026-11-04T00:13:26.700Z",
"plugins": [
{
"id": "ai-actions",
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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": {
Expand Down
1 change: 1 addition & 0 deletions public/registry/plugins/mfm-search-engine/api.json
Original file line number Diff line number Diff line change
@@ -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"}
18 changes: 18 additions & 0 deletions public/registry/plugins/mfm-search-engine/icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions public/registry/plugins/mfm-search-engine/locales/en.json
Original file line number Diff line number Diff line change
@@ -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."
}
14 changes: 14 additions & 0 deletions public/registry/plugins/mfm-search-engine/meta.json
Original file line number Diff line number Diff line change
@@ -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"
}
138 changes: 138 additions & 0 deletions public/registry/plugins/mfm-search-engine/plugin.is
Original file line number Diff line number Diff line change
@@ -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
})
2 changes: 1 addition & 1 deletion public/registry/queries.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"version": 1,
"updatedAt": "2026-10-29T19:40:04.494Z",
"updatedAt": "2026-11-04T00:13:26.700Z",
"queries": [
{
"id": "bot-only",
Expand Down
2 changes: 1 addition & 1 deletion public/registry/skills.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"version": 1,
"updatedAt": "2026-10-29T19:40:04.494Z",
"updatedAt": "2026-11-04T00:13:26.700Z",
"skills": [
{
"id": "aiscript-surgeon",
Expand Down
2 changes: 1 addition & 1 deletion public/registry/themes.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"version": 1,
"updatedAt": "2026-10-29T19:40:04.494Z",
"updatedAt": "2026-11-04T00:13:26.700Z",
"themes": [
{
"id": "adwaita-dark",
Expand Down
4 changes: 2 additions & 2 deletions public/registry/widgets.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"version": 1,
"updatedAt": "2026-10-29T20:42:21.909Z",
"updatedAt": "2026-11-04T00:13:26.700Z",
"widgets": [
{
"id": "aiscript-health",
Expand Down Expand Up @@ -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": {
Expand Down
1 change: 1 addition & 0 deletions scripts/sync-icons.js
Original file line number Diff line number Diff line change
Expand Up @@ -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' },
Expand Down
Loading