// ==UserScript==
// @name Advanced Search for X (Twitter) 🔍
// @name:ja Advanced Search for X(Twitter)🔍
// @name:en Advanced Search for X (Twitter) 🔍
// @name:zh-CN Advanced Search for X(Twitter)🔍
// @name:zh-TW Advanced Search for X(Twitter)🔍
// @name:ko Advanced Search for X (Twitter) 🔍
// @name:fr Advanced Search for X (Twitter) 🔍
// @name:es Advanced Search for X (Twitter) 🔍
// @name:de Advanced Search for X (Twitter) 🔍
// @name:pt-BR Advanced Search for X (Twitter) 🔍
// @name:ru Advanced Search for X (Twitter) 🔍
// @version 6.3.0
// @description Adds a floating modal for advanced search on X.com (Twitter). Syncs with search box and remembers position/display state. The top-right search icon is now draggable and its position persists.
// @description:ja X.com(Twitter)に高度な検索機能を呼び出せるフローティング・モーダルを追加します。検索ボックスと双方向で同期し、位置や表示状態も記憶します。右上の検索アイコンはドラッグで移動でき、位置は保存されます。
// @description:en Adds a floating modal for advanced search on X.com (formerly Twitter). Syncs with search box and remembers position/display state. The top-right search icon is draggable with persistent position.
// @description:zh-CN 为X.com(Twitter)添加高级搜索浮动模态框,支持与搜索框双向同步并记住位置与显示状态。右上角的搜索图标可拖动,并会记住位置。
// @description:zh-TW 為 X.com(Twitter)增加高級搜尋模態框,支援與搜尋框雙向同步並記住位置與顯示狀態。右上角搜尋圖示可拖曳,位置會被保存。
// @description:ko X.com(Twitter)에 고급 검색 모달을 추가합니다. 검색창과 양방향 동기화하며 위치와 표시 상태를 기억합니다. 우상단 검색 아이콘은 드래그 이동 및 위치 저장이 가능합니다.
// @description:fr Ajoute une fenêtre modale de recherche avancée à X.com (Twitter), synchronisée avec la barre de recherche et mémorise de l’état d’affichage. L’icône de recherche en haut à droite est déplaçable.
// @description:es Agrega un modal flotante de búsqueda avanzada en X.com (Twitter), sincronizado con la caja de búsqueda y con estado persistente.
// @description:de Fügt X.com (Twitter) ein modales Fenster für erweiterte Suche hinzu, synchronisiert mit der Suchleiste und speichert Position/Zustand. Das Suchsymbol oben rechts ist per Drag & Drop verschiebbar und bleibt gespeichert.
// @description:pt-BR Adiciona um modal de busca avançada flutuante no X.com (Twitter), sincronizado com a caixa de busca e com estado salvo. O ícone de busca no canto superior direito é arrastável com posição persistente.
// @description:ru Добавляет модальное окно расширенного поиска на X.com (Twitter). Синхронизируется с поисковой строкой и запоминает состояние. Кнопку поиска в правом верхнем углу можно перетаскивать; её положение сохраняется.
// @namespace https://github.com/koyasi777/advanced-search-for-x-twitter
// @author koyasi777
// @match https://x.com/*
// @match https://twitter.com/*
// @exclude https://x.com/i/tweetdeck*
// @exclude https://twitter.com/i/tweetdeck*
// @icon https://raw.githubusercontent.com/koyasi777/advanced-search-for-x-twitter/refs/heads/main/extension/icons/icon-128.png
// @grant GM_addStyle
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_deleteValue
// @grant GM_info
// @run-at document-idle
// @license MIT
// @homepageURL https://github.com/koyasi777/advanced-search-for-x-twitter
// @supportURL https://github.com/koyasi777/advanced-search-for-x-twitter/issues
// @downloadURL none
// ==/UserScript==
const __X_ADV_SEARCH_MAIN_LOGIC__ = function() {
'use strict';
if (window.__X_ADV_SEARCH_INITED__) return;
window.__X_ADV_SEARCH_INITED__ = true;
const i18n = {
translations: {
'en': {
modalTitle: "Advanced Search",
tooltipClose: "Close",
labelAllWords: "All of these words",
placeholderAllWords: "e.g., AI news",
labelExactPhrase: "This exact phrase",
placeholderExactPhrase: 'e.g., "ChatGPT 4o"',
labelAnyWords: "Any of these words (OR)",
placeholderAnyWords: "e.g., iPhone Android",
labelNotWords: "None of these words (-)",
placeholderNotWords: "e.g., -sale -ads",
labelHashtag: "Hashtags (#)",
placeholderHashtag: "e.g., #TechEvent",
labelLang: "Language (lang:)",
optLangDefault: "Any language",
optLangJa: "Japanese (ja)",
optLangEn: "English (en)",
optLangId: "Indonesian (id)",
optLangHi: "Hindi (hi)",
optLangDe: "German (de)",
optLangTr: "Turkish (tr)",
optLangEs: "Spanish (es)",
optLangPt: "Portuguese (pt)",
optLangAr: "Arabic (ar)",
optLangFr: "French (fr)",
optLangKo: "Korean (ko)",
optLangRu: "Russian (ru)",
optLangZhHans: "Chinese Simplified (zh-cn)",
optLangZhHant: "Chinese Traditional (zh-tw)",
hrSeparator: " ",
labelFilters: "Filters",
labelVerified: "Verified accounts",
labelLinks: "Links",
labelImages: "Images",
labelVideos: "Videos",
labelReposts: "Reposts",
labelTimelineHashtags: "Hashtags (#)",
checkInclude: "Include",
checkExclude: "Exclude",
labelReplies: "Replies",
optRepliesDefault: "Default (Show all)",
optRepliesInclude: "Include replies",
optRepliesOnly: "Replies only",
optRepliesExclude: "Exclude replies",
labelEngagement: "Engagement",
placeholderMinReplies: "Min replies",
placeholderMinLikes: "Min likes",
placeholderMinRetweets: "Min reposts",
labelDateRange: "Date range",
labelDateShortcut: "Quick Range",
optDate1Day: "Past 24h",
optDate1Week: "Past week",
optDate1Month: "Past month",
optDate3Months: "Past 3 months",
optDate6Months: "Past 6 months",
optDate1Year: "Past year",
optDate2Years: "Past 2 years",
optDate3Years: "Past 3 years",
optDate5Years: "Past 5 years",
optDateClear: "Clear dates",
tooltipSince: "From this date",
tooltipUntil: "Until this date",
labelFromUser: "From these accounts (from:)",
placeholderFromUser: "e.g., @X",
labelToUser: "To these accounts (to:)",
placeholderToUser: "e.g., @google",
labelMentioning: "Mentioning these accounts (@)",
placeholderMentioning: "e.g., @OpenAI",
buttonClear: "Clear",
buttonApply: "Search",
tooltipTrigger: "Open Advanced Search",
buttonOpen: "Open",
tabSearch: "Search",
tabHistory: "History",
tabSaved: "Saved",
buttonSave: "Save",
buttonSaved: "Saved",
secretMode: "Secret",
secretOn: "Secret mode ON (No history)",
secretOff: "Secret mode OFF",
toastSaved: "Saved.",
toastDeleted: "Deleted.",
toastReordered: "Order updated.",
emptyHistory: "No history yet.",
emptySaved: "No saved searches. Add from the Save button at the bottom left of the Search tab.",
run: "Run",
delete: "Delete",
updated: "Updated.",
tooltipSecret: "Toggle Secret Mode (no history will be recorded)",
historyClearAll: "Clear All",
confirmClearHistory: "Clear all history?",
labelAccountScope: "Accounts",
optAccountAll: "All accounts",
optAccountFollowing: "Accounts you follow",
labelLocationScope: "Location",
optLocationAll: "All locations",
optLocationNearby: "Near you",
chipFollowing: "Following",
chipNearby: "Nearby",
labelSearchTarget: "Search target",
labelHitName: "Exclude matches only in display name",
labelHitHandle: "Exclude matches only in username (@handle)",
hintSearchTarget: "Hide posts that only match in name or handle (not in body).",
hintName: "If a keyword appears only in the display name, hide it.",
hintHandle: "If a keyword appears only in @username, hide it. Exception: when the query explicitly uses from:/to:/@ with the same word.",
tabMute: "Mute",
labelMuteWord: "Add mute word",
placeholderMuteWord: "e.g., spoiler",
labelCaseSensitive: "Case sensitive",
labelWordBoundary: "Whole word",
labelEnabled: "Enabled",
labelEnableAll: "Enable all",
buttonAdd: "Add",
emptyMuted: "No muted words.",
mutedListTitle: "Muted words",
mutedListHeading: "Muted items",
optMuteHidden: "Hidden",
optMuteCollapsed: "Collapsed",
placeholderFilterMute: "Filter muted words...",
muteLabel: "Muted: ",
buttonShow: "Show",
muteHit: "Mute hits in body",
buttonRemute: "Re-mute",
buttonImport: "Import",
buttonExport: "Export",
/* Accounts tab */
tabAccounts: "Accounts",
emptyAccounts: "No accounts yet. Open a profile and click the Add button to save it.",
buttonAddAccount: "Add account",
toastAccountAdded: "Account added.",
toastAccountExists: "Already added.",
buttonConfirm: "Confirm",
/* Lists tab */
tabLists: "Lists",
emptyLists: "No lists yet. Open a List and click the + button in the top-right to add it.",
buttonAddList: "Add list",
toastListAdded: "List added.",
toastListExists: "Already added.",
/* History tab */
placeholderSearchHistory: "Search history (query)",
labelSortBy: "Sort by:",
placeholderSearchSaved: "Search saved (query)",
sortNewest: "Newest first",
sortOldest: "Oldest first",
sortNameAsc: "Query (A-Z)",
sortNameDesc: "Query (Z-A)",
/* Folder/List/Account tabs */
placeholderFilterAccounts: "Filter accounts (@, name)",
placeholderFilterLists: "Filter lists (name, url)",
buttonAddFolder: "+Folder",
folderFilterAll: "ALL",
folderFilterUnassigned: "Unassigned",
folderRename: "Rename",
folderRenameTitle: "Rename folder",
folderDelete: "Delete",
folderDeleteTitle: "Delete folder",
promptNewFolder: "New folder name",
confirmDeleteFolder: "Delete this folder and all items inside it? This cannot be undone.",
optListsAll: "Lists",
defaultSavedFolders: "Saved Searches",
/* Favorites */
tabFavorites: "Favorites",
emptyFavorites: "No favorite tweets yet. Click the ★ icon on tweets to save them.",
optFavoritesAll: "All Favorites",
toastFavorited: "Added to favorites.",
toastUnfavorited: "Removed from favorites.",
/* Settings */
settingsTitle: "Settings",
settingsTitleGeneral: "General",
settingsTitleFeatures: "Tab Visibility",
settingsTitleData: "Data",
buttonClose: "Close",
labelUILang: "Interface language",
optUILangAuto: "Auto",
labelInitialTab: "Startup tab",
optInitialTabLast: "Last opened (Default)",
labelImportExport: "Import / Export",
placeholderSettingsJSON: "Paste backup JSON here...",
tooltipSettings: "Open settings",
toastImported: "Imported.",
alertInvalidJSON: "Invalid JSON file.",
alertInvalidData: "Invalid data format.",
alertInvalidApp: 'This file is not a valid backup for "Advanced Search for X".',
toastExported: "Exported to file.",
buttonReset: "Reset all data",
confirmResetAll: "Reset all data? This cannot be undone.",
toastReset: "All data has been reset.",
buttonImportSuccess: "Imported successfully 👍️",
/* Favorites Sort */
sortSavedNewest: "Saved date (Newest)",
sortSavedOldest: "Saved date (Oldest)",
sortPostedNewest: "Posted date (Newest)",
sortPostedOldest: "Posted date (Oldest)",
/* --- Favorite Tags --- */
FT_UNCATEGORIZED: 'Uncategorized',
FT_DROPDOWN_TITLE: 'Favorite Tags',
FT_DROPDOWN_SETTINGS_TITLE: 'Favorite Tag Settings',
FT_DROPDOWN_NEW_TAG: 'New tag',
FT_DROPDOWN_NEW_TAG_PLACEHOLDER: 'Tag name',
FT_DROPDOWN_NEW_TAG_ADD: 'Add',
FT_FILTER_ALL: 'All',
FT_SETTINGS_TITLE: 'Favorite Tag Settings',
FT_SETTINGS_EMPTY_TAG_LIST:
'No tags yet. You can add one from "New tag".',
FT_SETTINGS_UNCATEGORIZED_NAME: 'Uncategorized',
FT_SETTINGS_UNCATEGORIZED_NAME_TOOLTIP:
'The name of "Uncategorized" cannot be changed.',
FT_SETTINGS_UNCATEGORIZED_DELETE_TOOLTIP:
'"Uncategorized" cannot be deleted.',
FT_SETTINGS_CLOSE: 'Close',
FT_SETTINGS_DELETE_BUTTON: 'Delete',
FT_SETTINGS_UP: '▲',
FT_SETTINGS_DOWN: '▼',
FT_SETTINGS_DISPLAY_SECTION_TITLE: 'Display',
FT_SETTINGS_DISPLAY_MODE_LABEL: 'Tag label format',
FT_SETTINGS_DISPLAY_MODE_LEAF: 'Tag name only',
FT_SETTINGS_DISPLAY_MODE_FULL: 'Full path',
FT_CONFIRM_DELETE_TAG_MSG: 'Delete tag "{tagName}"?\nFavorites with this tag will become "Uncategorized".',
FT_SETTINGS_BUTTON_TITLE: 'Favorite Tag Settings',
},
'ja': {
modalTitle: "高度な検索",
tooltipClose: "閉じる",
labelAllWords: "すべての語句を含む",
placeholderAllWords: "例: AI ニュース",
labelExactPhrase: "この語句を完全に含む",
placeholderExactPhrase: '例: "ChatGPT 4o"',
labelAnyWords: "いずれかの語句を含む (OR)",
placeholderAnyWords: "例: iPhone Android",
labelNotWords: "含まない語句 (-)",
placeholderNotWords: "例: -セール -広告",
labelHashtag: "ハッシュタグ (#)",
placeholderHashtag: "例: #技術書典",
labelLang: "言語 (lang:)",
optLangDefault: "指定しない",
optLangJa: "日本語 (ja)",
optLangEn: "英語 (en)",
optLangId: "インドネシア語 (id)",
optLangHi: "ヒンディー語 (hi)",
optLangDe: "ドイツ語 (de)",
optLangTr: "トルコ語 (tr)",
optLangEs: "スペイン語 (es)",
optLangPt: "ポルトガル語 (pt)",
optLangAr: "アラビア語 (ar)",
optLangFr: "フランス語 (fr)",
optLangKo: "韓国語 (ko)",
optLangRu: "ロシア語 (ru)",
optLangZhHans: "中国語(簡体字)(zh-cn)",
optLangZhHant: "中国語(繁体字)(zh-tw)",
hrSeparator: " ",
labelFilters: "フィルター",
labelVerified: "認証済みアカウント",
labelLinks: "リンク",
labelImages: "画像",
labelVideos: "動画",
labelReposts: "リポスト",
labelTimelineHashtags: "ハッシュタグ (#)",
checkInclude: "含む",
checkExclude: "含まない",
labelReplies: "返信",
optRepliesDefault: "指定しない",
optRepliesInclude: "返信を含める",
optRepliesOnly: "返信のみ",
optRepliesExclude: "返信を除外",
labelEngagement: "エンゲージメント",
placeholderMinReplies: "最小返信数",
placeholderMinLikes: "最小いいね数",
placeholderMinRetweets: "最小リポスト数",
labelDateRange: "期間指定",
labelDateShortcut: "期間ショートカット",
optDate1Day: "過去24時間",
optDate1Week: "過去1週間",
optDate1Month: "過去1ヶ月",
optDate3Months: "過去3ヶ月",
optDate6Months: "過去6ヶ月",
optDate1Year: "過去1年",
optDate2Years: "過去2年",
optDate3Years: "過去3年",
optDate5Years: "過去5年",
optDateClear: "日付クリア",
tooltipSince: "この日以降",
tooltipUntil: "この日以前",
labelFromUser: "このアカウントから (from:)",
placeholderFromUser: "例: @X",
labelToUser: "このアカウントへ (to:)",
placeholderToUser: "例: @google",
labelMentioning: "このアカウントへのメンション (@)",
placeholderMentioning: "例: @OpenAI",
buttonClear: "クリア",
buttonApply: "検索実行",
tooltipTrigger: "高度な検索を開く",
buttonOpen: "開く",
tabSearch: "検索",
tabHistory: "履歴",
tabSaved: "保存",
buttonSave: "保存",
buttonSaved: "保存済み",
secretMode: "シークレット",
secretOn: "シークレットモード ON(履歴は記録しません)",
secretOff: "シークレットモード OFF",
toastSaved: "保存しました。",
toastDeleted: "削除しました。",
toastReordered: "並び順を更新しました。",
emptyHistory: "履歴はまだありません。",
emptySaved: "保存済みの検索はありません。検索タブの左下の保存から追加してください。",
run: "実行",
delete: "削除",
updated: "更新しました。",
tooltipSecret: "シークレットモードを切り替え(履歴を記録しません)",
historyClearAll: "すべて削除",
confirmClearHistory: "履歴をすべて削除しますか?",
labelAccountScope: "アカウント",
optAccountAll: "すべてのアカウント",
optAccountFollowing: "フォローしているアカウント",
labelLocationScope: "場所",
optLocationAll: "すべての場所",
optLocationNearby: "近くの場所",
chipFollowing: "フォロー中",
chipNearby: "近く",
labelSearchTarget: "検索対象",
labelHitName: "表示名(名前)のみのヒットは除外",
labelHitHandle: "ユーザー名(@)のみのヒットは除外",
hintSearchTarget: "本文ではなく、名前/ユーザー名のみに一致した投稿を非表示にします。",
hintName: "キーワードが表示名のみに含まれる場合は非表示にします。",
hintHandle: "キーワードが @ユーザー名のみに含まれる場合は非表示にします。例外: 同じ語を from:/to:/@ で明示しているときは表示します。",
tabMute: "ミュート",
labelMuteWord: "ミュート語句の追加",
placeholderMuteWord: "例: ネタバレ",
labelCaseSensitive: "大文字小文字を区別",
labelWordBoundary: "完全一致(単語)",
labelEnabled: "有効",
labelEnableAll: "すべて有効",
buttonAdd: "追加",
emptyMuted: "ミュート語句はまだありません。",
mutedListTitle: "ミュート語句",
mutedListHeading: "ミュート一覧",
optMuteHidden: "非表示",
optMuteCollapsed: "折りたたみ",
placeholderFilterMute: "ミュートを検索...",
muteLabel: "ミュート: ",
buttonShow: "表示する",
muteHit: "本文でのヒットをミュート",
buttonRemute: "再ミュート",
buttonImport: "インポート",
buttonExport: "エクスポート",
/* Accounts tab */
tabAccounts: "アカウント",
emptyAccounts: "アカウントはまだありません。アカウントページの追加ボタンから追加してください。",
buttonAddAccount: "アカウントを追加",
toastAccountAdded: "アカウントを追加しました。",
toastAccountExists: "すでに追加済みです。",
buttonConfirm: "確認",
/* Lists tab */
tabLists: "リスト",
emptyLists: "リストはまだありません。リストを開き右上の+ボタンから追加してください。",
buttonAddList: "リストを追加",
toastListAdded: "リストを追加しました。",
toastListExists: "すでに追加済みです。",
/* History tab */
placeholderSearchHistory: "履歴を検索(クエリ)",
labelSortBy: "並び順:",
placeholderSearchSaved: "保存済みを検索(クエリ)",
sortNewest: "新しい順",
sortOldest: "古い順",
sortNameAsc: "クエリ (昇順)",
sortNameDesc: "クエリ (降順)",
/* Folder/List/Account tabs */
placeholderFilterAccounts: "アカウントを検索 (@, 名前)",
placeholderFilterLists: "リストを検索 (名前, URL)",
buttonAddFolder: "+フォルダー",
folderFilterAll: "すべて",
folderFilterUnassigned: "未分類",
folderRename: "名前変更",
folderRenameTitle: "フォルダー名を変更",
folderDelete: "削除",
folderDeleteTitle: "フォルダーを削除",
promptNewFolder: "新しいフォルダー名",
confirmDeleteFolder: "このフォルダーと中のすべてのアイテムを完全に削除しますか?この操作は元に戻せません。",
optListsAll: "リスト",
defaultSavedFolders: "保存済み検索",
/* Favorites */
tabFavorites: "お気に入り",
emptyFavorites: "お気に入りはまだありません。ツイートの★ボタンをクリックして保存できます。",
optFavoritesAll: "すべてのお気に入り",
toastFavorited: "お気に入りに追加しました。",
toastUnfavorited: "お気に入りから削除しました。",
/* Settings */
settingsTitle: "設定",
settingsTitleGeneral: "一般設定",
settingsTitleFeatures: "タブ表示設定",
settingsTitleData: "データ管理",
buttonClose: "閉じる",
labelUILang: "UI 言語",
optUILangAuto: "自動判定",
labelInitialTab: "起動時に開くタブ",
optInitialTabLast: "前回のタブ (デフォルト)",
labelImportExport: "インポート / エクスポート",
placeholderSettingsJSON: "ここにバックアップ JSON を貼り付けてください...",
tooltipSettings: "設定を開く",
toastImported: "インポートしました。",
toastExported: "ファイルにエクスポートしました。",
alertInvalidJSON: "無効なJSONファイルです。",
alertInvalidData: "無効なデータ形式です。",
alertInvalidApp: "このファイルは「Advanced Search for X」のバックアップデータではありません。",
buttonReset: "すべて初期化",
confirmResetAll: "すべてのデータを初期化しますか?この操作は元に戻せません。",
toastReset: "すべてのデータを初期化しました。",
buttonImportSuccess: "インポートに成功しました👍️",
/* Favorites Sort */
sortSavedNewest: "追加日 (新しい順)",
sortSavedOldest: "追加日 (古い順)",
sortPostedNewest: "投稿日 (新しい順)",
sortPostedOldest: "投稿日 (古い順)",
/* --- Favorite Tags --- */
FT_UNCATEGORIZED: '未分類',
FT_DROPDOWN_TITLE: 'お気に入りタグ',
FT_DROPDOWN_SETTINGS_TITLE: 'お気に入りタグ設定',
FT_DROPDOWN_NEW_TAG: '新しいタグ',
FT_DROPDOWN_NEW_TAG_PLACEHOLDER: 'タグ名',
FT_DROPDOWN_NEW_TAG_ADD: '追加',
FT_FILTER_ALL: 'すべて',
FT_SETTINGS_TITLE: 'お気に入りタグ設定',
FT_SETTINGS_EMPTY_TAG_LIST:
'タグはまだありません。「新しいタグ」から追加できます。',
FT_SETTINGS_UNCATEGORIZED_NAME: '未分類',
FT_SETTINGS_UNCATEGORIZED_NAME_TOOLTIP: '未分類の名前は変更できません',
FT_SETTINGS_UNCATEGORIZED_DELETE_TOOLTIP: '未分類は削除できません',
FT_SETTINGS_CLOSE: '閉じる',
FT_SETTINGS_DELETE_BUTTON: '削除',
FT_SETTINGS_UP: '▲',
FT_SETTINGS_DOWN: '▼',
FT_SETTINGS_DISPLAY_SECTION_TITLE: '表示設定',
FT_SETTINGS_DISPLAY_MODE_LABEL: 'タグの表示形式',
FT_SETTINGS_DISPLAY_MODE_LEAF: '末尾のみ (leaf)',
FT_SETTINGS_DISPLAY_MODE_FULL: 'フルパス (full)',
FT_CONFIRM_DELETE_TAG_MSG: 'タグ「{tagName}」を削除しますか?\nこのタグが付いていたお気に入りは未分類になります。',
FT_SETTINGS_BUTTON_TITLE: 'お気に入りタグ設定',
},
'zh-CN': {
modalTitle: "高级搜索",
tooltipClose: "关闭",
labelAllWords: "包含全部词语",
placeholderAllWords: "例如:AI 新闻",
labelExactPhrase: "包含确切短语",
placeholderExactPhrase: '例如:"ChatGPT 4o"',
labelAnyWords: "包含任意词语 (OR)",
placeholderAnyWords: "例如:iPhone Android",
labelNotWords: "不包含词语 (-)",
placeholderNotWords: "例如:-促销 -广告",
labelHashtag: "话题标签 (#)",
placeholderHashtag: "例如:#科技大会",
labelLang: "语言 (lang:)",
optLangDefault: "所有语言",
optLangJa: "日语 (ja)",
optLangEn: "英语 (en)",
optLangId: "印尼语 (id)",
optLangHi: "印地语 (hi)",
optLangDe: "德语 (de)",
optLangTr: "土耳其语 (tr)",
optLangEs: "西班牙语 (es)",
optLangPt: "葡萄牙语 (pt)",
optLangAr: "阿拉伯语 (ar)",
optLangFr: "法语 (fr)",
optLangKo: "韩语 (ko)",
optLangRu: "俄语 (ru)",
optLangZhHans: "简体中文 (zh-cn)",
optLangZhHant: "繁体中文 (zh-tw)",
hrSeparator: " ",
labelFilters: "筛选",
labelVerified: "认证账号",
labelLinks: "链接",
labelImages: "图片",
labelVideos: "视频",
labelReposts: "转发",
labelTimelineHashtags: "话题标签 (#)",
checkInclude: "包含",
checkExclude: "排除",
labelReplies: "回复",
optRepliesDefault: "默认 (显示全部)",
optRepliesInclude: "包含回复",
optRepliesOnly: "仅回复",
optRepliesExclude: "排除回复",
labelEngagement: "互动量",
placeholderMinReplies: "最少回复",
placeholderMinLikes: "最少喜欢",
placeholderMinRetweets: "最少转发",
labelDateRange: "日期范围",
labelDateShortcut: "快速选择",
optDate1Day: "过去 24 小时",
optDate1Week: "过去 1 周",
optDate1Month: "过去 1 个月",
optDate3Months: "过去 3 个月",
optDate6Months: "过去 6 个月",
optDate1Year: "过去 1 年",
optDate2Years: "过去 2 年",
optDate3Years: "过去 3 年",
optDate5Years: "过去 5 年",
optDateClear: "清除日期",
tooltipSince: "起始日期",
tooltipUntil: "结束日期",
labelFromUser: "来自这些账号 (from:)",
placeholderFromUser: "例如:@X",
labelToUser: "发送给这些账号 (to:)",
placeholderToUser: "例如:@google",
labelMentioning: "提及这些账号 (@)",
placeholderMentioning: "例如:@OpenAI",
buttonClear: "清除",
buttonApply: "搜索",
tooltipTrigger: "打开高级搜索",
buttonOpen: "打开",
tabSearch: "搜索",
tabHistory: "历史",
tabSaved: "已保存",
buttonSave: "保存",
buttonSaved: "已保存",
secretMode: "无痕模式",
secretOn: "无痕模式已开启 (不记录历史)",
secretOff: "无痕模式已关闭",
toastSaved: "已保存。",
toastDeleted: "已删除。",
toastReordered: "顺序已更新。",
emptyHistory: "暂无历史记录。",
emptySaved: "暂无保存的搜索。请在搜索标签页左下角点击保存按钮添加。",
run: "运行",
delete: "删除",
updated: "已更新。",
tooltipSecret: "切换无痕模式 (不记录搜索历史)",
historyClearAll: "全部清除",
confirmClearHistory: "确定要清除所有历史记录吗?",
labelAccountScope: "账号范围",
optAccountAll: "所有账号",
optAccountFollowing: "关注的账号",
labelLocationScope: "位置范围",
optLocationAll: "所有位置",
optLocationNearby: "附近",
chipFollowing: "已关注",
chipNearby: "附近",
labelSearchTarget: "搜索目标",
labelHitName: "排除仅在显示名称中的匹配",
labelHitHandle: "排除仅在用户名 (@handle) 中的匹配",
hintSearchTarget: "隐藏仅在名称或用户名中匹配(而非正文)的帖子。",
hintName: "如果关键词仅出现在显示名称中,则隐藏。",
hintHandle: "如果关键词仅出现在 @用户名 中,则隐藏。例外:当查询中明确使用了 from:/to:/@ 时除外。",
tabMute: "屏蔽",
labelMuteWord: "添加屏蔽词",
placeholderMuteWord: "例如:剧透",
labelCaseSensitive: "区分大小写",
labelWordBoundary: "全字匹配",
labelEnabled: "已启用",
labelEnableAll: "全部启用",
buttonAdd: "添加",
emptyMuted: "暂无屏蔽词。",
mutedListTitle: "屏蔽词",
mutedListHeading: "屏蔽列表",
optMuteHidden: "隐藏",
optMuteCollapsed: "折叠",
placeholderFilterMute: "筛选屏蔽词...",
muteLabel: "已屏蔽: ",
buttonShow: "显示",
muteHit: "屏蔽正文匹配项",
buttonRemute: "重新屏蔽",
buttonImport: "导入",
buttonExport: "导出",
/* Accounts tab */
tabAccounts: "账号",
emptyAccounts: "暂无账号。请打开个人资料页并点击添加按钮进行保存。",
buttonAddAccount: "添加账号",
toastAccountAdded: "账号已添加。",
toastAccountExists: "已存在。",
buttonConfirm: "确认",
/* Lists tab */
tabLists: "列表",
emptyLists: "暂无列表。请打开列表页并点击右上角的 + 按钮添加。",
buttonAddList: "添加列表",
toastListAdded: "列表已添加。",
toastListExists: "已存在。",
/* History tab */
placeholderSearchHistory: "搜索历史 (查询词)",
labelSortBy: "排序:",
placeholderSearchSaved: "搜索已保存 (查询词)",
sortNewest: "最新",
sortOldest: "最旧",
sortNameAsc: "查询词 (A-Z)",
sortNameDesc: "查询词 (Z-A)",
/* Folder/List/Account tabs */
placeholderFilterAccounts: "筛选账号 (@, 名称)",
placeholderFilterLists: "筛选列表 (名称, URL)",
buttonAddFolder: "+文件夹",
folderFilterAll: "全部",
folderFilterUnassigned: "未分类",
folderRename: "重命名",
folderRenameTitle: "重命名文件夹",
folderDelete: "删除",
folderDeleteTitle: "删除文件夹",
promptNewFolder: "新文件夹名称",
confirmDeleteFolder: "确定要删除此文件夹及其内部所有项目吗?此操作无法撤销。",
optListsAll: "列表",
defaultSavedFolders: "已保存搜索",
/* Favorites */
tabFavorites: "收藏",
emptyFavorites: "暂无收藏的帖子。点击帖子上的 ★ 按钮进行保存。",
optFavoritesAll: "所有收藏",
toastFavorited: "已添加到收藏。",
toastUnfavorited: "已从收藏中移除。",
/* Settings */
settingsTitle: "设置",
settingsTitleGeneral: "通用",
settingsTitleFeatures: "标签显示",
settingsTitleData: "数据管理",
buttonClose: "关闭",
labelUILang: "界面语言",
optUILangAuto: "自动",
labelInitialTab: "启动时打开的标签页",
optInitialTabLast: "上次打开的标签页 (默认)",
labelImportExport: "导入 / 导出",
placeholderSettingsJSON: "请在此粘贴备份 JSON...",
tooltipSettings: "打开设置",
toastImported: "已导入。",
toastExported: "已导出到文件。",
alertInvalidJSON: "无效的 JSON 文件。",
alertInvalidData: "无效的数据格式。",
alertInvalidApp: '此文件不是 "Advanced Search for X" 的备份数据。',
buttonReset: "重置所有数据",
confirmResetAll: "确定要重置所有数据吗?此操作无法撤销。",
toastReset: "所有数据已重置。",
buttonImportSuccess: "导入成功 👍️",
/* Favorites Sort */
sortSavedNewest: "保存日期 (最新)",
sortSavedOldest: "保存日期 (最旧)",
sortPostedNewest: "发布日期 (最新)",
sortPostedOldest: "发布日期 (最旧)",
/* --- Favorite Tags --- */
FT_UNCATEGORIZED: '未分类',
FT_DROPDOWN_TITLE: '收藏标签',
FT_DROPDOWN_SETTINGS_TITLE: '收藏标签设置',
FT_DROPDOWN_NEW_TAG: '新建标签',
FT_DROPDOWN_NEW_TAG_PLACEHOLDER: '标签名称',
FT_DROPDOWN_NEW_TAG_ADD: '添加',
FT_FILTER_ALL: '全部',
FT_SETTINGS_TITLE: '收藏标签设置',
FT_SETTINGS_EMPTY_TAG_LIST: '暂无标签。您可以从“新建标签”添加。',
FT_SETTINGS_UNCATEGORIZED_NAME: '未分类',
FT_SETTINGS_UNCATEGORIZED_NAME_TOOLTIP: '“未分类”的名称无法更改。',
FT_SETTINGS_UNCATEGORIZED_DELETE_TOOLTIP: '“未分类”无法被删除。',
FT_SETTINGS_CLOSE: '关闭',
FT_SETTINGS_DELETE_BUTTON: '删除',
FT_SETTINGS_UP: '▲',
FT_SETTINGS_DOWN: '▼',
FT_SETTINGS_DISPLAY_SECTION_TITLE: '显示设置',
FT_SETTINGS_DISPLAY_MODE_LABEL: '标签显示格式',
FT_SETTINGS_DISPLAY_MODE_LEAF: '仅末级 (leaf)',
FT_SETTINGS_DISPLAY_MODE_FULL: '完整路径 (full)',
FT_CONFIRM_DELETE_TAG_MSG: '确定要删除标签“{tagName}”吗?\n带有此标签的收藏将变为“未分类”。',
FT_SETTINGS_BUTTON_TITLE: '收藏标签设置',
},
'zh-TW': {
modalTitle: "進階搜尋",
tooltipClose: "關閉",
labelAllWords: "包含所有詞語",
placeholderAllWords: "例如:AI 新聞",
labelExactPhrase: "包含精確詞組",
placeholderExactPhrase: '例如:"ChatGPT 4o"',
labelAnyWords: "包含任一詞語 (OR)",
placeholderAnyWords: "例如:iPhone Android",
labelNotWords: "排除詞語 (-)",
placeholderNotWords: "例如:-促銷 -廣告",
labelHashtag: "主題標籤 (#)",
placeholderHashtag: "例如:#科技大會",
labelLang: "語言 (lang:)",
optLangDefault: "所有語言",
optLangJa: "日語 (ja)",
optLangEn: "英語 (en)",
optLangId: "印尼語 (id)",
optLangHi: "印地語 (hi)",
optLangDe: "德語 (de)",
optLangTr: "土耳其語 (tr)",
optLangEs: "西班牙語 (es)",
optLangPt: "葡萄牙語 (pt)",
optLangAr: "阿拉伯語 (ar)",
optLangFr: "法語 (fr)",
optLangKo: "韓語 (ko)",
optLangRu: "俄語 (ru)",
optLangZhHans: "簡體中文 (zh-cn)",
optLangZhHant: "繁體中文 (zh-tw)",
hrSeparator: " ",
labelFilters: "篩選",
labelVerified: "已認證帳號",
labelLinks: "連結",
labelImages: "圖片",
labelVideos: "影片",
labelReposts: "轉發",
labelTimelineHashtags: "主題標籤 (#)",
checkInclude: "包含",
checkExclude: "排除",
labelReplies: "回覆",
optRepliesDefault: "預設 (顯示全部)",
optRepliesInclude: "包含回覆",
optRepliesOnly: "僅限回覆",
optRepliesExclude: "排除回覆",
labelEngagement: "互動量",
placeholderMinReplies: "最少回覆",
placeholderMinLikes: "最少喜歡",
placeholderMinRetweets: "最少轉發",
labelDateRange: "日期範圍",
labelDateShortcut: "快速範圍",
optDate1Day: "過去 24 小時",
optDate1Week: "過去 1 週",
optDate1Month: "過去 1 個月",
optDate3Months: "過去 3 個月",
optDate6Months: "過去 6 個月",
optDate1Year: "過去 1 年",
optDate2Years: "過去 2 年",
optDate3Years: "過去 3 年",
optDate5Years: "過去 5 年",
optDateClear: "清除日期",
tooltipSince: "開始日期",
tooltipUntil: "結束日期",
labelFromUser: "來自這些帳號 (from:)",
placeholderFromUser: "例如:@X",
labelToUser: "發送給這些帳號 (to:)",
placeholderToUser: "例如:@google",
labelMentioning: "提及這些帳號 (@)",
placeholderMentioning: "例如:@OpenAI",
buttonClear: "清除",
buttonApply: "搜尋",
tooltipTrigger: "打開進階搜尋",
buttonOpen: "打開",
tabSearch: "搜尋",
tabHistory: "紀錄",
tabSaved: "已儲存",
buttonSave: "儲存",
buttonSaved: "已儲存",
secretMode: "無痕模式",
secretOn: "無痕模式已開啟 (不記錄歷史)",
secretOff: "無痕模式已關閉",
toastSaved: "已儲存。",
toastDeleted: "已刪除。",
toastReordered: "順序已更新。",
emptyHistory: "暫無搜尋紀錄。",
emptySaved: "暫無儲存的搜尋。請在搜尋分頁左下角點擊儲存按鈕添加。",
run: "執行",
delete: "刪除",
updated: "已更新。",
tooltipSecret: "切換無痕模式 (不記錄搜尋歷史)",
historyClearAll: "全部清除",
confirmClearHistory: "確定要清除所有搜尋紀錄嗎?",
labelAccountScope: "帳號範圍",
optAccountAll: "所有帳號",
optAccountFollowing: "跟隨的帳號",
labelLocationScope: "位置範圍",
optLocationAll: "所有位置",
optLocationNearby: "附近",
chipFollowing: "正在跟隨",
chipNearby: "附近",
labelSearchTarget: "搜尋目標",
labelHitName: "排除僅在顯示名稱中的相符項目",
labelHitHandle: "排除僅在使用者名稱 (@handle) 中的相符項目",
hintSearchTarget: "隱藏僅在名稱或使用者名稱中相符(而非內文)的貼文。",
hintName: "如果關鍵字僅出現在顯示名稱中,則隱藏。",
hintHandle: "如果關鍵字僅出現在 @使用者名稱 中,則隱藏。例外:當查詢中明確使用了 from:/to:/@ 時除外。",
tabMute: "靜音",
labelMuteWord: "新增靜音詞彙",
placeholderMuteWord: "例如:劇透",
labelCaseSensitive: "區分大小寫",
labelWordBoundary: "全字匹配",
labelEnabled: "已啟用",
labelEnableAll: "全部啟用",
buttonAdd: "新增",
emptyMuted: "暫無靜音詞彙。",
mutedListTitle: "靜音詞彙",
mutedListHeading: "靜音清單",
optMuteHidden: "隱藏",
optMuteCollapsed: "收合",
placeholderFilterMute: "篩選靜音詞彙...",
muteLabel: "已靜音: ",
buttonShow: "顯示",
muteHit: "靜音內文相符項目",
buttonRemute: "重新靜音",
buttonImport: "匯入",
buttonExport: "匯出",
/* Accounts tab */
tabAccounts: "帳號",
emptyAccounts: "暫無帳號。請打開個人檔案頁面並點擊新增按鈕進行儲存。",
buttonAddAccount: "新增帳號",
toastAccountAdded: "帳號已新增。",
toastAccountExists: "已存在。",
buttonConfirm: "確認",
/* Lists tab */
tabLists: "列表",
emptyLists: "暫無列表。請打開列表頁並點擊右上角的 + 按鈕新增。",
buttonAddList: "新增列表",
toastListAdded: "列表已新增。",
toastListExists: "已存在。",
/* History tab */
placeholderSearchHistory: "搜尋紀錄 (關鍵字)",
labelSortBy: "排序:",
placeholderSearchSaved: "搜尋已儲存 (關鍵字)",
sortNewest: "最新",
sortOldest: "最舊",
sortNameAsc: "關鍵字 (A-Z)",
sortNameDesc: "關鍵字 (Z-A)",
/* Folder/List/Account tabs */
placeholderFilterAccounts: "篩選帳號 (@, 名稱)",
placeholderFilterLists: "篩選列表 (名稱, URL)",
buttonAddFolder: "+資料夾",
folderFilterAll: "全部",
folderFilterUnassigned: "未分類",
folderRename: "重新命名",
folderRenameTitle: "重新命名資料夾",
folderDelete: "刪除",
folderDeleteTitle: "刪除資料夾",
promptNewFolder: "新資料夾名稱",
confirmDeleteFolder: "確定要刪除此資料夾及其內部所有項目嗎?此操作無法復原。",
optListsAll: "列表",
defaultSavedFolders: "已儲存的搜尋",
/* Favorites */
tabFavorites: "收藏",
emptyFavorites: "暫無收藏的貼文。點擊貼文上的 ★ 按鈕進行儲存。",
optFavoritesAll: "所有收藏",
toastFavorited: "已加入收藏。",
toastUnfavorited: "已從收藏中移除。",
/* Settings */
settingsTitle: "設定",
settingsTitleGeneral: "一般",
settingsTitleFeatures: "標籤顯示",
settingsTitleData: "資料管理",
buttonClose: "關閉",
labelUILang: "介面語言",
optUILangAuto: "自動",
labelInitialTab: "啟動時開啟的分頁",
optInitialTabLast: "上次開啟的分頁 (預設)",
labelImportExport: "匯入 / 匯出",
placeholderSettingsJSON: "請在此貼上備份 JSON...",
tooltipSettings: "打開設定",
toastImported: "已匯入。",
toastExported: "已匯出至檔案。",
alertInvalidJSON: "無效的 JSON 檔案。",
alertInvalidData: "無效的資料格式。",
alertInvalidApp: '此檔案不是 "Advanced Search for X" 的備份資料。',
buttonReset: "重設所有資料",
confirmResetAll: "確定要重設所有資料嗎?此操作無法復原。",
toastReset: "所有資料已重設。",
buttonImportSuccess: "匯入成功 👍️",
/* Favorites Sort */
sortSavedNewest: "儲存日期 (最新)",
sortSavedOldest: "儲存日期 (最舊)",
sortPostedNewest: "發布日期 (最新)",
sortPostedOldest: "發布日期 (最舊)",
/* --- Favorite Tags --- */
FT_UNCATEGORIZED: '未分類',
FT_DROPDOWN_TITLE: '收藏標籤',
FT_DROPDOWN_SETTINGS_TITLE: '收藏標籤設定',
FT_DROPDOWN_NEW_TAG: '新建標籤',
FT_DROPDOWN_NEW_TAG_PLACEHOLDER: '標籤名稱',
FT_DROPDOWN_NEW_TAG_ADD: '新增',
FT_FILTER_ALL: '全部',
FT_SETTINGS_TITLE: '收藏標籤設定',
FT_SETTINGS_EMPTY_TAG_LIST: '暫無標籤。您可以從「新建標籤」新增。',
FT_SETTINGS_UNCATEGORIZED_NAME: '未分類',
FT_SETTINGS_UNCATEGORIZED_NAME_TOOLTIP: '「未分類」的名稱無法更改。',
FT_SETTINGS_UNCATEGORIZED_DELETE_TOOLTIP: '「未分類」無法被刪除。',
FT_SETTINGS_CLOSE: '關閉',
FT_SETTINGS_DELETE_BUTTON: '刪除',
FT_SETTINGS_UP: '▲',
FT_SETTINGS_DOWN: '▼',
FT_SETTINGS_DISPLAY_SECTION_TITLE: '顯示設定',
FT_SETTINGS_DISPLAY_MODE_LABEL: '標籤顯示格式',
FT_SETTINGS_DISPLAY_MODE_LEAF: '僅末級 (leaf)',
FT_SETTINGS_DISPLAY_MODE_FULL: '完整路徑 (full)',
FT_CONFIRM_DELETE_TAG_MSG: '確定要刪除標籤「{tagName}」嗎?\n帶有此標籤的收藏將變為「未分類」。',
FT_SETTINGS_BUTTON_TITLE: '收藏標籤設定',
},
'ko': {
modalTitle: "고급 검색",
tooltipClose: "닫기",
labelAllWords: "다음 단어 모두 포함",
placeholderAllWords: "예: AI 뉴스",
labelExactPhrase: "정확하게 일치하는 문구",
placeholderExactPhrase: '예: "ChatGPT 4o"',
labelAnyWords: "다음 단어 중 하나라도 포함 (OR)",
placeholderAnyWords: "예: iPhone Android",
labelNotWords: "다음 단어 제외 (-)",
placeholderNotWords: "예: -세일 -광고",
labelHashtag: "해시태그 (#)",
placeholderHashtag: "예: #개발자",
labelLang: "언어 (lang:)",
optLangDefault: "모든 언어",
optLangJa: "일본어 (ja)",
optLangEn: "영어 (en)",
optLangId: "인도네시아어 (id)",
optLangHi: "힌디어 (hi)",
optLangDe: "독일어 (de)",
optLangTr: "튀르키예어 (tr)",
optLangEs: "스페인어 (es)",
optLangPt: "포르투갈어 (pt)",
optLangAr: "아랍어 (ar)",
optLangFr: "프랑스어 (fr)",
optLangKo: "한국어 (ko)",
optLangRu: "러시아어 (ru)",
optLangZhHans: "중국어 간체 (zh-cn)",
optLangZhHant: "중국어 번체 (zh-tw)",
hrSeparator: " ",
labelFilters: "필터",
labelVerified: "인증된 계정",
labelLinks: "링크",
labelImages: "이미지",
labelVideos: "동영상",
labelReposts: "재게시",
labelTimelineHashtags: "해시태그 (#)",
checkInclude: "포함",
checkExclude: "제외",
labelReplies: "답글",
optRepliesDefault: "기본 (모두 표시)",
optRepliesInclude: "답글 포함",
optRepliesOnly: "답글만",
optRepliesExclude: "답글 제외",
labelEngagement: "참여",
placeholderMinReplies: "최소 답글 수",
placeholderMinLikes: "최소 마음에 들어요 수",
placeholderMinRetweets: "최소 재게시 수",
labelDateRange: "날짜 범위",
labelDateShortcut: "빠른 범위 설정",
optDate1Day: "지난 24시간",
optDate1Week: "지난 1주",
optDate1Month: "지난 1개월",
optDate3Months: "지난 3개월",
optDate6Months: "지난 6개월",
optDate1Year: "지난 1년",
optDate2Years: "지난 2년",
optDate3Years: "지난 3년",
optDate5Years: "지난 5년",
optDateClear: "날짜 초기화",
tooltipSince: "시작일",
tooltipUntil: "종료일",
labelFromUser: "다음 계정에서 (from:)",
placeholderFromUser: "예: @X",
labelToUser: "다음 계정으로 (to:)",
placeholderToUser: "예: @google",
labelMentioning: "다음 계정 언급 (@)",
placeholderMentioning: "예: @OpenAI",
buttonClear: "지우기",
buttonApply: "검색",
tooltipTrigger: "고급 검색 열기",
buttonOpen: "열기",
tabSearch: "검색",
tabHistory: "기록",
tabSaved: "저장됨",
buttonSave: "저장",
buttonSaved: "저장됨",
secretMode: "시크릿 모드",
secretOn: "시크릿 모드 켜짐 (기록되지 않음)",
secretOff: "시크릿 모드 꺼짐",
toastSaved: "저장되었습니다.",
toastDeleted: "삭제되었습니다.",
toastReordered: "순서가 업데이트되었습니다.",
emptyHistory: "기록이 없습니다.",
emptySaved: "저장된 검색이 없습니다. 검색 탭 왼쪽 하단의 저장 버튼으로 추가하세요.",
run: "실행",
delete: "삭제",
updated: "업데이트됨.",
tooltipSecret: "시크릿 모드 전환 (검색 기록을 저장하지 않음)",
historyClearAll: "모두 지우기",
confirmClearHistory: "모든 기록을 삭제하시겠습니까?",
labelAccountScope: "계정 범위",
optAccountAll: "모든 계정",
optAccountFollowing: "팔로우 중인 계정",
labelLocationScope: "위치 범위",
optLocationAll: "모든 위치",
optLocationNearby: "내 주변",
chipFollowing: "팔로잉",
chipNearby: "주변",
labelSearchTarget: "검색 대상",
labelHitName: "표시 이름(닉네임)만 일치하는 결과 제외",
labelHitHandle: "사용자 아이디(@handle)만 일치하는 결과 제외",
hintSearchTarget: "본문이 아닌 이름/아이디만 일치하는 게시물을 숨깁니다.",
hintName: "키워드가 표시 이름에만 포함된 경우 숨깁니다.",
hintHandle: "키워드가 @아이디에만 포함된 경우 숨깁니다. 예외: 검색어에 from:/to:/@ 등으로 명시한 경우는 표시합니다.",
tabMute: "뮤트",
labelMuteWord: "뮤트 단어 추가",
placeholderMuteWord: "예: 스포일러",
labelCaseSensitive: "대소문자 구분",
labelWordBoundary: "단어 단위",
labelEnabled: "활성화",
labelEnableAll: "모두 활성화",
buttonAdd: "추가",
emptyMuted: "뮤트된 단어가 없습니다.",
mutedListTitle: "뮤트 단어",
mutedListHeading: "뮤트 목록",
optMuteHidden: "숨기기",
optMuteCollapsed: "접기",
placeholderFilterMute: "뮤트 단어 검색...",
muteLabel: "뮤트됨: ",
buttonShow: "표시",
muteHit: "본문 일치 항목 뮤트",
buttonRemute: "다시 뮤트",
buttonImport: "가져오기",
buttonExport: "내보내기",
/* Accounts tab */
tabAccounts: "계정",
emptyAccounts: "저장된 계정이 없습니다. 프로필 페이지를 열고 추가 버튼을 눌러 저장하세요.",
buttonAddAccount: "계정 추가",
toastAccountAdded: "계정이 추가되었습니다.",
toastAccountExists: "이미 추가되었습니다.",
buttonConfirm: "확인",
/* Lists tab */
tabLists: "리스트",
emptyLists: "저장된 리스트가 없습니다. 리스트를 열고 우측 상단의 + 버튼을 눌러 추가하세요.",
buttonAddList: "리스트 추가",
toastListAdded: "리스트가 추가되었습니다.",
toastListExists: "이미 추가되었습니다.",
/* History tab */
placeholderSearchHistory: "기록 검색 (검색어)",
labelSortBy: "정렬:",
placeholderSearchSaved: "저장된 항목 검색 (검색어)",
sortNewest: "최신순",
sortOldest: "오래된순",
sortNameAsc: "이름순 (ㄱ-ㅎ)",
sortNameDesc: "이름순 (ㅎ-ㄱ)",
/* Folder/List/Account tabs */
placeholderFilterAccounts: "계정 필터링 (@, 이름)",
placeholderFilterLists: "리스트 필터링 (이름, URL)",
buttonAddFolder: "+폴더",
folderFilterAll: "전체",
folderFilterUnassigned: "미분류",
folderRename: "이름 변경",
folderRenameTitle: "폴더 이름 변경",
folderDelete: "삭제",
folderDeleteTitle: "폴더 삭제",
promptNewFolder: "새 폴더 이름",
confirmDeleteFolder: "이 폴더와 내부의 모든 항목을 삭제하시겠습니까? 이 작업은 되돌릴 수 없습니다.",
optListsAll: "리스트",
defaultSavedFolders: "저장된 검색",
/* Favorites */
tabFavorites: "즐겨찾기",
emptyFavorites: "즐겨찾기에 추가한 게시물이 없습니다. 게시물의 ★ 버튼을 눌러 저장하세요.",
optFavoritesAll: "모든 즐겨찾기",
toastFavorited: "즐겨찾기에 추가했습니다.",
toastUnfavorited: "즐겨찾기에서 삭제했습니다.",
/* Settings */
settingsTitle: "설정",
settingsTitleGeneral: "일반",
settingsTitleFeatures: "탭 표시 설정",
settingsTitleData: "데이터 관리",
buttonClose: "닫기",
labelUILang: "UI 언어",
optUILangAuto: "자동",
labelInitialTab: "시작 시 열 탭",
optInitialTabLast: "마지막에 연 탭 (기본)",
labelImportExport: "가져오기 / 내보내기",
placeholderSettingsJSON: "백업 JSON을 여기에 붙여넣으세요...",
tooltipSettings: "설정 열기",
toastImported: "가져오기가 완료되었습니다.",
alertInvalidJSON: "유효하지 않은 JSON 파일입니다.",
alertInvalidData: "유효하지 않은 데이터 형식입니다.",
alertInvalidApp: '"Advanced Search for X"의 백업 파일이 아닙니다.',
toastExported: "파일로 내보냈습니다.",
buttonReset: "모든 데이터 초기화",
confirmResetAll: "모든 데이터를 초기화하시겠습니까? 이 작업은 되돌릴 수 없습니다.",
toastReset: "모든 데이터가 초기화되었습니다.",
buttonImportSuccess: "가져오기 성공 👍️",
/* Favorites Sort */
sortSavedNewest: "저장일 (최신순)",
sortSavedOldest: "저장일 (오래된순)",
sortPostedNewest: "게시일 (최신순)",
sortPostedOldest: "게시일 (오래된순)",
/* --- Favorite Tags --- */
FT_UNCATEGORIZED: '미분류',
FT_DROPDOWN_TITLE: '태그',
FT_DROPDOWN_SETTINGS_TITLE: '태그 설정',
FT_DROPDOWN_NEW_TAG: '새 태그',
FT_DROPDOWN_NEW_TAG_PLACEHOLDER: '태그 이름',
FT_DROPDOWN_NEW_TAG_ADD: '추가',
FT_FILTER_ALL: '전체',
FT_SETTINGS_TITLE: '태그 설정',
FT_SETTINGS_EMPTY_TAG_LIST:
'태그가 없습니다. "새 태그"에서 추가할 수 있습니다.',
FT_SETTINGS_UNCATEGORIZED_NAME: '미분류',
FT_SETTINGS_UNCATEGORIZED_NAME_TOOLTIP:
'"미분류" 이름은 변경할 수 없습니다.',
FT_SETTINGS_UNCATEGORIZED_DELETE_TOOLTIP:
'"미분류"는 삭제할 수 없습니다.',
FT_SETTINGS_CLOSE: '닫기',
FT_SETTINGS_DELETE_BUTTON: '삭제',
FT_SETTINGS_UP: '▲',
FT_SETTINGS_DOWN: '▼',
FT_SETTINGS_DISPLAY_SECTION_TITLE: '표시',
FT_SETTINGS_DISPLAY_MODE_LABEL: '태그 표시 형식',
FT_SETTINGS_DISPLAY_MODE_LEAF: '말단만 (leaf)',
FT_SETTINGS_DISPLAY_MODE_FULL: '전체 경로 (full)',
FT_CONFIRM_DELETE_TAG_MSG: '태그 "{tagName}"을(를) 삭제하시겠습니까?\n이 태그가 지정된 항목은 "미분류"가 됩니다.',
FT_SETTINGS_BUTTON_TITLE: '태그 설정',
},
'fr': {
modalTitle: "Recherche avancée",
tooltipClose: "Fermer",
labelAllWords: "Tous ces mots",
placeholderAllWords: "ex: AI actualités",
labelExactPhrase: "Cette phrase exacte",
placeholderExactPhrase: 'ex: "ChatGPT 4o"',
labelAnyWords: "L'un de ces mots (OR)",
placeholderAnyWords: "ex: iPhone Android",
labelNotWords: "Aucun de ces mots (-)",
placeholderNotWords: "ex: -soldes -pub",
labelHashtag: "Hashtags (#)",
placeholderHashtag: "ex: #Paris2024",
labelLang: "Langue (lang:)",
optLangDefault: "Toutes les langues",
optLangJa: "Japonais (ja)",
optLangEn: "Anglais (en)",
optLangId: "Indonésien (id)",
optLangHi: "Hindi (hi)",
optLangDe: "Allemand (de)",
optLangTr: "Turc (tr)",
optLangEs: "Espagnol (es)",
optLangPt: "Portugais (pt)",
optLangAr: "Arabe (ar)",
optLangFr: "Français (fr)",
optLangKo: "Coréen (ko)",
optLangRu: "Russe (ru)",
optLangZhHans: "Chinois simplifié (zh-cn)",
optLangZhHant: "Chinois traditionnel (zh-tw)",
hrSeparator: " ",
labelFilters: "Filtres",
labelVerified: "Comptes certifiés",
labelLinks: "Liens",
labelImages: "Images",
labelVideos: "Vidéos",
labelReposts: "Republications",
labelTimelineHashtags: "Hashtags (#)",
checkInclude: "Inclure",
checkExclude: "Exclure",
labelReplies: "Réponses",
optRepliesDefault: "Par défaut (Tout)",
optRepliesInclude: "Inclure les réponses",
optRepliesOnly: "Réponses uniquement",
optRepliesExclude: "Exclure les réponses",
labelEngagement: "Engagement",
placeholderMinReplies: "Min réponses",
placeholderMinLikes: "Min J'aime",
placeholderMinRetweets: "Min republications",
labelDateRange: "Période",
labelDateShortcut: "Plage rapide",
optDate1Day: "Dernières 24h",
optDate1Week: "Semaine dernière",
optDate1Month: "Mois dernier",
optDate3Months: "3 derniers mois",
optDate6Months: "6 derniers mois",
optDate1Year: "Année dernière",
optDate2Years: "2 dernières années",
optDate3Years: "3 dernières années",
optDate5Years: "5 dernières années",
optDateClear: "Effacer les dates",
tooltipSince: "Depuis cette date",
tooltipUntil: "Jusqu'à cette date",
labelFromUser: "De ces comptes (from:)",
placeholderFromUser: "ex: @X",
labelToUser: "À ces comptes (to:)",
placeholderToUser: "ex: @google",
labelMentioning: "Mentionnant ces comptes (@)",
placeholderMentioning: "ex: @OpenAI",
buttonClear: "Effacer",
buttonApply: "Rechercher",
tooltipTrigger: "Ouvrir la recherche avancée",
buttonOpen: "Ouvrir",
tabSearch: "Recherche",
tabHistory: "Historique",
tabSaved: "Enregistré",
buttonSave: "Enregistrer",
buttonSaved: "Enregistré",
secretMode: "Mode privé",
secretOn: "Mode privé activé (Pas d'historique)",
secretOff: "Mode privé désactivé",
toastSaved: "Enregistré.",
toastDeleted: "Supprimé.",
toastReordered: "Ordre mis à jour.",
emptyHistory: "Aucun historique.",
emptySaved: "Aucune recherche enregistrée. Ajoutez-en via le bouton Enregistrer en bas à gauche de l'onglet Recherche.",
run: "Lancer",
delete: "Supprimer",
updated: "Mis à jour.",
tooltipSecret: "Basculer le mode privé (aucun historique ne sera enregistré)",
historyClearAll: "Tout effacer",
confirmClearHistory: "Effacer tout l'historique ?",
labelAccountScope: "Comptes",
optAccountAll: "Tous les comptes",
optAccountFollowing: "Comptes suivis",
labelLocationScope: "Lieu",
optLocationAll: "Tous les lieux",
optLocationNearby: "Proche de vous",
chipFollowing: "Abonnements",
chipNearby: "À proximité",
labelSearchTarget: "Cible de la recherche",
labelHitName: "Exclure les résultats dans le nom d'affichage",
labelHitHandle: "Exclure les résultats dans le nom d'utilisateur (@)",
hintSearchTarget: "Masquer les posts qui ne correspondent que par le nom ou l'identifiant (pas dans le texte).",
hintName: "Si un mot-clé n'apparaît que dans le nom d'affichage, le masquer.",
hintHandle: "Si un mot-clé n'apparaît que dans le @nom_utilisateur, le masquer. Exception : si la requête utilise explicitement from:/to:/@.",
tabMute: "Masquer",
labelMuteWord: "Ajouter un mot masqué",
placeholderMuteWord: "ex: spoiler",
labelCaseSensitive: "Sensible à la casse",
labelWordBoundary: "Mot entier",
labelEnabled: "Activé",
labelEnableAll: "Tout activer",
buttonAdd: "Ajouter",
emptyMuted: "Aucun mot masqué.",
mutedListTitle: "Mots masqués",
mutedListHeading: "Liste masquée",
optMuteHidden: "Masqué",
optMuteCollapsed: "Réduit",
placeholderFilterMute: "Filtrer les mots masqués...",
muteLabel: "Masqué : ",
buttonShow: "Afficher",
muteHit: "Masquer les résultats dans le texte",
buttonRemute: "Masquer à nouveau",
buttonImport: "Importer",
buttonExport: "Exporter",
/* Accounts tab */
tabAccounts: "Comptes",
emptyAccounts: "Aucun compte. Ouvrez un profil et cliquez sur le bouton Ajouter pour l'enregistrer.",
buttonAddAccount: "Ajouter compte",
toastAccountAdded: "Compte ajouté.",
toastAccountExists: "Déjà ajouté.",
buttonConfirm: "Confirmer",
/* Lists tab */
tabLists: "Listes",
emptyLists: "Aucune liste. Ouvrez une liste et cliquez sur le bouton + en haut à droite pour l'ajouter.",
buttonAddList: "Ajouter liste",
toastListAdded: "Liste ajoutée.",
toastListExists: "Déjà ajoutée.",
/* History tab */
placeholderSearchHistory: "Historique (requête)",
labelSortBy: "Trier par :",
placeholderSearchSaved: "Recherches enregistrées (requête)",
sortNewest: "Plus récent",
sortOldest: "Plus ancien",
sortNameAsc: "Nom (A-Z)",
sortNameDesc: "Nom (Z-A)",
/* Folder/List/Account tabs */
placeholderFilterAccounts: "Filtrer comptes (@, nom)",
placeholderFilterLists: "Filtrer listes (nom, url)",
buttonAddFolder: "+Dossier",
folderFilterAll: "TOUT",
folderFilterUnassigned: "Non classé",
folderRename: "Renommer",
folderRenameTitle: "Renommer le dossier",
folderDelete: "Supprimer",
folderDeleteTitle: "Supprimer le dossier",
promptNewFolder: "Nom du dossier",
confirmDeleteFolder: "Supprimer ce dossier et tout son contenu ? Cette action est irréversible.",
optListsAll: "Listes",
defaultSavedFolders: "Recherches enregistrées",
/* Favorites */
tabFavorites: "Favoris",
emptyFavorites: "Aucun favori. Cliquez sur l'icône ★ d'un tweet pour l'enregistrer.",
optFavoritesAll: "Tous les favoris",
toastFavorited: "Ajouté aux favoris.",
toastUnfavorited: "Retiré des favoris.",
/* Settings */
settingsTitle: "Paramètres",
settingsTitleGeneral: "Général",
settingsTitleFeatures: "Affichage onglets",
settingsTitleData: "Données",
buttonClose: "Fermer",
labelUILang: "Langue de l'interface",
optUILangAuto: "Auto",
labelInitialTab: "Onglet au démarrage",
optInitialTabLast: "Dernier ouvert (Défaut)",
labelImportExport: "Importer / Exporter",
placeholderSettingsJSON: "Collez le JSON de sauvegarde ici...",
tooltipSettings: "Ouvrir les paramètres",
toastImported: "Importé.",
toastExported: "Exporté vers un fichier.",
alertInvalidJSON: "Fichier JSON invalide.",
alertInvalidData: "Format de données invalide.",
alertInvalidApp: 'Ce fichier n\'est pas une sauvegarde valide pour "Advanced Search for X".',
buttonReset: "Réinitialiser tout",
confirmResetAll: "Tout réinitialiser ? Cette action est irréversible.",
toastReset: "Toutes les données ont été réinitialisées.",
buttonImportSuccess: "Importation réussie 👍️",
/* Favorites Sort */
sortSavedNewest: "Date d'ajout (Récent)",
sortSavedOldest: "Date d'ajout (Ancien)",
sortPostedNewest: "Date de publication (Récent)",
sortPostedOldest: "Date de publication (Ancien)",
/* --- Favorite Tags --- */
FT_UNCATEGORIZED: 'Non classé',
FT_DROPDOWN_TITLE: 'Tags favoris',
FT_DROPDOWN_SETTINGS_TITLE: 'Réglages des tags',
FT_DROPDOWN_NEW_TAG: 'Nouveau tag',
FT_DROPDOWN_NEW_TAG_PLACEHOLDER: 'Nom du tag',
FT_DROPDOWN_NEW_TAG_ADD: 'Ajouter',
FT_FILTER_ALL: 'Tout',
FT_SETTINGS_TITLE: 'Réglages des tags favoris',
FT_SETTINGS_EMPTY_TAG_LIST: 'Aucun tag. Ajoutez-en un depuis "Nouveau tag".',
FT_SETTINGS_UNCATEGORIZED_NAME: 'Non classé',
FT_SETTINGS_UNCATEGORIZED_NAME_TOOLTIP: 'Le nom "Non classé" ne peut pas être modifié.',
FT_SETTINGS_UNCATEGORIZED_DELETE_TOOLTIP: '"Non classé" ne peut pas être supprimé.',
FT_SETTINGS_CLOSE: 'Fermer',
FT_SETTINGS_DELETE_BUTTON: 'Supprimer',
FT_SETTINGS_UP: '▲',
FT_SETTINGS_DOWN: '▼',
FT_SETTINGS_DISPLAY_SECTION_TITLE: 'Affichage',
FT_SETTINGS_DISPLAY_MODE_LABEL: 'Format du tag',
FT_SETTINGS_DISPLAY_MODE_LEAF: 'Libellé seul (leaf)',
FT_SETTINGS_DISPLAY_MODE_FULL: 'Chemin complet',
FT_CONFIRM_DELETE_TAG_MSG: 'Supprimer le tag "{tagName}" ?\nLes favoris associés deviendront "Non classé".',
FT_SETTINGS_BUTTON_TITLE: 'Réglages des tags',
},
'es': {
modalTitle: "Búsqueda avanzada",
tooltipClose: "Cerrar",
labelAllWords: "Todas estas palabras",
placeholderAllWords: "ej. AI noticias",
labelExactPhrase: "Esta frase exacta",
placeholderExactPhrase: 'ej. "ChatGPT 4o"',
labelAnyWords: "Cualquiera de estas palabras (OR)",
placeholderAnyWords: "ej. iPhone Android",
labelNotWords: "Ninguna de estas palabras (-)",
placeholderNotWords: "ej. -oferta -anuncio",
labelHashtag: "Hashtags (#)",
placeholderHashtag: "ej. #Tecnología",
labelLang: "Idioma (lang:)",
optLangDefault: "Cualquier idioma",
optLangJa: "Japonés (ja)",
optLangEn: "Inglés (en)",
optLangId: "Indonesio (id)",
optLangHi: "Hindi (hi)",
optLangDe: "Alemán (de)",
optLangTr: "Turco (tr)",
optLangEs: "Español (es)",
optLangPt: "Portugués (pt)",
optLangAr: "Árabe (ar)",
optLangFr: "Francés (fr)",
optLangKo: "Coreano (ko)",
optLangRu: "Ruso (ru)",
optLangZhHans: "Chino simplificado (zh-cn)",
optLangZhHant: "Chino tradicional (zh-tw)",
hrSeparator: " ",
labelFilters: "Filtros",
labelVerified: "Cuentas verificadas",
labelLinks: "Enlaces",
labelImages: "Imágenes",
labelVideos: "Vídeos",
labelReposts: "Reposts",
labelTimelineHashtags: "Hashtags (#)",
checkInclude: "Incluir",
checkExclude: "Excluir",
labelReplies: "Respuestas",
optRepliesDefault: "Por defecto (Todo)",
optRepliesInclude: "Incluir respuestas",
optRepliesOnly: "Solo respuestas",
optRepliesExclude: "Excluir respuestas",
labelEngagement: "Interacciones",
placeholderMinReplies: "Mín. respuestas",
placeholderMinLikes: "Mín. Me gusta",
placeholderMinRetweets: "Mín. reposts",
labelDateRange: "Rango de fechas",
labelDateShortcut: "Rango rápido",
optDate1Day: "Últimas 24 horas",
optDate1Week: "Última semana",
optDate1Month: "Último mes",
optDate3Months: "Últimos 3 meses",
optDate6Months: "Últimos 6 meses",
optDate1Year: "Último año",
optDate2Years: "Últimos 2 años",
optDate3Years: "Últimos 3 años",
optDate5Years: "Últimos 5 años",
optDateClear: "Borrar fechas",
tooltipSince: "Desde esta fecha",
tooltipUntil: "Hasta esta fecha",
labelFromUser: "De estas cuentas (from:)",
placeholderFromUser: "ej. @X",
labelToUser: "Para estas cuentas (to:)",
placeholderToUser: "ej. @google",
labelMentioning: "Mencionando a estas cuentas (@)",
placeholderMentioning: "ej. @OpenAI",
buttonClear: "Borrar",
buttonApply: "Buscar",
tooltipTrigger: "Abrir búsqueda avanzada",
buttonOpen: "Abrir",
tabSearch: "Búsqueda",
tabHistory: "Historial",
tabSaved: "Guardado",
buttonSave: "Guardar",
buttonSaved: "Guardado",
secretMode: "Secreto",
secretOn: "Modo secreto ACTIVADO (sin historial)",
secretOff: "Modo secreto DESACTIVADO",
toastSaved: "Guardado.",
toastDeleted: "Eliminado.",
toastReordered: "Orden actualizado.",
emptyHistory: "Aún no hay historial.",
emptySaved: "No hay búsquedas guardadas. Añade una desde el botón Guardar abajo a la izquierda en la pestaña Búsqueda.",
run: "Ejecutar",
delete: "Eliminar",
updated: "Actualizado.",
tooltipSecret: "Alternar modo secreto (no se guardará historial)",
historyClearAll: "Borrar todo",
confirmClearHistory: "¿Borrar todo el historial?",
labelAccountScope: "Cuentas",
optAccountAll: "Todas las cuentas",
optAccountFollowing: "Cuentas que sigues",
labelLocationScope: "Ubicación",
optLocationAll: "Todas las ubicaciones",
optLocationNearby: "Cerca de ti",
chipFollowing: "Siguiendo",
chipNearby: "Cerca",
labelSearchTarget: "Ámbito de búsqueda",
labelHitName: "Excluir coincidencias solo en nombre",
labelHitHandle: "Excluir coincidencias solo en usuario (@)",
hintSearchTarget: "Ocultar publicaciones que solo coincidan en el nombre o usuario (no en el cuerpo).",
hintName: "Si la palabra clave aparece solo en el nombre mostrado, ocultarla.",
hintHandle: "Si la palabra clave aparece solo en el @usuario, ocultarla. Excepción: si la consulta usa explícitamente from:/to:/@.",
tabMute: "Silenciar",
labelMuteWord: "Añadir palabra silenciada",
placeholderMuteWord: "ej. spoiler",
labelCaseSensitive: "Distinguir mayúsculas",
labelWordBoundary: "Palabra completa",
labelEnabled: "Habilitado",
labelEnableAll: "Habilitar todo",
buttonAdd: "Añadir",
emptyMuted: "No hay palabras silenciadas.",
mutedListTitle: "Palabras silenciadas",
mutedListHeading: "Lista de silenciados",
optMuteHidden: "Oculto",
optMuteCollapsed: "Colapsado",
placeholderFilterMute: "Filtrar palabras silenciadas...",
muteLabel: "Silenciado: ",
buttonShow: "Mostrar",
muteHit: "Silenciar coincidencias en cuerpo",
buttonRemute: "Volver a silenciar",
buttonImport: "Importar",
buttonExport: "Exportar",
/* Accounts tab */
tabAccounts: "Cuentas",
emptyAccounts: "Aún no hay cuentas. Abre un perfil y haz clic en el botón Añadir para guardarlo.",
buttonAddAccount: "Añadir cuenta",
toastAccountAdded: "Cuenta añadida.",
toastAccountExists: "Ya existe.",
buttonConfirm: "Confirmar",
/* Lists tab */
tabLists: "Listas",
emptyLists: "Aún no hay listas. Abre una lista y haz clic en el botón + arriba a la derecha para añadirla.",
buttonAddList: "Añadir lista",
toastListAdded: "Lista añadida.",
toastListExists: "Ya existe.",
/* History tab */
placeholderSearchHistory: "Historial de búsqueda (consulta)",
labelSortBy: "Ordenar por:",
placeholderSearchSaved: "Búsquedas guardadas (consulta)",
sortNewest: "Más reciente",
sortOldest: "Más antiguo",
sortNameAsc: "Nombre (A-Z)",
sortNameDesc: "Nombre (Z-A)",
/* Folder/List/Account tabs */
placeholderFilterAccounts: "Filtrar cuentas (@, nombre)",
placeholderFilterLists: "Filtrar listas (nombre, url)",
buttonAddFolder: "+Carpeta",
folderFilterAll: "TODO",
folderFilterUnassigned: "Sin asignar",
folderRename: "Renombrar",
folderRenameTitle: "Renombrar carpeta",
folderDelete: "Eliminar",
folderDeleteTitle: "Eliminar carpeta",
promptNewFolder: "Nombre de nueva carpeta",
confirmDeleteFolder: "¿Eliminar esta carpeta y todo su contenido? Esto no se puede deshacer.",
optListsAll: "Listas",
defaultSavedFolders: "Búsquedas guardadas",
/* Favorites */
tabFavorites: "Favoritos",
emptyFavorites: "No hay favoritos. Haz clic en el icono ★ de los tweets para guardarlos.",
optFavoritesAll: "Todos los favoritos",
toastFavorited: "Añadido a favoritos.",
toastUnfavorited: "Eliminado de favoritos.",
/* Settings */
settingsTitle: "Configuración",
settingsTitleGeneral: "General",
settingsTitleFeatures: "Visibilidad de pestañas",
settingsTitleData: "Datos",
buttonClose: "Cerrar",
labelUILang: "Idioma de interfaz",
optUILangAuto: "Automático",
labelInitialTab: "Pestaña de inicio",
optInitialTabLast: "Última abierta (Predeterminado)",
labelImportExport: "Importar / Exportar",
placeholderSettingsJSON: "Pega el JSON de respaldo aquí...",
tooltipSettings: "Abrir configuración",
toastImported: "Importado.",
toastExported: "Exportado a archivo.",
alertInvalidJSON: "Archivo JSON inválido.",
alertInvalidData: "Formato de datos inválido.",
alertInvalidApp: 'Este archivo no es un respaldo válido para "Advanced Search for X".',
buttonReset: "Restablecer todo",
confirmResetAll: "¿Restablecer todos los datos? Esto no se puede deshacer.",
toastReset: "Todos los datos han sido restablecidos.",
buttonImportSuccess: "Importado con éxito 👍️",
/* Favorites Sort */
sortSavedNewest: "Fecha de guardado (Reciente)",
sortSavedOldest: "Fecha de guardado (Antigua)",
sortPostedNewest: "Fecha de publicación (Reciente)",
sortPostedOldest: "Fecha de publicación (Antigua)",
/* --- Favorite Tags --- */
FT_UNCATEGORIZED: 'Sin categoría',
FT_DROPDOWN_TITLE: 'Etiquetas de favoritos',
FT_DROPDOWN_SETTINGS_TITLE: 'Configuración de etiquetas',
FT_DROPDOWN_NEW_TAG: 'Nueva etiqueta',
FT_DROPDOWN_NEW_TAG_PLACEHOLDER: 'Nombre de etiqueta',
FT_DROPDOWN_NEW_TAG_ADD: 'Añadir',
FT_FILTER_ALL: 'Todo',
FT_SETTINGS_TITLE: 'Configuración de etiquetas',
FT_SETTINGS_EMPTY_TAG_LIST: 'No hay etiquetas. Añade una desde "Nueva etiqueta".',
FT_SETTINGS_UNCATEGORIZED_NAME: 'Sin categoría',
FT_SETTINGS_UNCATEGORIZED_NAME_TOOLTIP: 'El nombre "Sin categoría" no se puede cambiar.',
FT_SETTINGS_UNCATEGORIZED_DELETE_TOOLTIP: '"Sin categoría" no se puede eliminar.',
FT_SETTINGS_CLOSE: 'Cerrar',
FT_SETTINGS_DELETE_BUTTON: 'Eliminar',
FT_SETTINGS_UP: '▲',
FT_SETTINGS_DOWN: '▼',
FT_SETTINGS_DISPLAY_SECTION_TITLE: 'Visualización',
FT_SETTINGS_DISPLAY_MODE_LABEL: 'Formato de etiqueta',
FT_SETTINGS_DISPLAY_MODE_LEAF: 'Solo etiqueta (leaf)',
FT_SETTINGS_DISPLAY_MODE_FULL: 'Ruta completa (full)',
FT_CONFIRM_DELETE_TAG_MSG: '¿Eliminar la etiqueta "{tagName}"?\nLos favoritos con esta etiqueta pasarán a "Sin categoría".',
FT_SETTINGS_BUTTON_TITLE: 'Configuración de etiquetas',
},
'de': {
modalTitle: "Erweiterte Suche",
tooltipClose: "Schließen",
labelAllWords: "All diese Wörter",
placeholderAllWords: "z.B. AI Nachrichten",
labelExactPhrase: "Genau dieser Ausdruck",
placeholderExactPhrase: 'z.B. "ChatGPT 4o"',
labelAnyWords: "Beliebige dieser Wörter (OR)",
placeholderAnyWords: "z.B. iPhone Android",
labelNotWords: "Keines dieser Wörter (-)",
placeholderNotWords: "z.B. -Verkauf -Werbung",
labelHashtag: "Hashtags (#)",
placeholderHashtag: "z.B. #TechEvent",
labelLang: "Sprache (lang:)",
optLangDefault: "Beliebige Sprache",
optLangJa: "Japanisch (ja)",
optLangEn: "Englisch (en)",
optLangId: "Indonesisch (id)",
optLangHi: "Hindi (hi)",
optLangDe: "Deutsch (de)",
optLangTr: "Türkisch (tr)",
optLangEs: "Spanisch (es)",
optLangPt: "Portugiesisch (pt)",
optLangAr: "Arabisch (ar)",
optLangFr: "Französisch (fr)",
optLangKo: "Koreanisch (ko)",
optLangRu: "Russisch (ru)",
optLangZhHans: "Chinesisch vereinfacht (zh-cn)",
optLangZhHant: "Chinesisch traditionell (zh-tw)",
hrSeparator: " ",
labelFilters: "Filter",
labelVerified: "Verifizierte Konten",
labelLinks: "Links",
labelImages: "Bilder",
labelVideos: "Videos",
labelReposts: "Reposts",
labelTimelineHashtags: "Hashtags (#)",
checkInclude: "Einschl.",
checkExclude: "Ausschl.",
labelReplies: "Antworten",
optRepliesDefault: "Standard (Alle)",
optRepliesInclude: "Antworten einschließen",
optRepliesOnly: "Nur Antworten",
optRepliesExclude: "Antworten ausschließen",
labelEngagement: "Interaktionen",
placeholderMinReplies: "Min. Antworten",
placeholderMinLikes: "Min. Gefällt mir",
placeholderMinRetweets: "Min. Reposts",
labelDateRange: "Zeitraum",
labelDateShortcut: "Schnellauswahl",
optDate1Day: "Letzte 24 Std.",
optDate1Week: "Letzte Woche",
optDate1Month: "Letzter Monat",
optDate3Months: "Letzte 3 Monate",
optDate6Months: "Letzte 6 Monate",
optDate1Year: "Letztes Jahr",
optDate2Years: "Letzte 2 Jahre",
optDate3Years: "Letzte 3 Jahre",
optDate5Years: "Letzte 5 Jahre",
optDateClear: "Datum löschen",
tooltipSince: "Seit diesem Datum",
tooltipUntil: "Bis zu diesem Datum",
labelFromUser: "Von diesen Konten (from:)",
placeholderFromUser: "z.B. @X",
labelToUser: "An diese Konten (to:)",
placeholderToUser: "z.B. @google",
labelMentioning: "Erwähnung dieser Konten (@)",
placeholderMentioning: "z.B. @OpenAI",
buttonClear: "Löschen",
buttonApply: "Suchen",
tooltipTrigger: "Erweiterte Suche öffnen",
buttonOpen: "Öffnen",
tabSearch: "Suche",
tabHistory: "Verlauf",
tabSaved: "Gespeichert",
buttonSave: "Speichern",
buttonSaved: "Gespeichert",
secretMode: "Inkognito",
secretOn: "Inkognito-Modus AN (Kein Verlauf)",
secretOff: "Inkognito-Modus AUS",
toastSaved: "Gespeichert.",
toastDeleted: "Gelöscht.",
toastReordered: "Reihenfolge aktualisiert.",
emptyHistory: "Noch kein Verlauf.",
emptySaved: "Keine gespeicherten Suchen. Fügen Sie welche über den Speichern-Button unten links im Suche-Tab hinzu.",
run: "Ausführen",
delete: "Löschen",
updated: "Aktualisiert.",
tooltipSecret: "Inkognito-Modus umschalten (kein Verlauf wird gespeichert)",
historyClearAll: "Alle löschen",
confirmClearHistory: "Gesamten Verlauf löschen?",
labelAccountScope: "Konten",
optAccountAll: "Alle Konten",
optAccountFollowing: "Konten, denen du folgst",
labelLocationScope: "Standort",
optLocationAll: "Alle Standorte",
optLocationNearby: "In deiner Nähe",
chipFollowing: "Folge ich",
chipNearby: "In der Nähe",
labelSearchTarget: "Suchziel",
labelHitName: "Treffer nur im Anzeigenamen ausschließen",
labelHitHandle: "Treffer nur im Benutzernamen (@) ausschließen",
hintSearchTarget: "Beiträge ausblenden, die nur im Namen oder Handle übereinstimmen (nicht im Text).",
hintName: "Wenn ein Stichwort nur im Anzeigenamen vorkommt, ausblenden.",
hintHandle: "Wenn ein Stichwort nur im @Benutzernamen vorkommt, ausblenden. Ausnahme: wenn die Anfrage explizit from:/to:/@ verwendet.",
tabMute: "Stummschalten",
labelMuteWord: "Stummes Wort hinzufügen",
placeholderMuteWord: "z.B. Spoiler",
labelCaseSensitive: "Groß-/Kleinschreibung",
labelWordBoundary: "Ganzes Wort",
labelEnabled: "Aktiviert",
labelEnableAll: "Alle aktivieren",
buttonAdd: "Hinzufügen",
emptyMuted: "Keine stummgeschalteten Wörter.",
mutedListTitle: "Stummgeschaltete Wörter",
mutedListHeading: "Stummgeschaltete Liste",
optMuteHidden: "Verborgen",
optMuteCollapsed: "Eingeklappt",
placeholderFilterMute: "Stummgeschaltete Wörter filtern...",
muteLabel: "Stummgeschaltet: ",
buttonShow: "Anzeigen",
muteHit: "Treffer im Text stummschalten",
buttonRemute: "Erneut stummschalten",
buttonImport: "Importieren",
buttonExport: "Exportieren",
/* Accounts tab */
tabAccounts: "Konten",
emptyAccounts: "Noch keine Konten. Öffnen Sie ein Profil und klicken Sie auf Hinzufügen, um es zu speichern.",
buttonAddAccount: "Konto hinzufügen",
toastAccountAdded: "Konto hinzugefügt.",
toastAccountExists: "Bereits vorhanden.",
buttonConfirm: "Bestätigen",
/* Lists tab */
tabLists: "Listen",
emptyLists: "Noch keine Listen. Öffnen Sie eine Liste und klicken Sie oben rechts auf +, um sie hinzuzufügen.",
buttonAddList: "Liste hinzufügen",
toastListAdded: "Liste hinzugefügt.",
toastListExists: "Bereits vorhanden.",
/* History tab */
placeholderSearchHistory: "Suchverlauf (Query)",
labelSortBy: "Sortieren nach:",
placeholderSearchSaved: "Gespeicherte Suchen (Query)",
sortNewest: "Neueste zuerst",
sortOldest: "Älteste zuerst",
sortNameAsc: "Name (A-Z)",
sortNameDesc: "Name (Z-A)",
/* Folder/List/Account tabs */
placeholderFilterAccounts: "Konten filtern (@, Name)",
placeholderFilterLists: "Listen filtern (Name, URL)",
buttonAddFolder: "+Ordner",
folderFilterAll: "ALLE",
folderFilterUnassigned: "Nicht zugewiesen",
folderRename: "Umbenennen",
folderRenameTitle: "Ordner umbenennen",
folderDelete: "Löschen",
folderDeleteTitle: "Ordner löschen",
promptNewFolder: "Neuer Ordnername",
confirmDeleteFolder: "Diesen Ordner und alle Elemente darin löschen? Dies kann nicht rückgängig gemacht werden.",
optListsAll: "Listen",
defaultSavedFolders: "Gespeicherte Suchen",
/* Favorites */
tabFavorites: "Favoriten",
emptyFavorites: "Keine Favoriten. Klicken Sie auf das ★-Symbol bei Tweets, um sie zu speichern.",
optFavoritesAll: "Alle Favoriten",
toastFavorited: "Zu Favoriten hinzugefügt.",
toastUnfavorited: "Aus Favoriten entfernt.",
/* Settings */
settingsTitle: "Einstellungen",
settingsTitleGeneral: "Allgemein",
settingsTitleFeatures: "Tab-Sichtbarkeit",
settingsTitleData: "Daten",
buttonClose: "Schließen",
labelUILang: "Oberflächensprache",
optUILangAuto: "Automatisch",
labelInitialTab: "Start-Tab",
optInitialTabLast: "Zuletzt geöffnet (Standard)",
labelImportExport: "Import / Export",
placeholderSettingsJSON: "Backup-JSON hier einfügen...",
tooltipSettings: "Einstellungen öffnen",
toastImported: "Importiert.",
toastExported: "In Datei exportiert.",
alertInvalidJSON: "Ungültige JSON-Datei.",
alertInvalidData: "Ungültiges Datenformat.",
alertInvalidApp: 'Diese Datei ist kein gültiges Backup für "Advanced Search for X".',
buttonReset: "Alle Daten zurücksetzen",
confirmResetAll: "Alle Daten zurücksetzen? Dies kann nicht rückgängig gemacht werden.",
toastReset: "Alle Daten wurden zurückgesetzt.",
buttonImportSuccess: "Erfolgreich importiert 👍️",
/* Favorites Sort */
sortSavedNewest: "Speicherdatum (Neu)",
sortSavedOldest: "Speicherdatum (Alt)",
sortPostedNewest: "Veröffentlichungsdatum (Neu)",
sortPostedOldest: "Veröffentlichungsdatum (Alt)",
/* --- Favorite Tags --- */
FT_UNCATEGORIZED: 'Unkategorisiert',
FT_DROPDOWN_TITLE: 'Favoriten-Tags',
FT_DROPDOWN_SETTINGS_TITLE: 'Tag-Einstellungen',
FT_DROPDOWN_NEW_TAG: 'Neuer Tag',
FT_DROPDOWN_NEW_TAG_PLACEHOLDER: 'Tag-Name',
FT_DROPDOWN_NEW_TAG_ADD: 'Hinzufügen',
FT_FILTER_ALL: 'Alle',
FT_SETTINGS_TITLE: 'Favoriten-Tag-Einstellungen',
FT_SETTINGS_EMPTY_TAG_LIST: 'Keine Tags. Fügen Sie einen über "Neuer Tag" hinzu.',
FT_SETTINGS_UNCATEGORIZED_NAME: 'Unkategorisiert',
FT_SETTINGS_UNCATEGORIZED_NAME_TOOLTIP: 'Der Name "Unkategorisiert" kann nicht geändert werden.',
FT_SETTINGS_UNCATEGORIZED_DELETE_TOOLTIP: '"Unkategorisiert" kann nicht gelöscht werden.',
FT_SETTINGS_CLOSE: 'Schließen',
FT_SETTINGS_DELETE_BUTTON: 'Löschen',
FT_SETTINGS_UP: '▲',
FT_SETTINGS_DOWN: '▼',
FT_SETTINGS_DISPLAY_SECTION_TITLE: 'Anzeige',
FT_SETTINGS_DISPLAY_MODE_LABEL: 'Tag-Format',
FT_SETTINGS_DISPLAY_MODE_LEAF: 'Nur Label (leaf)',
FT_SETTINGS_DISPLAY_MODE_FULL: 'Voller Pfad (full)',
FT_CONFIRM_DELETE_TAG_MSG: 'Tag "{tagName}" löschen?\nFavoriten mit diesem Tag werden "Unkategorisiert".',
FT_SETTINGS_BUTTON_TITLE: 'Tag-Einstellungen',
},
'pt-BR': {
modalTitle: "Busca avançada",
tooltipClose: "Fechar",
labelAllWords: "Todas estas palavras",
placeholderAllWords: "ex: AI notícias",
labelExactPhrase: "Esta frase exata",
placeholderExactPhrase: 'ex: "ChatGPT 4o"',
labelAnyWords: "Qualquer destas palavras (OR)",
placeholderAnyWords: "ex: iPhone Android",
labelNotWords: "Nenhuma destas palavras (-)",
placeholderNotWords: "ex: -promoção -ads",
labelHashtag: "Hashtags (#)",
placeholderHashtag: "ex: #Tecnologia",
labelLang: "Idioma (lang:)",
optLangDefault: "Qualquer idioma",
optLangJa: "Japonês (ja)",
optLangEn: "Inglês (en)",
optLangId: "Indonésio (id)",
optLangHi: "Hindi (hi)",
optLangDe: "Alemão (de)",
optLangTr: "Turco (tr)",
optLangEs: "Espanhol (es)",
optLangPt: "Português (pt)",
optLangAr: "Árabe (ar)",
optLangFr: "Francês (fr)",
optLangKo: "Coreano (ko)",
optLangRu: "Russo (ru)",
optLangZhHans: "Chinês Simplificado (zh-cn)",
optLangZhHant: "Chinês Tradicional (zh-tw)",
hrSeparator: " ",
labelFilters: "Filtros",
labelVerified: "Contas verificadas",
labelLinks: "Links",
labelImages: "Imagens",
labelVideos: "Vídeos",
labelReposts: "Reposts",
labelTimelineHashtags: "Hashtags (#)",
checkInclude: "Incluir",
checkExclude: "Excluir",
labelReplies: "Respostas",
optRepliesDefault: "Padrão (Tudo)",
optRepliesInclude: "Incluir respostas",
optRepliesOnly: "Apenas respostas",
optRepliesExclude: "Excluir respostas",
labelEngagement: "Engajamento",
placeholderMinReplies: "Mín respostas",
placeholderMinLikes: "Mín curtidas",
placeholderMinRetweets: "Mín reposts",
labelDateRange: "Período",
labelDateShortcut: "Intervalo rápido",
optDate1Day: "Últimas 24h",
optDate1Week: "Última semana",
optDate1Month: "Último mês",
optDate3Months: "Últimos 3 meses",
optDate6Months: "Últimos 6 meses",
optDate1Year: "Último ano",
optDate2Years: "Últimos 2 anos",
optDate3Years: "Últimos 3 anos",
optDate5Years: "Últimos 5 anos",
optDateClear: "Limpar datas",
tooltipSince: "A partir desta data",
tooltipUntil: "Até esta data",
labelFromUser: "Destas contas (from:)",
placeholderFromUser: "ex: @X",
labelToUser: "Para estas contas (to:)",
placeholderToUser: "ex: @google",
labelMentioning: "Mencionando estas contas (@)",
placeholderMentioning: "ex: @OpenAI",
buttonClear: "Limpar",
buttonApply: "Buscar",
tooltipTrigger: "Abrir busca avançada",
buttonOpen: "Abrir",
tabSearch: "Busca",
tabHistory: "Histórico",
tabSaved: "Salvos",
buttonSave: "Salvar",
buttonSaved: "Salvo",
secretMode: "Secreto",
secretOn: "Modo secreto ON (Sem histórico)",
secretOff: "Modo secreto OFF",
toastSaved: "Salvo.",
toastDeleted: "Excluído.",
toastReordered: "Ordem atualizada.",
emptyHistory: "Sem histórico ainda.",
emptySaved: "Nenhuma busca salva. Adicione pelo botão Salvar no canto inferior esquerdo da aba Busca.",
run: "Executar",
delete: "Excluir",
updated: "Atualizado.",
tooltipSecret: "Alternar Modo Secreto (histórico não será gravado)",
historyClearAll: "Limpar tudo",
confirmClearHistory: "Limpar todo o histórico?",
labelAccountScope: "Contas",
optAccountAll: "Todas as contas",
optAccountFollowing: "Contas que você segue",
labelLocationScope: "Localização",
optLocationAll: "Todas as localizações",
optLocationNearby: "Perto de você",
chipFollowing: "Seguindo",
chipNearby: "Próximo",
labelSearchTarget: "Alvo da busca",
labelHitName: "Excluir resultados apenas no nome",
labelHitHandle: "Excluir resultados apenas no usuário (@)",
hintSearchTarget: "Ocultar posts que correspondem apenas ao nome ou usuário (não no corpo).",
hintName: "Se a palavra-chave aparecer apenas no nome de exibição, ocultar.",
hintHandle: "Se a palavra-chave aparecer apenas no @usuario, ocultar. Exceção: quando a consulta usar explicitamente from:/to:/@.",
tabMute: "Silenciar",
labelMuteWord: "Adicionar palavra silenciada",
placeholderMuteWord: "ex: spoiler",
labelCaseSensitive: "Diferenciar maiúsculas",
labelWordBoundary: "Palavra inteira",
labelEnabled: "Ativado",
labelEnableAll: "Ativar tudo",
buttonAdd: "Adicionar",
emptyMuted: "Nenhuma palavra silenciada.",
mutedListTitle: "Palavras silenciadas",
mutedListHeading: "Lista de silenciados",
optMuteHidden: "Oculto",
optMuteCollapsed: "Colapsado",
placeholderFilterMute: "Filtrar palavras silenciadas...",
muteLabel: "Silenciado: ",
buttonShow: "Mostrar",
muteHit: "Silenciar resultados no corpo",
buttonRemute: "Silenciar novamente",
buttonImport: "Importar",
buttonExport: "Exportar",
/* Accounts tab */
tabAccounts: "Contas",
emptyAccounts: "Nenhuma conta ainda. Abra um perfil e clique no botão Adicionar para salvar.",
buttonAddAccount: "Adicionar conta",
toastAccountAdded: "Conta adicionada.",
toastAccountExists: "Já adicionada.",
buttonConfirm: "Confirmar",
/* Lists tab */
tabLists: "Listas",
emptyLists: "Nenhuma lista ainda. Abra uma Lista e clique no botão + no canto superior direito para adicionar.",
buttonAddList: "Adicionar lista",
toastListAdded: "Lista adicionada.",
toastListExists: "Já adicionada.",
/* History tab */
placeholderSearchHistory: "Histórico de busca (query)",
labelSortBy: "Ordenar por:",
placeholderSearchSaved: "Buscas salvas (query)",
sortNewest: "Mais recente",
sortOldest: "Mais antigo",
sortNameAsc: "Nome (A-Z)",
sortNameDesc: "Nome (Z-A)",
/* Folder/List/Account tabs */
placeholderFilterAccounts: "Filtrar contas (@, nome)",
placeholderFilterLists: "Filtrar listas (nome, url)",
buttonAddFolder: "+Pasta",
folderFilterAll: "TUDO",
folderFilterUnassigned: "Não atribuído",
folderRename: "Renomear",
folderRenameTitle: "Renomear pasta",
folderDelete: "Excluir",
folderDeleteTitle: "Excluir pasta",
promptNewFolder: "Nome da nova pasta",
confirmDeleteFolder: "Excluir esta pasta e todos os itens dentro dela? Isso não pode ser desfeito.",
optListsAll: "Listas",
defaultSavedFolders: "Buscas Salvas",
/* Favorites */
tabFavorites: "Favoritos",
emptyFavorites: "Nenhum favorito ainda. Clique no ícone ★ nos tweets para salvar.",
optFavoritesAll: "Todos os favoritos",
toastFavorited: "Adicionado aos favoritos.",
toastUnfavorited: "Removido dos favoritos.",
/* Settings */
settingsTitle: "Configurações",
settingsTitleGeneral: "Geral",
settingsTitleFeatures: "Visibilidade de abas",
settingsTitleData: "Dados",
buttonClose: "Fechar",
labelUILang: "Idioma da interface",
optUILangAuto: "Automático",
labelInitialTab: "Aba inicial",
optInitialTabLast: "Última aberta (Padrão)",
labelImportExport: "Importar / Exportar",
placeholderSettingsJSON: "Cole o JSON de backup aqui...",
tooltipSettings: "Abrir configurações",
toastImported: "Importado.",
toastExported: "Exportado para arquivo.",
alertInvalidJSON: "Arquivo JSON inválido.",
alertInvalidData: "Formato de dados inválido.",
alertInvalidApp: 'Este arquivo não é um backup válido para "Advanced Search for X".',
buttonReset: "Redefinir tudo",
confirmResetAll: "Redefinir todos os dados? Isso não pode ser desfeito.",
toastReset: "Todos os dados foram redefinidos.",
buttonImportSuccess: "Importado com sucesso 👍️",
/* Favorites Sort */
sortSavedNewest: "Data (Mais recente)",
sortSavedOldest: "Data (Mais antigo)",
sortPostedNewest: "Postado (Mais recente)",
sortPostedOldest: "Postado (Mais antigo)",
/* --- Favorite Tags --- */
FT_UNCATEGORIZED: 'Sem categoria',
FT_DROPDOWN_TITLE: 'Tags favoritas',
FT_DROPDOWN_SETTINGS_TITLE: 'Configurações de tags',
FT_DROPDOWN_NEW_TAG: 'Nova tag',
FT_DROPDOWN_NEW_TAG_PLACEHOLDER: 'Nome da tag',
FT_DROPDOWN_NEW_TAG_ADD: 'Adicionar',
FT_FILTER_ALL: 'Tudo',
FT_SETTINGS_TITLE: 'Configurações de tags favoritas',
FT_SETTINGS_EMPTY_TAG_LIST: 'Sem tags. Adicione em "Nova tag".',
FT_SETTINGS_UNCATEGORIZED_NAME: 'Sem categoria',
FT_SETTINGS_UNCATEGORIZED_NAME_TOOLTIP: 'O nome "Sem categoria" não pode ser alterado.',
FT_SETTINGS_UNCATEGORIZED_DELETE_TOOLTIP: '"Sem categoria" não pode ser excluída.',
FT_SETTINGS_CLOSE: 'Fechar',
FT_SETTINGS_DELETE_BUTTON: 'Excluir',
FT_SETTINGS_UP: '▲',
FT_SETTINGS_DOWN: '▼',
FT_SETTINGS_DISPLAY_SECTION_TITLE: 'Exibição',
FT_SETTINGS_DISPLAY_MODE_LABEL: 'Formato da tag',
FT_SETTINGS_DISPLAY_MODE_LEAF: 'Apenas etiqueta (leaf)',
FT_SETTINGS_DISPLAY_MODE_FULL: 'Caminho completo (full)',
FT_CONFIRM_DELETE_TAG_MSG: 'Excluir tag "{tagName}"?\nFavoritos com esta tag ficarão "Sem categoria".',
FT_SETTINGS_BUTTON_TITLE: 'Configurações de tags',
},
'ru': {
modalTitle: "Расширенный поиск",
tooltipClose: "Закрыть",
labelAllWords: "Все эти слова",
placeholderAllWords: "напр., AI новости",
labelExactPhrase: "Точная фраза",
placeholderExactPhrase: 'напр., "ChatGPT 4o"',
labelAnyWords: "Любое из этих слов (OR)",
placeholderAnyWords: "напр., iPhone Android",
labelNotWords: "Исключить слова (-)",
placeholderNotWords: "напр., -распродажа -реклама",
labelHashtag: "Хэштеги (#)",
placeholderHashtag: "напр., #TechEvent",
labelLang: "Язык (lang:)",
optLangDefault: "Любой язык",
optLangJa: "Японский (ja)",
optLangEn: "Английский (en)",
optLangId: "Индонезийский (id)",
optLangHi: "Хинди (hi)",
optLangDe: "Немецкий (de)",
optLangTr: "Турецкий (tr)",
optLangEs: "Испанский (es)",
optLangPt: "Португальский (pt)",
optLangAr: "Арабский (ar)",
optLangFr: "Французский (fr)",
optLangKo: "Корейский (ko)",
optLangRu: "Русский (ru)",
optLangZhHans: "Китайский упр. (zh-cn)",
optLangZhHant: "Китайский трад. (zh-tw)",
hrSeparator: " ",
labelFilters: "Фильтры",
labelVerified: "Подтвержденные аккаунты",
labelLinks: "Ссылки",
labelImages: "Изображения",
labelVideos: "Видео",
labelReposts: "Репосты",
labelTimelineHashtags: "Хэштеги (#)",
checkInclude: "Вкл",
checkExclude: "Искл",
labelReplies: "Ответы",
optRepliesDefault: "По умолчанию (Все)",
optRepliesInclude: "Включая ответы",
optRepliesOnly: "Только ответы",
optRepliesExclude: "Исключить ответы",
labelEngagement: "Вовлеченность",
placeholderMinReplies: "Мин. ответов",
placeholderMinLikes: "Мин. лайков",
placeholderMinRetweets: "Мин. репостов",
labelDateRange: "Диапазон дат",
labelDateShortcut: "Быстрый выбор",
optDate1Day: "За 24 часа",
optDate1Week: "За неделю",
optDate1Month: "За месяц",
optDate3Months: "За 3 месяца",
optDate6Months: "За 6 месяцев",
optDate1Year: "За год",
optDate2Years: "За 2 года",
optDate3Years: "За 3 года",
optDate5Years: "За 5 лет",
optDateClear: "Очистить даты",
tooltipSince: "С этой даты",
tooltipUntil: "По эту дату",
labelFromUser: "От этих аккаунтов (from:)",
placeholderFromUser: "напр., @X",
labelToUser: "Этим аккаунтам (to:)",
placeholderToUser: "напр., @google",
labelMentioning: "Упоминание этих аккаунтов (@)",
placeholderMentioning: "напр., @OpenAI",
buttonClear: "Очистить",
buttonApply: "Поиск",
tooltipTrigger: "Открыть расширенный поиск",
buttonOpen: "Открыть",
tabSearch: "Поиск",
tabHistory: "История",
tabSaved: "Сохраненное",
buttonSave: "Сохранить",
buttonSaved: "Сохранено",
secretMode: "Секретный",
secretOn: "Секретный режим ВКЛ (без истории)",
secretOff: "Секретный режим ВЫКЛ",
toastSaved: "Сохранено.",
toastDeleted: "Удалено.",
toastReordered: "Порядок обновлен.",
emptyHistory: "Истории пока нет.",
emptySaved: "Нет сохраненных поисков. Добавьте их кнопкой Сохранить внизу вкладки Поиск.",
run: "Выполнить",
delete: "Удалить",
updated: "Обновлено.",
tooltipSecret: "Переключить секретный режим (история не будет записана)",
historyClearAll: "Очистить всё",
confirmClearHistory: "Очистить всю историю?",
labelAccountScope: "Аккаунты",
optAccountAll: "Все аккаунты",
optAccountFollowing: "Читаемые вами",
labelLocationScope: "Местоположение",
optLocationAll: "Везде",
optLocationNearby: "Рядом с вами",
chipFollowing: "Читаемые",
chipNearby: "Рядом",
labelSearchTarget: "Цель поиска",
labelHitName: "Исключить совпадения только в имени",
labelHitHandle: "Исключить совпадения только в юзернейме (@)",
hintSearchTarget: "Скрыть посты, совпадающие только по имени/юзернейму (но не в тексте).",
hintName: "Если ключевое слово только в отображаемом имени — скрыть.",
hintHandle: "Если ключевое слово только в @юзернейме — скрыть. Искл: если запрос явно использует from:/to:/@.",
tabMute: "Скрыть",
labelMuteWord: "Добавить скрытое слово",
placeholderMuteWord: "напр., спойлер",
labelCaseSensitive: "Учитывать регистр",
labelWordBoundary: "Слово целиком",
labelEnabled: "Включено",
labelEnableAll: "Включить все",
buttonAdd: "Добавить",
emptyMuted: "Нет скрытых слов.",
mutedListTitle: "Скрытые слова",
mutedListHeading: "Список скрытого",
optMuteHidden: "Скрыто",
optMuteCollapsed: "Свернуто",
placeholderFilterMute: "Фильтр скрытых слов...",
muteLabel: "Скрыто: ",
buttonShow: "Показать",
muteHit: "Скрывать совпадения в тексте",
buttonRemute: "Скрыть снова",
buttonImport: "Импорт",
buttonExport: "Экспорт",
/* Accounts tab */
tabAccounts: "Аккаунты",
emptyAccounts: "Аккаунтов нет. Откройте профиль и нажмите Добавить, чтобы сохранить.",
buttonAddAccount: "Добавить аккаунт",
toastAccountAdded: "Аккаунт добавлен.",
toastAccountExists: "Уже добавлен.",
buttonConfirm: "Подтвердить",
/* Lists tab */
tabLists: "Списки",
emptyLists: "Списков нет. Откройте список и нажмите + в углу для добавления.",
buttonAddList: "Добавить список",
toastListAdded: "Список добавлен.",
toastListExists: "Уже добавлен.",
/* History tab */
placeholderSearchHistory: "История поиска (запрос)",
labelSortBy: "Сортировка:",
placeholderSearchSaved: "Сохраненный поиск (запрос)",
sortNewest: "Сначала новые",
sortOldest: "Сначала старые",
sortNameAsc: "Имя (А-Я)",
sortNameDesc: "Имя (Я-А)",
/* Folder/List/Account tabs */
placeholderFilterAccounts: "Фильтр аккаунтов (@, имя)",
placeholderFilterLists: "Фильтр списков (имя, url)",
buttonAddFolder: "+Папка",
folderFilterAll: "ВСЕ",
folderFilterUnassigned: "Без папки",
folderRename: "Переименовать",
folderRenameTitle: "Переименовать папку",
folderDelete: "Удалить",
folderDeleteTitle: "Удалить папку",
promptNewFolder: "Имя новой папки",
confirmDeleteFolder: "Удалить эту папку и всё содержимое? Это нельзя отменить.",
optListsAll: "Списки",
defaultSavedFolders: "Сохраненные поиски",
/* Favorites */
tabFavorites: "Избранное",
emptyFavorites: "В избранном пусто. Нажмите ★ на твите, чтобы сохранить.",
optFavoritesAll: "Всё избранное",
toastFavorited: "Добавлено в избранное.",
toastUnfavorited: "Удалено из избранного.",
/* Settings */
settingsTitle: "Настройки",
settingsTitleGeneral: "Общие",
settingsTitleFeatures: "Вкладки",
settingsTitleData: "Данные",
buttonClose: "Закрыть",
labelUILang: "Язык интерфейса",
optUILangAuto: "Авто",
labelInitialTab: "Вкладка при запуске",
optInitialTabLast: "Последняя открытая (По умолч.)",
labelImportExport: "Импорт / Экспорт",
placeholderSettingsJSON: "Вставьте JSON резервной копии...",
tooltipSettings: "Открыть настройки",
toastImported: "Импортировано.",
toastExported: "Экспортировано в файл.",
alertInvalidJSON: "Неверный файл JSON.",
alertInvalidData: "Неверный формат данных.",
alertInvalidApp: 'Файл не является копией "Advanced Search for X".',
buttonReset: "Сбросить всё",
confirmResetAll: "Сбросить все данные? Это нельзя отменить.",
toastReset: "Все данные сброшены.",
buttonImportSuccess: "Успешный импорт 👍️",
/* Favorites Sort */
sortSavedNewest: "Дата сохр. (Новые)",
sortSavedOldest: "Дата сохр. (Старые)",
sortPostedNewest: "Дата публ. (Новые)",
sortPostedOldest: "Дата публ. (Старые)",
/* --- Favorite Tags --- */
FT_UNCATEGORIZED: 'Без категории',
FT_DROPDOWN_TITLE: 'Теги избранного',
FT_DROPDOWN_SETTINGS_TITLE: 'Настройка тегов',
FT_DROPDOWN_NEW_TAG: 'Новый тег',
FT_DROPDOWN_NEW_TAG_PLACEHOLDER: 'Имя тега',
FT_DROPDOWN_NEW_TAG_ADD: 'Добавить',
FT_FILTER_ALL: 'Все',
FT_SETTINGS_TITLE: 'Настройка тегов избранного',
FT_SETTINGS_EMPTY_TAG_LIST: 'Тегов нет. Добавьте через "Новый тег".',
FT_SETTINGS_UNCATEGORIZED_NAME: 'Без категории',
FT_SETTINGS_UNCATEGORIZED_NAME_TOOLTIP: 'Имя "Без категории" нельзя изменить.',
FT_SETTINGS_UNCATEGORIZED_DELETE_TOOLTIP: '"Без категории" нельзя удалить.',
FT_SETTINGS_CLOSE: 'Закрыть',
FT_SETTINGS_DELETE_BUTTON: 'Удалить',
FT_SETTINGS_UP: '▲',
FT_SETTINGS_DOWN: '▼',
FT_SETTINGS_DISPLAY_SECTION_TITLE: 'Отображение',
FT_SETTINGS_DISPLAY_MODE_LABEL: 'Формат тега',
FT_SETTINGS_DISPLAY_MODE_LEAF: 'Только имя (leaf)',
FT_SETTINGS_DISPLAY_MODE_FULL: 'Полный путь (full)',
FT_CONFIRM_DELETE_TAG_MSG: 'Удалить тег "{tagName}"?\nЭлементы с этим тегом станут "Без категории".',
FT_SETTINGS_BUTTON_TITLE: 'Настройка тегов',
}
},
lang: 'en',
init: function() {
const supportedLangs = Object.keys(this.translations);
let detectedLang = document.documentElement.lang || navigator.language || 'en';
if (supportedLangs.includes(detectedLang)) { this.lang = detectedLang; return; }
const baseLang = detectedLang.split('-')[0];
if (supportedLangs.includes(baseLang)) { this.lang = baseLang; return; }
this.lang = 'en';
},
t: function(key) { return this.translations[this.lang]?.[key] || this.translations['en'][key] || `[${key}]`; },
apply: function(container) {
container.querySelectorAll('[data-i18n]').forEach(el => { el.textContent = this.t(el.dataset.i18n); });
container.querySelectorAll('[data-i18n-placeholder]').forEach(el => { el.placeholder = this.t(el.dataset.i18nPlaceholder); });
container.querySelectorAll('[data-i18n-title]').forEach(el => { el.title = this.t(el.dataset.i18nTitle); });
}
};
const SEARCH_SVG = `
`;
const SETTINGS_SVG = `
`;
const FOLDER_TOGGLE_OPEN_SVG = `
`;
const FOLDER_TOGGLE_CLOSED_SVG = `
`;
// トグルボタンの小ユーティリティ
function renderFolderToggleButton(collapsed) {
const btn = document.createElement('button');
btn.type = 'button';
btn.className = 'adv-folder-toggle-btn';
btn.setAttribute('aria-label', collapsed ? 'Expand' : 'Collapse');
btn.setAttribute('title', collapsed ? 'Expand' : 'Collapse');
btn.setAttribute('aria-expanded', (!collapsed).toString());
btn.style.cssText = `
appearance:none;border:none;background:transparent;cursor:pointer;
width:22px;height:22px;display:inline-flex;align-items:center;justify-content:center;
margin-right:8px;color:inherit;flex:0 0 auto;
`;
btn.innerHTML = collapsed ? FOLDER_TOGGLE_CLOSED_SVG : FOLDER_TOGGLE_OPEN_SVG;
return btn;
}
function updateFolderToggleButton(btn, collapsed) {
if (!btn) return;
btn.innerHTML = collapsed ? FOLDER_TOGGLE_CLOSED_SVG : FOLDER_TOGGLE_OPEN_SVG;
btn.setAttribute('aria-label', collapsed ? 'Expand' : 'Collapse');
btn.setAttribute('title', collapsed ? 'Expand' : 'Collapse');
btn.setAttribute('aria-expanded', (!collapsed).toString());
}
const themeManager = {
colors: {
light: {
'--modal-bg': '#ffffff', '--modal-text-primary': '#0f1419', '--modal-text-secondary': '#536471', '--modal-border': '#d9e1e8',
'--modal-input-bg': '#eff3f4', '--modal-input-border': '#cfd9de', '--modal-button-hover-bg': 'rgba(15, 20, 25, 0.1)',
'--modal-scrollbar-thumb': '#aab8c2', '--modal-scrollbar-track': '#eff3f4', '--modal-close-color': '#0f1419',
'--modal-close-hover-bg': 'rgba(15, 20, 25, 0.1)', '--hr-color': '#eff3f4',
'--modal-tabs-shadow': '0 1px 12px rgba(0, 0, 0, 0.22)',
},
dim: {
'--modal-bg': '#15202b', '--modal-text-primary': '#f7f9f9', '--modal-text-secondary': '#8899a6', '--modal-border': '#38444d',
'--modal-input-bg': '#192734', '--modal-input-border': '#38444d', '--modal-button-hover-bg': 'rgba(247, 249, 249, 0.1)',
'--modal-scrollbar-thumb': '#536471', '--modal-scrollbar-track': '#192734', '--modal-close-color': '#f7f9f9',
'--modal-close-hover-bg': 'rgba(247, 249, 249, 0.1)', '--hr-color': '#38444d',
'--modal-tabs-shadow': '0 5px 12px rgba(0, 0, 0, 0.27)',
},
dark: {
'--modal-bg': '#000000', '--modal-text-primary': '#e7e9ea', '--modal-text-secondary': '#71767b', '--modal-border': '#2f3336',
'--modal-input-bg': '#16181c', '--modal-input-border': '#54595d', '--modal-button-hover-bg': 'rgba(231, 233, 234, 0.1)',
'--modal-scrollbar-thumb': '#536471', '--modal-scrollbar-track': '#16181c', '--modal-close-color': '#e7e9ea',
'--modal-close-hover-bg': 'rgba(231, 233, 234, 0.1)', '--hr-color': '#2f3336',
'--modal-tabs-shadow': '0 5px 12px rgba(0, 0, 0, 0.27)',
}
},
applyTheme: function(modalElement, triggerEl) {
if (!modalElement) return;
const bodyBg = getComputedStyle(document.body).backgroundColor;
let theme = 'dark';
if (bodyBg === 'rgb(21, 32, 43)') theme = 'dim';
else if (bodyBg === 'rgb(255, 255, 255)') theme = 'light';
// ▼ ブックマークUIのテーマ切替用にクラスを付与
try {
document.documentElement.classList.remove('x-theme-light', 'x-theme-dim', 'x-theme-dark');
if (theme === 'light') {
document.documentElement.classList.add('x-theme-light');
} else if (theme === 'dim') {
document.documentElement.classList.add('x-theme-dim');
} else {
document.documentElement.classList.add('x-theme-dark');
}
} catch (e) {}
const themeColors = this.colors[theme] || this.colors.dark;
const targets = [modalElement, document.documentElement];
if (triggerEl) targets.push(triggerEl);
for (const t of targets) {
for (const [key, value] of Object.entries(themeColors)) {
t.style.setProperty(key, value);
}
}
},
observeChanges: function(modalElement, triggerEl) {
const observer = new MutationObserver(() => this.applyTheme(modalElement, triggerEl));
observer.observe(document.body, { attributes: true, attributeFilter: ['style'] });
this.applyTheme(modalElement, triggerEl);
}
};
/**
* Mobile Drag & Drop Shim
* タッチイベントを検知し、HTML5 Drag & Dropイベント(dragstart, dragover, drop等)を発火させる
*/
function enableMobileDragSupport() {
let dragSource = null;
let lastTarget = null;
// DataTransferのデータを保持する擬似ストア
let dataTransferStore = {};
// 擬似的な DragEvent を作成するヘルパー
const createEvent = (type, touch, target) => {
const event = new CustomEvent(type, { bubbles: true, cancelable: true });
// dataTransfer オブジェクトを擬似的に再現
event.dataTransfer = {
effectAllowed: 'move',
dropEffect: 'move',
types: Object.keys(dataTransferStore),
setData: (format, data) => { dataTransferStore[format] = data; },
getData: (format) => dataTransferStore[format],
clearData: () => { dataTransferStore = {}; }
};
// 座標情報を付与 (getDragAfterElement 等の計算に必要)
event.clientX = touch.clientX;
event.clientY = touch.clientY;
event.pageX = touch.pageX;
event.pageY = touch.pageY;
// ターゲット要素を上書き設定 (CustomEventの制約回避)
Object.defineProperty(event, 'target', { value: target, enumerable: true });
return event;
};
const onTouchStart = (e) => {
// ハンドル、またはドラッグ可能な要素自体へのタッチか判定
const handle = e.target.closest('.adv-item-handle, .adv-folder-header, .adv-tab-btn, .ft-modal-tag-drag-handle');
if (!handle) return;
const draggable = handle.closest('[draggable="true"]');
if (!draggable) return;
dragSource = draggable;
dataTransferStore = {}; // データ初期化
const touch = e.changedTouches[0];
const evt = createEvent('dragstart', touch, dragSource);
dragSource.dispatchEvent(evt);
};
const onTouchMove = (e) => {
if (!dragSource) return;
// スクロール防止(CSSのtouch-actionで防げない場合用)
if (e.cancelable) e.preventDefault();
const touch = e.changedTouches[0];
// 指の下にある要素を取得
const element = document.elementFromPoint(touch.clientX, touch.clientY);
if (!element) return;
// dragover は頻繁に発火させる必要がある
// ターゲットが変わった場合は dragenter/dragleave も検討すべきだが、
// このアプリのロジック(並び替え)では dragover がメインのため、そこに集中する
// 既存ロジックが .closest('.adv-item') 等を使っているため、適切なターゲットに対して発火
// ここでは elementFromPoint で取れた要素に対して dragover を投げる
const evt = createEvent('dragover', touch, element);
element.dispatchEvent(evt);
lastTarget = element;
};
const onTouchEnd = (e) => {
if (!dragSource) return;
const touch = e.changedTouches[0];
// 最後に指があった要素に対して drop を発火
if (lastTarget) {
const evtDrop = createEvent('drop', touch, lastTarget);
lastTarget.dispatchEvent(evtDrop);
}
const evtEnd = createEvent('dragend', touch, dragSource);
dragSource.dispatchEvent(evtEnd);
// クリーンアップ
dragSource = null;
lastTarget = null;
dataTransferStore = {};
};
document.addEventListener('touchstart', onTouchStart, { passive: false });
document.addEventListener('touchmove', onTouchMove, { passive: false });
document.addEventListener('touchend', onTouchEnd);
}
function decodeURIComponentSafe(s) {
try { return decodeURIComponent(s); } catch { return s; }
}
// 正規表現の特殊文字をエスケープする
function escapeRegExp(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
// “ ” 『』などのスマート引用を ASCII の " に寄せる
function normalizeQuotes(s) {
return String(s).replace(/[\u201C\u201D\u300C\u300D\uFF02]/g, '"');
}
// 解析前に軽く正規化(URL から来る %22..., 連続空白など)
function normalizeForParse(s) {
if (!s) return '';
let out = String(s);
// URL っぽいエンコードだけ軽く剥がす(%22 等)
if (/%[0-9A-Fa-f]{2}/.test(out)) out = decodeURIComponentSafe(out);
out = normalizeQuotes(out);
// 制御文字を潰し、空白を整形
out = out.replace(/\s+/g, ' ').trim();
return out;
}
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
// ── OR/引用のための簡易トークナイザ
function tokenizeQuotedWords(s) {
const out = [];
let cur = '';
let inQ = false;
for (let i = 0; i < s.length; i++) {
const c = s[i];
if (c === '"') { inQ = !inQ; cur += c; continue; }
if (!inQ && /\s/.test(c)) { if (cur) { out.push(cur); cur=''; } }
else { cur += c; }
}
if (cur) out.push(cur);
return out.filter(Boolean);
}
// トップレベルの OR で文字列を分割(引用/括弧を考慮)
function splitTopLevelOR(str) {
const parts = [];
let cur = '';
let inQ = false, depth = 0;
for (let i = 0; i < str.length; ) {
const c = str[i];
if (c === '"') { inQ = !inQ; cur += c; i++; continue; }
if (!inQ && (c === '(' || c === ')')) { depth += (c === '(' ? 1 : -1); cur += c; i++; continue; }
if (!inQ && depth === 0) {
// 単語境界の "or" / "OR"
if ((str.slice(i, i+2).toLowerCase() === 'or') &&
(i === 0 || /\s|\(/.test(str[i-1] || '')) &&
(i+2 >= str.length || /\s|\)/.test(str[i+2] || ''))) {
parts.push(cur.trim());
cur = '';
i += 2;
continue;
}
}
cur += c; i++;
}
if (cur.trim()) parts.push(cur.trim());
return parts.length > 1 ? parts : null;
}
// OR 専用判定(演算子/否定/括弧が無い素の OR 群なら true)
function isPureORQuery(q) {
const hasOps = /(?:^|\s)(?:from:|to:|lang:|filter:|is:|min_replies:|min_faves:|min_retweets:|since:|until:)\b/i.test(q);
const hasNeg = /(^|\s)-\S/.test(q);
const hasPar = /[()]/.test(q);
return !hasOps && !hasNeg && !hasPar;
}
function waitForElement(selector, timeout = 10000, checkProperty = null) {
return new Promise((resolve) => {
const checkInterval = 100;
let elapsedTime = 0;
const intervalId = setInterval(() => {
const element = document.querySelector(selector);
if (element) {
if (checkProperty) {
if (element[checkProperty]) {
clearInterval(intervalId);
resolve(element);
return;
}
} else {
clearInterval(intervalId);
resolve(element);
return;
}
}
elapsedTime += checkInterval;
if (elapsedTime >= timeout) {
clearInterval(intervalId);
resolve(null);
}
}, checkInterval);
});
}
function hideUIImmediately(modal, trigger) {
if (modal) modal.style.display = 'none';
if (trigger) trigger.style.display = 'none';
}
// ▼ ルート適用を軽く検証(URL一致 + プロフィール系DOMが現れたか)
function waitForRouteApply(path, timeoutMs = 2000) {
const goal = new URL(path, location.origin).pathname;
// ルート毎の判定を用意(必要に応じて拡張)
const perRouteProbes = [
// 検索ページ:検索結果タイムライン or 検索ボックス or 何かしらのツイート
{ test: p => p.startsWith('/search'),
sels: [
'[aria-label*="Search results"], [aria-label*="検索結果"]',
'div[data-testid="primaryColumn"] input[data-testid="SearchBox_Search_Input"]',
'div[data-testid="primaryColumn"] article[data-testid="tweet"]'
] },
// プロフィール
{ test: p => /^\/[A-Za-z0-9_]{1,50}\/?$/.test(p),
sels: [
'[data-testid="UserName"]',
'div[data-testid="UserProfileHeader_Items"]',
'div[data-testid="UserDescription"]'
] },
// デフォルト(保険):主要カラムに何かレンダされたらOK
{ test: _ => true,
sels: [
'div[data-testid="primaryColumn"]',
'main[role="main"]'
] }
];
const probes = (perRouteProbes.find(x => x.test(goal)) || perRouteProbes.at(-1)).sels;
return new Promise(resolve => {
const t0 = performance.now();
(function tick() {
const elapsed = performance.now() - t0;
const urlOk = location.pathname === goal;
const domOk = probes.some(sel => document.querySelector(sel));
if (urlOk && domOk) return resolve(true);
if (elapsed >= timeoutMs) return resolve(false);
// 立ち上がりは速く、以後はやや疎にポーリング
setTimeout(tick, elapsed < 300 ? 60 : elapsed < 700 ? 120 : 180);
})();
});
}
// ▼ SPA 遷移の核。pushState → 合成 popstate → DOM適用待ち → 失敗ならフォールバック
async function spaNavigate(path, { ctrlMeta = false, timeoutMs = 1200 } = {}) {
try {
const to = new URL(path, location.origin);
if (to.origin !== location.origin) throw new Error('cross-origin');
history.pushState(history.state, '', to.pathname + to.search + to.hash);
// X のルーターは popstate を購読している想定
window.dispatchEvent(new PopStateEvent('popstate', { state: history.state }));
const ok = await waitForRouteApply(to.pathname, timeoutMs);
if (ok) return; // 成功
} catch (e) {
// fall through to fallback
}
// フォールバック:修飾キーありなら新規タブ、なければ通常遷移
if (ctrlMeta) window.open(path, '_blank', 'noopener');
else location.assign(path);
}
const uid = () => Math.random().toString(36).slice(2) + Date.now().toString(36);
let isUpdating = false;
let manualOverrideOpen = false;
const lastHistory = { q: null, pf: null, lf: null, ts: 0 };
// ▼ パース結果をキャッシュ(スクロール時の再パース防止)
let __cachedSearchTokens = null;
let __cachedSearchQuery = null; // このクエリ文字列で __cachedSearchTokens が生成された
// ▼ 入力中ガード(IME合成を含めてカバー)
let __typingGuardUntil = 0;
const TYPING_GRACE_MS = 600; // 入力終了からこのmsはスキャン停止
const markTyping = () => { __typingGuardUntil = Date.now() + TYPING_GRACE_MS; };
const isTyping = () => Date.now() < __typingGuardUntil;
const isMediaViewPath = (pathname) => /\/status\/\d+\/(?:photo|video|media|analytics)(?:\/\d+)?\/?$/.test(pathname);
const isComposePath = (pathname) => /^\/compose\/post(?:\/|$)/.test(pathname);
const isProfileMediaPath = (pathname) => /^\/[A-Za-z0-9_]{1,50}\/(?:photo|header_photo)\/?$/.test(pathname);
const isBroadcastPath = (pathname) => /^\/i\/broadcasts\//.test(pathname);
const isBlockedPath = (pathname) => isMediaViewPath(pathname) || isComposePath(pathname) || isProfileMediaPath(pathname) || isBroadcastPath(pathname);
GM_addStyle(`
:root { --modal-primary-color:#1d9bf0; --modal-primary-color-hover:#1a8cd8; --modal-primary-text-color:#fff; }
#advanced-search-trigger { position:fixed; top:18px; right:20px; z-index:9999; background-color:var(--modal-primary-color); color:var(--modal-primary-text-color); border:none; border-radius:50%; width:50px; height:50px; font-size:24px; cursor:pointer; box-shadow:0 4px 12px rgba(0,0,0,0.15); display:flex; align-items:center; justify-content:center; transition:transform .2s, background-color .2s; }
#advanced-search-trigger:hover { transform:scale(1.1); background-color:var(--modal-primary-color-hover); }
#advanced-search-modal { position:fixed; z-index:10000; width:450px; display:none; flex-direction:column; font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif; background-color:var(--modal-bg, #000); color:var(--modal-text-primary, #e7e9ea); border:1px solid var(--modal-border, #333); border-radius:16px; box-shadow:0 8px 24px rgba(29,155,240,.2); transition:background-color .2s,color .2s,border-color .2s; }
.adv-modal-header{padding:12px 16px;border-bottom:1px solid var(--modal-border,#333);cursor:move;display:flex;justify-content:space-between;align-items:center}
.adv-modal-title-left{display:flex;align-items:center;gap:8px;}
.adv-modal-header h2{margin:0;font-size:18px;font-weight:700}
.adv-settings-btn{
margin-left:6px;
width:26px;height:26px;
border-radius:9999px;
border:1px solid var(--modal-input-border,#38444d);
background:var(--modal-input-bg,#202327);
display:inline-flex;
align-items:center;
justify-content:center;
cursor:pointer;
padding:0;
}
.adv-settings-btn:hover{
background-color:var(--modal-button-hover-bg,rgba(231,233,234,.1));
}
.adv-settings-btn svg{
width:14px;
height:14px;
}
.adv-modal-close{background:0 0;border:none;color:var(--modal-close-color,#e7e9ea);font-size:24px;cursor:pointer;width:32px;height:32px;border-radius:50%;display:flex;align-items:center;justify-content:center;transition:background-color .2s}
.adv-modal-close:hover{background-color:var(--modal-close-hover-bg,rgba(231,233,234,.1))}
.adv-modal-body{flex:1;overflow-y:auto;padding:0}
.adv-form-group{margin-bottom:16px}
.adv-form-group label{display:block;margin-bottom:6px;font-size:14px;font-weight:700;color:var(--modal-text-secondary,#8b98a5)}
.adv-form-group input[type=text],.adv-form-group input[type=number],.adv-form-group input[type=date],.adv-form-group select{width:100%;background-color:var(--modal-input-bg,#202327);border:1px solid var(--modal-input-border,#38444d);border-radius:4px;padding:8px 12px;color:var(--modal-text-primary,#e7e9ea);font-size:15px;box-sizing:border-box}
.adv-form-group input:focus,.adv-form-group select:focus{outline:0;border-color:var(--modal-primary-color)}
.adv-form-group input::placeholder{color:var(--modal-text-secondary,#536471)}
.adv-form-group-date-container {display:flex;gap:8px;align-items: center;}
.adv-form-group-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 6px;
gap: 12px;
}
.adv-form-group-header label {
margin-bottom: 0;
white-space: nowrap;
flex-shrink: 0;
}
.adv-select-mini {
background-color: var(--modal-input-bg, #202327);
color: var(--modal-text-primary, #e7e9ea);
border: 1px solid var(--modal-input-border, #38444d);
border-radius: 18px !important;
font-size: 13px !important;
height: 34px;
line-height: normal;
padding: 0 8px;
width: auto;
min-width: 90px;
max-width: 200px;
text-overflow: ellipsis;
cursor: pointer;
outline: none;
}
.adv-select-mini:hover {
border-color: var(--modal-text-secondary, #8b98a5);
}
.adv-select-mini:focus {
border-color: var(--modal-primary-color);
}
.adv-form-group-date-container input[type=date] {flex:1;min-width: 0;width: auto !important;}
.adv-date-separator {color:var(--modal-text-secondary, #8b98a5);font-weight:700;user-select:none;flex-shrink:0;padding: 0 2px;}
.adv-filter-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px}
.adv-checkbox-group{background-color:var(--modal-input-bg,#202327);border:1px solid var(--modal-input-border,#38444d);border-radius:8px;padding:10px;display:flex;flex-direction:column;gap:8px}
.adv-checkbox-group span{font-weight:700;font-size:14px;color:var(--modal-text-primary,#e7e9ea)}
.adv-checkbox-item{display:flex;align-items:center}
.adv-checkbox-item input{margin-right:8px; accent-color:var(--modal-primary-color);}
.adv-checkbox-item label{color:var(--modal-text-secondary,#8b98a5);margin-bottom:0}
.adv-checkbox-item input[type="checkbox"]:disabled {opacity:0.5; cursor:not-allowed;}
.adv-checkbox-item input[type="checkbox"]:disabled + label {opacity:0.5;cursor:not-allowed;text-decoration:line-through;}
.adv-modal-footer{padding:12px 16px;border-top:1px solid var(--modal-border,#333);display:flex;justify-content:flex-end;gap:12px}
.adv-modal-button{padding:5px 16px;border-radius:9999px;border:1px solid var(--modal-text-secondary,#536471);background-color:transparent;color:var(--modal-text-primary,#e7e9ea);font-weight:700;cursor:pointer;transition:background-color .2s}
.adv-modal-button:hover{background-color:var(--modal-button-hover-bg,rgba(231,233,234,.1))}
.adv-modal-button.primary,
.adv-chip.primary {
background-color:var(--modal-primary-color);
border-color:var(--modal-primary-color);
color:var(--modal-primary-text-color);
}
.adv-modal-button.primary:hover{background-color:var(--modal-primary-color-hover)}
.adv-modal-button[disabled]{opacity:.5; cursor:not-allowed;}
#adv-settings-import.adv-modal-button[disabled]{opacity:1;}
.adv-modal-body::-webkit-scrollbar{width:8px}
.adv-modal-body::-webkit-scrollbar-track{background:var(--modal-scrollbar-track,#202327)}
.adv-modal-body::-webkit-scrollbar-thumb{background:var(--modal-scrollbar-thumb,#536471);border-radius:4px}
body.adv-dragging{user-select:none}
.adv-account-label-group{display:flex;justify-content:space-between;align-items:center;margin-bottom:6px}
.adv-exclude-toggle{display:flex;align-items:center}
.adv-exclude-toggle input{margin-right:4px}
.adv-exclude-toggle label{font-size:13px;font-weight:normal;color:var(--modal-text-secondary,#8b98a5);cursor:pointer}
hr.adv-separator{border:none;height:1px;background-color:var(--hr-color,#333);margin:20px 0;transition:background-color .2s}
/* ★全タブ共通のズーム対象に拡張(検索タブの既存idにも適用維持) */
.adv-zoom-root, #adv-zoom-root{ transform-origin: top left; will-change: transform; padding:12px 11.6px 10px 11px; }
#adv-zoom-root {
padding-top: 16px; /* 検索タブの上余白だけを 16px に上書き */
padding-left:16px; padding-right:20px;
}
.adv-modal-body{ overflow:auto; }
.adv-form-row.two-cols { display:grid; grid-template-columns:1fr 1fr; gap:10px; }
@media (max-width: 480px) { .adv-form-row.two-cols { grid-template-columns:1fr; } }
.adv-tabs {
display: flex;
border-bottom: 1px solid var(--modal-border, #333);
padding: 0 8px 0 6px;
gap: 4px;
align-items: stretch;
flex-wrap: wrap;
container-type: inline-size;
/* ▼ 固定表示設定 */
position: sticky;
top: 0;
z-index: 10;
background-color: var(--modal-bg, #000);
box-shadow: var(--modal-tabs-shadow);
}
.adv-tab-btn {
appearance: none;
border: none;
background: transparent;
color: var(--modal-text-secondary, #8b98a5);
padding: 10px 8px;
cursor: pointer;
font-weight: 700;
border-radius: 8px 8px 0 0;
font-size: 0.78rem;
/* ボタン内のテキストは折り返さない */
white-space: nowrap;
/* 余ったスペースを全員で分け合う(均等配置・最大化) */
flex: 1 1 auto;
text-align: center;
/* なめらかな変化 */
transition: font-size 0.1s, padding 0.1s, background-color 0.2s;
}
.adv-tab-btn.active {
color: var(--modal-text-primary, #e7e9ea);
background-color: var(--modal-input-bg, #202327);
border: 1px solid var(--modal-input-border, #38444d);
border-bottom: none;
/* アクティブタブは少し強調 */
z-index: 1;
}
/* ▼▼▼ コンテナクエリ: 幅に応じて最適化 ▼▼▼ */
/* 幅 480px 以下: フォントを少し小さくし、1行収まりを狙う */
@container (max-width: 480px) {
.adv-tab-btn {
font-size: 12px;
padding: 8px 4px;
}
}
/* 幅 380px 以下: さらにフォントを詰め、もし2行になっても違和感ないサイズに */
@container (max-width: 380px) {
.adv-tab-btn {
font-size: 11px;
padding: 6px 2px;
border-radius: 6px; /* 角丸も少し小さく */
}
/* 2行になった際に上下の列がくっつきすぎないようにする */
.adv-tabs {
row-gap: 2px;
}
/* 2行目のボーダー処理(見た目を整える) */
.adv-tab-btn.active {
border-bottom: 1px solid var(--modal-input-bg, #202327);
margin-bottom: -1px;
}
}
.adv-tab-content { display:none; }
.adv-tab-content.active { display:block; }
.adv-secret-wrap { display:flex; align-items:center; gap:8px; }
.adv-secret-btn { cursor:pointer; border:1px solid var(--modal-input-border,#38444d); background:var(--modal-input-bg,#202327); color:var(--modal-text-primary,#e7e9ea); padding:4px 8px; border-radius:9999px; font-weight:700; user-select:none; display:flex; align-items:center; gap:6px; font-size:12px; }
.adv-secret-btn .dot { width:7px; height:7px; border-radius:50%; background:#777; box-shadow:0 0 0px #0000; transition:all .2s; }
.adv-secret-btn.off { opacity:0.9; }
.adv-secret-btn.on { background-color:var(--modal-primary-color); border-color:var(--modal-primary-color); color:var(--modal-primary-text-color); }
.adv-secret-btn.on .dot { background:#fff; box-shadow:0 0 8px rgba(255,255,255,.9); }
.adv-list { display:flex; flex-direction:column; gap:8px; }
.adv-item { position: relative; border:1px solid var(--modal-input-border,#38444d); background:var(--modal-input-bg,#202327); border-radius:8px; padding:8px; display:flex; gap:8px; align-items:flex-start; }
.adv-item.dragging { opacity:.6; }
.adv-item-handle { cursor:grab; user-select:none; padding:4px 6px; border-radius:6px; border:1px dashed var(--modal-border,#333); touch-action: none; }
.adv-item-avatar { width:36px; height:36px; border-radius:9999px; object-fit:cover; flex:0 0 auto; background:var(--modal-border,#333); }
a.adv-link { color: inherit; text-decoration: none; }
a.adv-link:hover { text-decoration: underline; cursor: pointer; }
.adv-item-avatar-link { display:inline-block; border-radius:9999px; }
.adv-item-main { flex:1; min-width:0; }
.adv-item-title { font-size:14px; font-weight:700; color:var(--modal-text-primary,#e7e9ea); word-break:break-word; display: flex; align-items: center; flex-wrap: wrap; gap: 6px; }
.adv-item-sub { font-size:12px; color:var(--modal-text-secondary,#8b98a5); margin-top:2px; display:flex; gap:6px; flex-wrap:wrap; align-items:center; }
.adv-item-actions { display:flex; gap:6px; align-items:center; align-self:center; }
.adv-chip { border:1px solid var(--modal-input-border,#38444d); background:transparent; color:var(--modal-text-primary,#e7e9ea); padding:4px 8px; border-radius:9999px; font-size:12px; cursor:pointer; }
.adv-fav-btn-pos { position: absolute; right: 8px; }
.adv-fav-btn-top { top: 8px; }
.adv-fav-btn-bottom { bottom: 8px; }
.adv-chip.danger { border-color:#8b0000; color:#ffb3b3; }
.adv-modal-button.danger {
border-color:#8b0000;
color:#ffb3b3;
}
.adv-modal-button.danger:hover{
background-color:rgba(139,0,0,0.2);
}
.adv-chip.scope { padding:2px 6px; font-size:11px; line-height:1.2; opacity:0.95; }
.adv-toast { position:fixed; z-index:10001; left:50%; transform:translateX(-50%); bottom:24px; background:#111a; color:#fff; backdrop-filter: blur(6px); border:1px solid #fff3; padding:8px 12px; border-radius:8px; font-weight:700; opacity:0; pointer-events:none; transition:opacity .2s, transform .2s; }
.adv-toast.show { opacity:1; transform:translateX(-50%) translateY(-6px); }
.adv-modal-footer { justify-content:flex-end; }
.adv-modal-footer .adv-modal-button#adv-save-button { margin-right:auto; }
.adv-tab-toolbar {
display:flex;
justify-content: space-between;
align-items: center;
gap: 8px;
flex-wrap: wrap;
margin-bottom:12px;
padding: 0 2px;
}
/* ツールバーの左側(検索・ソート) */
.adv-tab-toolbar-left {
display: flex;
align-items: center;
gap: 8px;
flex: 1 1 auto;
min-width: 150px;
}
/* ツールバーの右側(すべて削除ボタン) */
.adv-tab-toolbar-right {
display: flex;
flex: 0 0 auto;
}
/* ツールバー入力欄の共通スタイル */
.adv-select, .adv-input {
background-color:var(--modal-input-bg,#202327);
border:1px solid var(--modal-input-border,#38444d);
border-radius:8px;
padding:6px 10px;
color:var(--modal-text-primary,#e7e9ea);
}
/* 検索ボックスとセレクトボックスのスタイル(.adv-folder-toolbar内と共通化) */
/* 共通スタイルは .adv-input, .adv-select が担当 */
.adv-tab-toolbar .adv-input {
flex: 1;
min-width: 80px;
}
.adv-tab-toolbar .adv-select {
flex: 0 1 auto;
}
[data-testid="cellInnerDiv"][data-adv-hidden],
article[data-adv-hidden] {
display:none !important;
content-visibility: hidden;
contain: strict;
}
#advanced-search-modal { max-height:none; }
.adv-resizer { position:absolute; z-index:10002; background:transparent; }
.adv-resizer.e, .adv-resizer.w { top:-3px; bottom:-3px; width:8px; }
.adv-resizer.e { right:-3px; cursor: ew-resize; }
.adv-resizer.w { left:-3px; cursor: ew-resize; }
.adv-resizer.n, .adv-resizer.s { left:-3px; right:-3px; height:8px; }
.adv-resizer.n { top:-3px; cursor: ns-resize; }
.adv-resizer.s { bottom:-3px; cursor: ns-resize; }
.adv-resizer.se, .adv-resizer.ne, .adv-resizer.sw, .adv-resizer.nw { width:12px; height:12px; }
.adv-resizer.se { right:-4px; bottom:-4px; cursor:nwse-resize; }
.adv-resizer.ne { right:-4px; top:-4px; cursor:nesw-resize; }
.adv-resizer.sw { left:-4px; bottom:-4px; cursor:nesw-resize; }
.adv-resizer.nw { left:-4px; top:-4px; cursor:nwse-resize; }
/* ▶ Mute タブ */
.adv-mute-add { display:flex; gap:8px; align-items:center; margin-bottom:10px; }
.adv-mute-add input[type=text]{ flex:1; border-radius:8px; padding: 6px 10px; font-size: 14px; }
.adv-mute-list { display:flex; flex-direction:column; gap:8px; }
/* ▼ グローバル無効(マスターOFF)のとき:リスト全体を淡く */
.adv-mute-list.disabled {
opacity: .6;
filter: grayscale(35%);
}
/* ▼ 個別無効(enabled=false)の行だけ淡く+打ち消し等の視覚 */
.adv-mute-item {
border:1px solid var(--modal-input-border,#38444d);
background:var(--modal-input-bg,#202327);
border-radius:8px;
padding:8px 10px;
display:flex;
gap:10px;
justify-content: space-between;
align-items:center;
transition: opacity .15s ease, filter .15s ease, border-color .15s ease;
}
.adv-mute-item.disabled {
opacity: .55;
filter: grayscale(25%);
border-color: color-mix(in oklab, var(--modal-input-border,#38444d), transparent 20%);
}
.adv-mute-item.disabled .adv-mute-word {
color: var(--modal-text-secondary,#8b98a5);
text-decoration: line-through;
}
/* 左側のコンテナ(単語+オプション) */
.adv-mute-content-left {
display: flex;
flex-direction: column;
gap: 4px;
flex: 1;
min-width: 0;
}
.adv-mute-word {
font-weight:700;
color:var(--modal-text-primary,#e7e9ea);
word-break:break-word;
font-size: 14px;
}
/* 左下のオプション群 */
.adv-mute-options-row {
display: flex;
gap: 12px;
align-items: center;
}
/* 右側のコンテナ(削除ボタンのみ) */
.adv-mute-actions-right {
display:flex;
align-items:center;
justify-content:center;
flex: 0 0 auto;
white-space: nowrap;
padding-left: 4px;
}
@media (max-width: 480px) {
.adv-mute-actions { margin-top: 4px; }
}
.adv-toggle {
display: inline-flex;
gap: 6px;
align-items: center;
color: var(--modal-text-secondary,#8b98a5);
line-height: 1;
margin-bottom:0!important;
}
.adv-toggle input[type="checkbox"] {
width: 14px;
height: 14px;
margin: 0;
flex: 0 0 auto;
vertical-align: middle;
}
.adv-toggle span {
font-size: 11px;
line-height: 1;
}
/* ▼▼▼ Mute Header Fix ▼▼▼ */
.adv-mute-header {
display:flex;
justify-content:space-between;
align-items:center;
margin: 4px 0 12px;
gap: 10px;
flex-wrap: nowrap; /* 折り返しを禁止して1行に強制 */
}
.adv-mute-header input[type="text"] {
flex: 1;
min-width: 0;
border-radius: 8px;
padding: 6px 10px;
font-size: 14px;
background-color: var(--modal-input-bg,#202327);
border: 1px solid var(--modal-input-border,#38444d);
color: var(--modal-text-primary,#e7e9ea);
}
.adv-mute-header input[type="text"]:focus {
outline: 0;
border-color: var(--modal-primary-color);
}
.adv-mute-title {
font-weight:700;
color: var(--modal-text-primary,#e7e9ea);
white-space: nowrap; /* テキスト折り返し禁止 */
overflow: hidden;
text-overflow: ellipsis; /* 溢れたら...にする */
flex-shrink: 1; /* 幅不足時はタイトル側を縮める */
min-width: 0;
}
.adv-mute-header-controls {
display: flex;
align-items: center;
gap: 8px; /* 余白を少し詰める */
flex-shrink: 0; /* 操作パネルは縮めない */
}
#adv-mute-mode {
padding: 3px 24px 3px 8px; /* 矢印スペース考慮 */
font-size: 12px;
height: 28px;
cursor: pointer;
width: auto;
}
/* マスター切替の一瞬だけ付けるガードクラス */
.adv-no-anim, .adv-no-anim * {
transition: none !important;
}
#adv-history-empty:not(:empty),
#adv-saved-empty:not(:empty),
#adv-favorites-empty:not(:empty),
#adv-accounts-empty:not(:empty),
#adv-lists-empty:not(:empty) {
padding-inline: 7px;
}
#adv-mute-empty:not(:empty) {
padding-top: 6px;
}
/* ▼ マスターOFF中は、個別無効の“さらに薄く”を抑制(親の薄さのみ適用) */
.adv-mute-list.disabled .adv-mute-item.disabled {
opacity: 1; /* 子の追加の薄さを無効化(親のopacityのみが効く) */
filter: none; /* 子の追加グレースケールも無効化(親側のfilterのみ適用) */
/* ボーダーだけ通常色に戻す */
/* border-color: var(--modal-input-border,#38444d); */
}
/* === Trigger: モーダルと同質の見た目に合わせる === */
#advanced-search-trigger.adv-trigger-search {
width: 49px; height: 49px;
border-radius: 9999px;
background-color: var(--modal-bg, #000);
color: var(--modal-text-primary, #e7e9ea);
border: 2px solid var(--modal-border, #2f3336); /* ← モーダルと同じ枠色 */
box-shadow: 0 8px 24px rgba(29,155,240,.2); /* ← モーダルと同じshadow */
display:flex; align-items:center; justify-content:center;
transition: transform .15s ease, box-shadow .15s ease, border-color .15s ease;
}
#advanced-search-trigger.adv-trigger-search:hover {
/* 背景は変えず、浮かせる表現だけ強化 */
transform: translateZ(0) scale(1.04);
box-shadow: 0 12px 36px rgba(29,155,240,.28);
border-color: var(--modal-border, #2f3336);
}
#advanced-search-trigger.adv-trigger-search:active {
transform: translateZ(0) scale(0.98);
box-shadow: 0 6px 18px rgba(29,155,240,.22);
}
#advanced-search-trigger.adv-trigger-search:focus-visible {
outline: none;
box-shadow:
0 8px 24px rgba(29,155,240,.2),
0 0 0 3px color-mix(in oklab, var(--modal-primary-color, #1d9bf0) 45%, transparent);
}
#advanced-search-trigger.adv-trigger-search svg {
width: 22px; height: 22px;
display:block;
/* 検索アイコンは stroke="currentColor" を使っているので配色は自動追従 */
}
/* リストコンテナ自体に十分な高さを確保し、下部にドロップ用の余白を強制的に広げる */
#adv-accounts-list,
#adv-lists-list,
#adv-saved-list {
min-height: 200px; /* アイテムが空でもドロップできるようにする */
padding-bottom: 20px;
box-sizing: content-box; /* padding分を確実に高さに加える */
}
/* 未分類セクションが空の時も、ドラッグ中は少し広げて受け入れやすくする */
body.adv-dragging .adv-unassigned {
min-height: 60px;
background-color: rgba(128, 128, 128, 0.05); /* 視覚的にエリアを暗示 */
border-radius: 8px;
transition: min-height 0.2s ease, background-color 0.2s;
}
/* === Folders === */
.adv-folder { border:1px solid var(--modal-input-border,#38444d); border-radius:10px; margin-bottom:10px; }
.adv-folder-header {
display:flex; justify-content:space-between; align-items:center;
padding:8px 10px; background:var(--modal-input-bg,#202327); border-bottom:1px solid var(--modal-input-border,#38444d);
}
.adv-folder[data-drop="1"] { outline:2px dashed var(--modal-primary-color); outline-offset:-2px; }
.adv-folder-title { display:flex; gap:8px; align-items:baseline; }
.adv-folder-actions { display:flex; gap:6px; }
.adv-folder-toolbar { display:flex; gap:8px; align-items:center; margin:0 0 12px; padding:0 2px; }
.adv-folder-toolbar input[type="text"] { flex:1; min-width:80px; }
.adv-folder-collapsed .adv-list { display:none; }
/* ▶ Folder headers: show grab cursor except on action buttons */
.adv-folder-header { cursor: grab; touch-action: none; }
.adv-folder-header:active { cursor: grabbing; }
/* ボタン上では通常のポインタ(=ドラッグ開始させない見た目) */
.adv-folder-header .adv-folder-actions,
.adv-folder-header .adv-folder-actions * {
cursor: pointer;
}
/* ▼ トグルボタン(左端) */
.adv-folder-toggle {
appearance: none;
border: none;
background: transparent;
display: inline-flex;
align-items: center;
justify-content: center;
width: 28px;
height: 28px;
border-radius: 6px;
cursor: pointer;
margin-right: 6px;
}
.adv-folder-toggle:focus-visible {
outline: none;
box-shadow: 0 0 0 2px color-mix(in oklab, var(--modal-primary-color, #1d9bf0) 60%, transparent);
}
/* ▼ アイコン(chevron) */
.adv-folder-toggle svg {
width: 16px; height: 16px;
transition: transform .15s ease;
}
/* ▼ 開閉で向きを変える(右▶ → 下▼) */
.adv-folder:not(.adv-folder-collapsed) .adv-folder-toggle svg {
transform: rotate(90deg);
}
/* ▼ 開いているヘッダーはわずかに背景強調 */
.adv-folder:not(.adv-folder-collapsed) .adv-folder-header {
background: color-mix(in oklab, var(--modal-input-bg,#202327) 92%, var(--modal-primary-color,#1d9bf0));
}
/* ▼ ドラッグハンドルは“掴める”見た目を強調 */
.adv-folder-drag-handle {
cursor: grab;
user-select: none;
padding: 4px 6px;
border-radius: 6px;
border: 1px dashed var(--modal-border,#38444d);
}
.adv-folder-drag-handle:active { cursor: grabbing; }
/* ▼ Unassigned セクション(見出しなし・枠なし) */
.adv-unassigned {
margin-bottom: 10px;
min-height: 30px; /* ★ 空の時でもドロップできるように最小高さを確保 */
}
.adv-unassigned .adv-list {
display: flex;
flex-direction: column;
gap: 8px;
}
/* フォルダー並び替え用のドラッグ時の視覚(Unassigned も対象) */
.adv-unassigned.dragging-folder {
opacity: .6;
}
/* タブ背景およびリストコンテナ背景へのドロップハイライト */
#adv-tab-accounts.adv-bg-drop-active,
#adv-tab-lists.adv-bg-drop-active,
#adv-tab-saved.adv-bg-drop-active,
#adv-accounts-list.adv-bg-drop-active,
#adv-lists-list.adv-bg-drop-active,
#adv-saved-list.adv-bg-drop-active {
outline: 2px dashed var(--modal-primary-color, #1d9bf0);
/* リストコンテナ側はパディングが無いためオフセットを小さく */
outline-offset: -4px;
}
/* タブパネル(上部余白)側は既存のオフセットを維持 */
#adv-tab-accounts.adv-bg-drop-active,
#adv-tab-lists.adv-bg-drop-active,
#adv-tab-saved.adv-bg-drop-active {
outline-offset: -8px;
}
/* 背景(Unassigned 宛て)をドロップ中は、フォルダー内の“薄い残像”を消す */
#adv-tab-accounts.adv-bg-drop-active .adv-list .adv-item.dragging,
#adv-accounts-list.adv-bg-drop-active .adv-list .adv-item.dragging,
#adv-tab-lists.adv-bg-drop-active .adv-list .adv-item.dragging,
#adv-lists-list.adv-bg-drop-active .adv-list .adv-item.dragging,
#adv-tab-saved.adv-bg-drop-active .adv-list .adv-item.dragging,
#adv-saved-list.adv-bg-drop-active .adv-list .adv-item.dragging {
display: none !important;
}
/* === Settings modal === */
#adv-settings-modal.adv-settings-modal{
position:fixed;
inset:0;
z-index:10001;
display:none;
align-items:center;
justify-content:center;
background:rgba(0,0,0,.5);
}
.adv-settings-dialog{
width:420px;
max-width:90vw;
max-height:80vh;
background-color:var(--modal-bg,#000);
color:var(--modal-text-primary,#e7e9ea);
border-radius:16px;
border:1px solid var(--modal-border,#333);
box-shadow:0 8px 24px rgba(0,0,0,.3);
display:flex;
flex-direction:column;
overflow:hidden;
font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
}
.adv-settings-header{
padding:12px 16px;
border-bottom:1px solid var(--modal-border,#333);
display:flex;
align-items:center;
justify-content:space-between;
}
.adv-settings-title{
margin:0;
font-size:16px;
font-weight:700;
}
.adv-settings-close{
border:none;
background:transparent;
color:var(--modal-close-color,#e7e9ea);
font-size:20px;
width:32px;
height:32px;
border-radius:50%;
display:flex;
align-items:center;
justify-content:center;
cursor:pointer;
}
.adv-settings-close:hover{
background-color:var(--modal-close-hover-bg,rgba(231,233,234,.1));
}
.adv-settings-body{
padding:12px 16px 23px 16px;
overflow-y:auto;
display:flex;
flex-direction:column;
gap:16px;
}
.adv-settings-group label{
display:block;
margin-bottom:4px;
font-size:14px;
font-weight:700;
color:var(--modal-text-secondary,#8b98a5);
}
.adv-settings-group select,
.adv-settings-group textarea{
width:100%;
background-color:var(--modal-input-bg,#202327);
border:1px solid var(--modal-input-border,#38444d);
border-radius:8px;
padding:8px 10px;
color:var(--modal-text-primary,#e7e9ea);
font-size:14px;
box-sizing:border-box;
}
.adv-settings-group textarea{
resize:vertical;
min-height:80px;
}
.adv-settings-section-header {
margin: 12px 0 2px 0;
padding-bottom: 4px;
border-bottom: 1px solid var(--modal-border,#333);
font-size: 13px;
font-weight: 700;
color: var(--modal-text-primary,#e7e9ea);
}
.adv-settings-toggle-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: 6px 0;
}
.adv-settings-toggle-row .adv-toggle {
font-size: 14px;
color: var(--modal-text-primary,#e7e9ea);
user-select: none;
cursor: pointer;
}
.adv-settings-toggle-row .adv-toggle span {
font-size: 14px;
}
/* Simple toggle switch CSS */
.adv-switch {
position: relative;
display: inline-block;
width: 40px;
height: 22px;
}
.adv-switch input {
opacity: 0;
width: 0;
height: 0;
}
.adv-slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: var(--modal-input-border,#38444d);
transition: .2s;
border-radius: 22px;
}
.adv-slider:before {
position: absolute;
content: "";
height: 16px;
width: 16px;
left: 3px;
bottom: 3px;
background-color: var(--modal-bg, #000);
transition: .2s;
border-radius: 50%;
}
.adv-switch input:checked + .adv-slider {
background-color: var(--modal-primary-color);
}
.adv-switch input:checked + .adv-slider:before {
transform: translateX(18px);
}
.adv-settings-actions-inline{
display:flex;
gap:8px;
margin-top:6px;
flex-wrap:wrap;
}
.adv-settings-footer{
padding:10px 16px;
border-top:1px solid var(--modal-border,#333);
display:flex;
justify-content:flex-end;
gap:8px;
}
/* === Tab Drag & Drop === */
.adv-tab-btn {
user-select: none;
}
.adv-tab-btn:active {
cursor: grabbing;
}
.adv-tab-btn.dragging {
opacity: .5;
}
/* --- Favorite Tags CSS --- */
/* ▼ ブックマークUI専用の配色変数を定義 */
:root {
/* デフォルト (Dim / Dark) は静的なダークテーマ */
--ft-bg: rgb(21, 24, 28);
--ft-border-light: rgba(239, 243, 244, 0.24);
--ft-border-dim: rgba(239, 243, 244, 0.15);
--ft-border-strong: rgba(239, 243, 244, 0.3);
--ft-border-accent: rgba(239, 243, 244, 0.8);
--ft-text-primary: rgb(239, 243, 244);
--ft-text-secondary: rgba(239, 243, 244, 0.7);
--ft-input-bg: rgba(0,0,0,0.2);
--ft-input-border: rgba(239,243,244,0.2);
--ft-hover-bg: rgba(255, 255, 255, 0.06);
--ft-hover-bg-strong: rgba(255, 255, 255, 0.08);
--ft-accent-color: #1d9bf0;
}
:root.x-theme-light {
/* Lightテーマの時だけ、X本体の動的変数を参照する */
--ft-bg: var(--modal-bg);
--ft-border-light: var(--modal-border);
--ft-border-dim: var(--modal-border);
--ft-border-strong: var(--modal-text-secondary);
--ft-border-accent: var(--modal-text-primary);
--ft-text-primary: var(--modal-text-primary);
--ft-text-secondary: var(--modal-text-secondary);
--ft-input-bg: var(--modal-input-bg);
--ft-input-border: var(--modal-input-border);
--ft-hover-bg: var(--modal-button-hover-bg);
--ft-hover-bg-strong: var(--modal-button-hover-bg);
--ft-accent-color: var(--modal-primary-color);
}
/* Tag chip on tweet header */
.ft-tag-chip {
display: inline-flex;
align-items: center;
margin-left: 4px; /* JS (ft_attachTagChipToArticle) 側の gap: 4px と連動 */
padding: 1px 8px;
border-radius: 9999px;
border: 1px solid currentColor;
font-size: 11px;
line-height: 1.4;
cursor: pointer;
user-select: none;
white-space: nowrap;
background: rgba(255, 255, 255, 0.03); /* これは静的なまま (ほぼ透明なので) */
flex: 0 0 auto;
order: 9999;
}
.ft-tag-chip-label {
max-width: 150px;
overflow: hidden;
text-overflow: ellipsis;
}
.ft-tag-chip-uncategorized {
opacity: 0.7;
}
/* Dropdown for selecting tag / filter */
.ft-tag-dropdown {
position: fixed;
z-index: 2147482000;
min-width: 220px;
max-width: 260px;
max-height: 60vh;
overflow-y: auto;
padding: 8px;
border-radius: 12px;
border: 1px solid var(--ft-border-light);
background: var(--ft-bg);
box-shadow: 0 12px 30px rgba(0, 0, 0, 0.7);
font-size: 13px;
color: var(--ft-text-primary);
}
.ft-tag-dropdown-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 6px;
font-weight: 600;
}
.ft-tag-dropdown-close {
border: none;
background: transparent;
color: inherit;
cursor: pointer;
padding: 2px 4px;
}
.ft-tag-dropdown-tags {
display: flex;
flex-direction: column;
gap: 4px;
margin-bottom: 8px;
}
.ft-tag-dropdown-tag-item {
display: flex;
align-items: center;
padding: 4px 6px;
border-radius: 6px;
cursor: pointer;
}
.ft-tag-dropdown-tag-item:hover {
background: var(--ft-hover-bg);
}
.ft-tag-dropdown-tag-color {
width: 10px;
height: 10px;
border-radius: 9999px;
margin-right: 6px;
}
.ft-tag-dropdown-tag-label {
flex: 1;
}
.ft-tag-dropdown-tag-selected::after {
content: '✓';
margin-left: 6px;
font-size: 11px;
}
/* New tag row in dropdown */
.ft-tag-dropdown-new {
border-top: 1px solid var(--ft-border-dim);
padding-top: 6px;
display: flex;
flex-direction: column;
gap: 4px;
}
.ft-tag-dropdown-new-row {
display: flex;
gap: 4px;
}
.ft-tag-dropdown-new-input {
flex: 1;
background: var(--ft-input-bg);
border: 1px solid var(--ft-input-border);
border-radius: 6px;
padding: 3px 6px;
color: inherit;
}
.ft-tag-dropdown-new-color {
width: 36px;
padding: 0;
border-radius: 6px;
border: 1px solid var(--ft-input-border);
background: transparent;
}
.ft-tag-dropdown-new-button {
border-radius: 6px;
border: 1px solid var(--ft-border-strong);
background: transparent;
color: inherit;
padding: 2px 6px;
font-size: 12px;
cursor: pointer;
}
.ft-tag-dropdown-new-button:hover {
background: var(--ft-hover-bg);
}
/* Bookmark header controls (テーマ変数適用) */
.ft-filter-button {
border-radius: 8px;
border: 1px solid var(--modal-border, rgba(239,243,244,0.3));
background: var(--modal-input-bg, rgba(0,0,0,0.2));
color: var(--modal-text-primary, rgb(239,243,244));
font-size: 14px;
padding: 4px 10px;
display: inline-flex;
align-items: center;
gap: 6px;
cursor: pointer;
}
.ft-filter-button-label {
max-width: 140px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.ft-filter-button-caret {
font-size: 10px;
opacity: 0.8;
}
.ft-filter-button[disabled] {
opacity: 0.4;
cursor: default;
}
.ft-filter-button:not([disabled]):hover {
background: var(--modal-button-hover-bg, rgba(255,255,255,0.06));
border-color: var(--modal-text-secondary, rgba(239,243,244,0.6));
}
.ft-settings-button {
border-radius: 9999px;
width: 26px;
height: 26px;
display: inline-flex;
align-items: center;
justify-content: center;
border: 1px solid var(--modal-border, rgba(239,243,244,0.3));
background: var(--modal-input-bg, rgba(0,0,0,0.2));
color: var(--modal-text-primary, rgb(239,243,244));
cursor: pointer;
}
.ft-settings-button:hover {
background: var(--modal-button-hover-bg, rgba(255,255,255,0.06));
}
/* Settings modal */
.ft-modal-backdrop {
position: fixed;
inset: 0;
background: rgba(0,0,0,0.6);
z-index: 2147483000;
display: flex;
align-items: center;
justify-content: center;
}
.ft-modal {
width: min(380px, 100vw - 32px);
max-height: 80vh;
border-radius: 16px;
background: var(--ft-bg);
border: 1px solid var(--ft-border-light);
box-shadow: 0 20px 40px rgba(0,0,0,0.75);
display: flex;
flex-direction: column;
color: var(--ft-text-primary);
}
.ft-modal-header {
padding: 10px 14px;
border-bottom: 1px solid var(--ft-border-dim);
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
}
.ft-modal-title {
font-size: 14px;
font-weight: 600;
}
.ft-modal-toggle {
display: inline-flex;
align-items: center;
gap: 4px;
font-size: 12px;
}
.ft-modal-toggle input[type="checkbox"] {
accent-color: var(--ft-accent-color);
}
.ft-modal-body {
padding: 10px 14px 12px;
overflow-y: auto;
font-size: 13px;
}
.ft-modal-footer {
padding: 8px 14px 10px;
border-top: 1px solid var(--ft-border-dim);
display: flex;
justify-content: flex-end;
gap: 8px;
}
.ft-modal-button {
border-radius: 9999px;
border: 1px solid var(--ft-border-strong);
background: transparent;
color: inherit;
font-size: 12px;
padding: 4px 10px;
cursor: pointer;
}
.ft-modal-button:hover {
background: var(--ft-hover-bg);
}
/* Display settings section */
.ft-modal-display-settings {
margin-bottom: 10px;
padding-bottom: 8px;
border-bottom: 1px solid var(--ft-border-dim);
font-size: 12px;
}
.ft-modal-display-settings-row {
display: flex;
flex-wrap: wrap;
gap: 8px;
align-items: center;
margin-top: 4px;
}
.ft-modal-display-radio {
display: inline-flex;
align-items: center;
gap: 4px;
}
/* Tag list in modal */
.ft-modal-tag-list {
display: flex;
flex-direction: column;
gap: 6px;
padding-bottom: 30px; /* 余白を大きく取る */
min-height: 100px; /* 空っぽでもドロップできるように */
position: relative; /* ルートドロップの枠線表示用 */
box-sizing: content-box; /* paddingを含めない高さ計算 */
}
/* 一番下の余白にドラッグした時に、リスト全体の下に枠線を出すクラス */
.ft-modal-tag-list.ft-drag-to-root::after {
content: '';
position: absolute;
bottom: 20px; /* 余白の中ほどに線を引く */
left: 0;
right: 0;
height: 2px;
background-color: var(--modal-primary-color, #1d9bf0);
box-shadow: 0 0 4px var(--modal-primary-color, #1d9bf0);
}
.ft-modal-tag-item {
position: relative;
display: grid;
/* [mainCell] [dragHandle] [orderButtons] [deleteBtn] */
grid-template-columns: minmax(0, 1fr) auto auto auto;
align-items: center;
gap: 6px;
/* cursor: grab; を削除 (ハンドルが担当) */
}
.ft-modal-tag-main {
display: flex;
align-items: center;
gap: 6px;
}
.ft-modal-tag-item-dragging {
opacity: 0.6;
}
.ft-modal-tag-item-drop-before::before,
.ft-modal-tag-item-drop-after::after {
content: '';
position: absolute;
left: 0;
right: 0;
height: 1px;
/* 従来の色(白っぽいグレー) */
background-color: var(--ft-border-accent, rgba(239, 243, 244, 0.8));
border: none; /* border-top/bottom を background-color に変更して統一 */
pointer-events: none;
}
.ft-modal-tag-item-drop-before::before { top: -3.5px; }
.ft-modal-tag-item-drop-after::after { bottom: -3.5px; }
/* ルート階層用(青い線) */
.ft-modal-tag-item.ft-is-root-ref.ft-modal-tag-item-drop-before::before,
.ft-modal-tag-item.ft-is-root-ref.ft-modal-tag-item-drop-after::after {
background-color: var(--modal-primary-color, #1d9bf0);
box-shadow: 0 0 4px var(--modal-primary-color, #1d9bf0); /* 発光させて強調 */
height: 2px;
z-index: 10;
}
/* 青い線の位置(太くなった分、あるいは強調のため少し外側に広げる) */
.ft-modal-tag-item.ft-is-root-ref.ft-modal-tag-item-drop-before::before {
top: -4.2px;
}
.ft-modal-tag-item.ft-is-root-ref.ft-modal-tag-item-drop-after::after {
bottom: -4.2px;
}
.ft-modal-tag-item-drop-child {
background: var(--ft-hover-bg-strong);
}
.ft-modal-tag-name {
background: var(--ft-input-bg);
border-radius: 6px;
border: 1px solid var(--ft-input-border);
padding: 3px 6px;
color: inherit;
font-size: 12px;
}
.ft-modal-tag-color {
width: 40px;
padding: 0;
border-radius: 6px;
border: 1px solid var(--ft-input-border);
background: transparent;
}
.ft-modal-tag-order,
.ft-modal-tag-delete {
border-radius: 6px;
border: 1px solid var(--ft-border-strong);
background: transparent;
color: inherit;
padding: 2px 4px;
cursor: pointer;
font-size: 11px;
}
.ft-modal-tag-order:hover,
.ft-modal-tag-delete:hover {
background: var(--ft-hover-bg-strong);
}
/* --- Drag handle for tag settings --- */
.ft-modal-tag-drag-handle {
display: inline-flex;
align-items: center;
justify-content: center;
width: 20px;
height: 20px;
border-radius: 4px;
cursor: grab;
color: var(--ft-text-secondary);
user-select: none;
touch-action: none;
}
.ft-modal-tag-drag-handle:hover {
background: var(--ft-hover-bg-strong);
color: var(--ft-text-primary);
}
/* Uncategorized: disable drag */
.ft-modal-tag-item[data-kind="uncat"] .ft-modal-tag-drag-handle {
cursor: not-allowed;
opacity: 0.5;
}
/* New tag row */
.ft-modal-new-tag {
border-top: 1px solid var(--ft-border-dim);
padding-top: 8px;
display: flex;
flex-direction: column;
gap: 6px;
}
.ft-modal-new-tag-row {
display: grid;
grid-template-columns: auto minmax(0, 1fr) auto;
gap: 6px;
}
/* 未分類は名前変更不可&削除不可の視覚表現 */
.ft-modal-tag-name[readonly] {
cursor: not-allowed;
opacity: 0.8;
}
.ft-modal-tag-delete:disabled {
cursor: not-allowed;
opacity: 0.4;
}
/* Hidden helper */
.ft-hidden {
display: none !important;
content-visibility: hidden;
contain: strict;
}
/* --- End Favorite Tags CSS --- */
/* --- Favorites Feature --- */
.adv-fav-btn {
display: inline-flex; align-items: center; justify-content: center;
background: transparent; border: none; cursor: pointer;
color: rgb(83, 100, 113); /* Default grey */
padding: 0; margin: 0;
width: 34.75px; height: 34.75px; /* X standard icon size touch target */
border-radius: 50%;
transition: background-color 0.2s, color 0.2s;
}
/* ネイティブのクラスを借用した時は固定サイズを無効化する */
.adv-fav-btn.adv-native-style {
width: auto;
height: auto;
min-width: 34.75px; /* 最低限の大きさは確保 */
min-height: 34.75px;
}
.adv-fav-btn:hover {
background-color: rgba(29, 155, 240, 0.1);
color: rgb(29, 155, 240);
}
.adv-fav-btn.active {
color: rgb(249, 24, 128); /* Pink/Red like Like, or Gold? Let's use Gold for Star */
color: rgb(255, 215, 0);
}
.adv-fav-btn.active:hover {
background-color: rgba(255, 215, 0, 0.1);
}
.adv-fav-btn svg {
width: 20px; height: 20px;
fill: currentColor;
}
.adv-item-body-text {
font-size: 13px; color: var(--modal-text-primary); margin-top: 4px;
white-space: pre-wrap; /* 改行を維持 */
word-break: break-word; /* 長い単語を折り返し */
}
/* Favorites Media */
.adv-item-media-row {
display: flex;
gap: 4px;
margin-top: 6px;
overflow-x: auto;
padding-bottom: 2px;
}
.adv-item-media-row::-webkit-scrollbar { height: 4px; }
.adv-item-media-row::-webkit-scrollbar-thumb { background: var(--modal-border); border-radius: 2px; }
.adv-media-thumb {
height: 60px;
min-width: 60px;
border-radius: 6px;
border: 1px solid var(--modal-border);
object-fit: cover;
cursor: pointer;
}
/* Favorites Quote */
.adv-quote-box {
margin-top: 8px;
border: 1px solid var(--modal-border);
border-radius: 12px;
padding: 8px 12px;
background-color: rgba(0, 0, 0, 0.03);
}
.adv-quote-header {
display: flex;
align-items: center;
gap: 6px;
margin-bottom: 4px;
font-size: 12px;
}
.adv-quote-avatar {
width: 20px;
height: 20px;
border-radius: 50%;
object-fit: cover;
}
.adv-quote-name {
font-weight: 700;
color: var(--modal-text-primary);
}
.adv-quote-handle {
color: var(--modal-text-secondary);
}
.adv-quote-text {
font-size: 13px;
color: var(--modal-text-primary);
white-space: pre-wrap;
word-break: break-word;
}
/* Content Link */
.adv-content-link {
color: var(--modal-primary-color);
text-decoration: none;
}
.adv-content-link:hover {
text-decoration: underline;
}
/* Media Play Icon */
.adv-media-wrap {
position: relative;
display: inline-flex;
}
.adv-media-play-icon {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 24px;
height: 24px;
background-color: rgba(0, 0, 0, 0.6);
color: #fff;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
pointer-events: none; /* クリックを下の画像(リンク)に透過させる */
backdrop-filter: blur(1px);
z-index: 1;
}
.adv-media-play-icon svg {
width: 14px;
height: 14px;
fill: currentColor;
display: block;
margin-left: 2px;
}
/* Favorites Item Tag Container */
.adv-fav-tag-container {
margin-top:0.7px;
margin-left: 2px;
display: inline-flex;
align-items: center;
}
/* --- Mute Collapse Styles --- */
/* Hard Mute: data-adv-hidden */
[data-testid="cellInnerDiv"][data-adv-hidden],
article[data-adv-hidden] {
display: none !important;
content-visibility: hidden;
contain: strict;
}
/* Soft Mute: data-adv-collapsed */
/* 1. Hide original content */
[data-testid="cellInnerDiv"][data-adv-collapsed] > div:not(.adv-collapsed-placeholder),
article[data-adv-collapsed] > div:not(.adv-collapsed-placeholder) {
display: none !important;
}
/* 2. Show placeholder */
.adv-collapsed-placeholder {
display: none;
align-items: center;
justify-content: space-between;
padding: 12px 16px;
background-color: var(--modal-input-bg, #202327);
border-bottom: 1px solid var(--modal-border, #38444d);
cursor: pointer;
user-select: none;
}
.adv-collapsed-placeholder:hover {
background-color: color-mix(in srgb, var(--modal-input-bg, #202327) 85%, var(--modal-text-primary, #e7e9ea));
}
[data-testid="cellInnerDiv"][data-adv-collapsed] .adv-collapsed-placeholder,
article[data-adv-collapsed] .adv-collapsed-placeholder {
display: flex !important;
}
.adv-collapsed-label {
flex: 1;
font-size: 13px;
color: var(--modal-text-secondary, #8b98a5);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
margin-right: 12px;
}
.adv-btn-show {
background: transparent;
border: 1px solid var(--modal-primary-color, #1d9bf0);
color: var(--modal-primary-color, #1d9bf0);
border-radius: 9999px;
padding: 4px 16px;
font-size: 12px;
font-weight: 700;
cursor: pointer;
transition: background-color 0.2s;
}
.adv-btn-show:hover {
background-color: rgba(29, 155, 240, 0.1);
}
/* タグチップのサイズ微調整 */
.adv-item-sub .ft-tag-chip {
margin-left: 8px;
font-size: 10px;
padding: 0 6px;
height: 18px;
}
/* ▼▼▼ 再ミュートボタンのスタイル ▼▼▼ */
.adv-btn-remute {
margin-right: 12px; /* Caret(…)との間隔を確保 */
padding: 4px 12px; /* クリックしやすいよう少し拡大 */
font-size: 12px;
font-weight: 700;
border-radius: 9999px;
border: 1px solid var(--modal-border, #38444d);
color: var(--modal-text-secondary, #8b98a5);
background: transparent;
cursor: pointer;
white-space: nowrap;
display: inline-flex;
align-items: center;
height: 28px; /* ヘッダーのアクションボタンの高さに合わせる */
line-height: 1;
transition: all 0.2s;
}
.adv-btn-remute:hover {
background: rgba(244, 33, 46, 0.1); /* Red tint */
color: rgb(244, 33, 46);
border-color: rgb(244, 33, 46);
}
/* 検索入力中、モーダルを背景レベルまで下げる */
#advanced-search-modal.adv-z-lower {
z-index: 0 !important;
}
/* 検索入力中、Xのアプリ全体をモーダルの上に持ち上げる */
/* #react-root は body 直下の X アプリケーションのルート要素 */
#react-root.adv-app-lifted {
z-index: 1 !important;
position: relative !important; /* z-indexを効かせるために必須 */
}
/* === Native Search Resizer === */
form[role="search"] {
position: relative !important; /* リサイザーの基準点 */
max-width: none !important; /* 幅制限の解除 */
}
.adv-native-search-resizer {
position: absolute;
right: -8px;
top: 0;
bottom: 0;
width: 16px;
cursor: col-resize;
z-index: 9999;
background: transparent;
touch-action: none; /* スマホでのスクロール干渉防止 */
}
.adv-native-search-resizer:hover {
background: rgba(29,155,240,0.15); /* ホバー時に薄く青色を表示 */
}
`);
const modalHTML = `
`;
const initialize = async () => {
i18n.init();
const kv = {
get(key, def) { try { return GM_getValue(key, def); } catch (_) { return def; } },
set(key, val) { try { GM_setValue(key, val); } catch (_) {} },
del(key) { try { GM_deleteValue(key); } catch (_) {} },
};
const loadJSON = (key, def) => {
try {
const raw = kv.get(key, JSON.stringify(def));
return JSON.parse(raw);
} catch(_) { return def; }
};
const saveJSON = (key, value) => {
try { kv.set(key, JSON.stringify(value)); } catch(_) {}
};
const DEFAULT_TABS = ['search', 'history', 'saved', 'favorites', 'mute', 'lists', 'accounts'];
const DEFAULT_TABS_VISIBILITY = {
search: true,
history: true,
saved: true,
favorites: true,
mute: true,
lists: true,
accounts: true,
};
const loadTabsVisibility = () => {
const stored = loadJSON(TABS_VISIBILITY_KEY, DEFAULT_TABS_VISIBILITY);
const normalized = { ...DEFAULT_TABS_VISIBILITY };
for (const key of DEFAULT_TABS) {
normalized[key] = stored[key] === false ? false : true; // false のみ明示的に引き継ぐ
}
return normalized;
};
const saveTabsVisibility = (state) => {
saveJSON(TABS_VISIBILITY_KEY, state);
};
/* --- Favorite Tags: Code Block --- */
// ------------- 定数 & 状態 ------------- //
const FT_STATE_KEY = 'ftTagState_v1';
const FT_FILTER_ALL = 'all';
const FT_FILTER_UNCATEGORIZED = 'uncategorized';
const FT_TWEET_ID_REGEX = /\/status\/(\d+)/;
let ft_state = null;
let ft_initialized = false;
let ft_currentFilter = FT_FILTER_ALL;
let ft_currentDropdown = null;
let ft_settingsModalBackdrop = null;
let ft_dragSrcEntry = null;
// ------------- State 管理 ------------- //
function ft_createDefaultState() {
return {
enabled: true,
tags: [],
tweetTags: {},
uncategorized: { color: '#8899A6', order: 0 },
display: { mode: 'leaf' },
};
}
function ft_normalizeTagOrdersFor(stateObj) {
if (!stateObj || !Array.isArray(stateObj.tags)) return;
const groups = new Map();
for (const tag of stateObj.tags) {
if (!tag || typeof tag !== 'object') continue;
const pid = tag.parentId || null;
if (!groups.has(pid)) groups.set(pid, []);
groups.get(pid).push(tag);
}
for (const arr of groups.values()) {
arr.sort((a, b) => (typeof a.order === 'number' ? a.order : 0) - (typeof b.order === 'number' ? b.order : 0));
arr.forEach((tag, i) => { tag.order = i; });
}
}
function ft_countRootTagsFor(stateObj) {
if (!stateObj || !Array.isArray(stateObj.tags)) return 0;
return stateObj.tags.filter((t) => !t.parentId).length;
}
function ft_clampUncategorizedOrderFor(stateObj) {
if (!stateObj) return;
if (!stateObj.uncategorized || typeof stateObj.uncategorized !== 'object') {
stateObj.uncategorized = { color: '#8899A6', order: 0 };
}
const rootCount = ft_countRootTagsFor(stateObj);
let pos = typeof stateObj.uncategorized.order === 'number' ? stateObj.uncategorized.order : 0;
if (pos < 0) pos = 0;
if (pos > rootCount) pos = rootCount;
stateObj.uncategorized.order = pos;
}
function ft_normalizeTagOrders() { if (ft_state) ft_normalizeTagOrdersFor(ft_state); }
function ft_clampUncategorizedOrder() { if (ft_state) ft_clampUncategorizedOrderFor(ft_state); }
function ft_loadState() {
try {
const parsed = loadJSON(FT_STATE_KEY, null);
if (!parsed || typeof parsed !== 'object') return ft_createDefaultState();
if (!Array.isArray(parsed.tags)) parsed.tags = [];
if (!parsed.tweetTags || typeof parsed.tweetTags !== 'object') parsed.tweetTags = {};
parsed.enabled = true;
if (!parsed.uncategorized || typeof parsed.uncategorized !== 'object') {
parsed.uncategorized = { color: '#8899A6', order: 0 };
} else {
if (!parsed.uncategorized.color) parsed.uncategorized.color = '#8899A6';
if (typeof parsed.uncategorized.order !== 'number') parsed.uncategorized.order = 0;
}
if (!parsed.display || typeof parsed.display !== 'object') {
parsed.display = { mode: 'leaf' };
} else if (parsed.display.mode !== 'leaf' && parsed.display.mode !== 'full') {
parsed.display.mode = 'leaf';
}
ft_normalizeTagOrdersFor(parsed);
ft_clampUncategorizedOrderFor(parsed);
return parsed;
} catch (e) {
return ft_createDefaultState();
}
}
function ft_saveState(newState) {
if (newState) ft_state = newState;
try {
if (ft_state) {
ft_normalizeTagOrdersFor(ft_state);
ft_clampUncategorizedOrderFor(ft_state);
saveJSON(FT_STATE_KEY, ft_state);
}
} catch (e) {}
requestAnimationFrame(() => {
ft_refreshAllTagChips();
// お気に入りタブが開いていれば再描画してタグ変更/絞り込みを反映
if (getActiveTabName() === 'favorites') {
renderFavorites();
}
});
}
function ft_generateTagId() {
return 'tag_' + Date.now().toString(36) + '_' + Math.random().toString(36).slice(2, 8);
}
function ft_getTagById(tagId) {
return ft_state.tags.find((t) => t.id === tagId) || null;
}
function ft_getAllTags() {
return ft_state.tags.slice();
}
function ft_getTagColor(tagId) {
const tag = ft_getTagById(tagId);
return tag ? tag.color || '#1d9bf0' : '#8899A6';
}
function ft_getUncategorizedColor() {
return ft_state?.uncategorized?.color || '#8899A6';
}
function ft_createNewTag(name, color, parentId) {
const pid = parentId || null;
const siblingsCount = ft_state.tags.filter((t) => (t.parentId || null) === pid).length;
const tag = {
id: ft_generateTagId(),
name,
color,
parentId: pid,
order: siblingsCount,
};
ft_state.tags.push(tag);
return tag;
}
function ft_countRootTags() {
return ft_countRootTagsFor(ft_state);
}
function ft_getTagAncestors(tag) {
const result = [];
if (!tag) return result;
const seen = new Set();
let current = tag;
while (current) {
if (seen.has(current.id)) break;
seen.add(current.id);
result.unshift(current);
if (!current.parentId) break;
current = ft_getTagById(current.parentId);
}
return result;
}
function ft_getTagFullPath(tag) {
const ancestors = ft_getTagAncestors(tag);
if (!ancestors.length) return tag ? tag.name || '' : '';
return ancestors.map((t) => t.name || '').join(' / ');
}
function ft_getTagDisplayLabelFromTag(tag) {
if (!tag) return '';
const mode = ft_state?.display?.mode;
if (mode === 'full') return ft_getTagFullPath(tag);
return tag.name;
}
function ft_getTagListWithUncategorized() {
const result = [];
if (!ft_state || !Array.isArray(ft_state.tags)) return result;
const byParent = new Map();
for (const tag of ft_state.tags) {
if (!tag || typeof tag !== 'object') continue;
const pid = tag.parentId || null;
if (!byParent.has(pid)) byParent.set(pid, []);
byParent.get(pid).push(tag);
}
for (const arr of byParent.values()) {
arr.sort((a, b) => (typeof a.order === 'number' ? a.order : 0) - (typeof b.order === 'number' ? b.order : 0));
}
function dfs(parentId, depth) {
const arr = byParent.get(parentId || null);
if (!arr) return;
for (const tag of arr) {
result.push({ tag, depth });
dfs(tag.id, depth + 1);
}
}
dfs(null, 0);
const entries = [];
const rootCount = result.filter((e) => e.depth === 0).length;
let uncatPos = ft_state.uncategorized.order || 0;
if (uncatPos < 0) uncatPos = 0;
if (uncatPos > rootCount) uncatPos = rootCount;
let rootIndex = 0;
for (const item of result) {
if (item.depth === 0 && rootIndex === uncatPos) {
entries.push({ kind: 'uncat', depth: 0 });
}
entries.push({ kind: 'tag', tag: item.tag, depth: item.depth });
if (item.depth === 0) rootIndex++;
}
if (rootCount === 0 || uncatPos === rootCount) {
entries.push({ kind: 'uncat', depth: 0 });
}
return entries;
}
function ft_isTagInSubtree(tagId, rootTagId) {
// ft_state が存在しない場合は即座に false を返す
if (!ft_state || !tagId || !rootTagId) return false;
if (tagId === rootTagId) return true;
let current = ft_getTagById(tagId);
const visited = new Set();
while (current && current.parentId) {
if (visited.has(current.id)) break;
visited.add(current.id);
if (current.parentId === rootTagId) return true;
current = ft_getTagById(current.parentId);
}
return false;
}
function ft_wouldCreateCycle(newParentId, childId) {
if (!newParentId || !childId) return false;
if (newParentId === childId) return true;
let current = ft_getTagById(newParentId);
const visited = new Set();
while (current && current.parentId) {
if (visited.has(current.id)) break;
visited.add(current.id);
if (current.parentId === childId) return true;
current = ft_getTagById(current.parentId);
}
return false;
}
// ------------- ルート & ユーティリティ ------------- //
// ツイートのDOMからIDを抽出
function ft_extractTweetId(article) {
if (article.dataset.ftTweetId) return article.dataset.ftTweetId;
// 引用ツイート(カード部分)の中にあるリンクを除外するための判定関数
// div[role="link"] は引用カードのコンテナに付与される属性です
const isInsideQuote = (el) => {
return !!el.closest('div[role="link"]');
};
// 1. 最も確実な方法: