From f33ac5d0de714a248e21f052914fff57fc5d56a9 Mon Sep 17 00:00:00 2001
From: ujiro99
Date: Mon, 7 Jul 2025 12:49:28 +0900
Subject: [PATCH 1/9] Fix: PageRule PageRule was not applied.
---
src/components/Popup.tsx | 32 ++++-----
src/hooks/useDetectStartup.ts | 20 +++---
src/hooks/useSetting.ts | 118 +++++++++++++++++++---------------
3 files changed, 91 insertions(+), 79 deletions(-)
diff --git a/src/components/Popup.tsx b/src/components/Popup.tsx
index 80b2ef19..1bfe7ff9 100644
--- a/src/components/Popup.tsx
+++ b/src/components/Popup.tsx
@@ -1,14 +1,14 @@
-import { useState, useEffect, createContext, forwardRef } from 'react'
-import { Popover, PopoverContent, PopoverAnchor } from '@/components/ui/popover'
-import { Menu } from '@/components/menu/Menu'
-import { useUserSettings } from '@/hooks/useSetting'
-import { useDetectStartup } from '@/hooks/useDetectStartup'
-import { useTabCommandReceiver } from '@/hooks/useTabCommandReceiver'
-import { hexToHsl, isMac, onHover, cn } from '@/lib/utils'
-import { t } from '@/services/i18n'
-import { STYLE_VARIABLE, EXIT_DURATION, SIDE, ALIGN } from '@/const'
+import { useState, useEffect, createContext, forwardRef } from "react"
+import { Popover, PopoverContent, PopoverAnchor } from "@/components/ui/popover"
+import { Menu } from "@/components/menu/Menu"
+import { useUserSettings } from "@/hooks/useSetting"
+import { useDetectStartup } from "@/hooks/useDetectStartup"
+import { useTabCommandReceiver } from "@/hooks/useTabCommandReceiver"
+import { hexToHsl, isMac, onHover, cn } from "@/lib/utils"
+import { t } from "@/services/i18n"
+import { STYLE_VARIABLE, EXIT_DURATION, SIDE, ALIGN } from "@/const"
-import css from './Popup.module.css'
+import css from "./Popup.module.css"
export type PopupProps = {
positionElm: Element | null
@@ -46,7 +46,7 @@ export const Popup = forwardRef(
userSettings?.userStyles &&
userSettings.userStyles.reduce((acc: any, cur: any) => {
if (cur.value == null) return acc
- if (cur.name === 'background-color' || cur.name === 'border-color') {
+ if (cur.name === "background-color" || cur.name === "border-color") {
const hsl = hexToHsl(cur.value)
return {
...acc,
@@ -116,7 +116,7 @@ export const Popup = forwardRef(
align={align}
sideOffset={sideOffset}
alignOffset={alignOffset}
- className={cn(css.popup, isPreview && 'z-10')}
+ className={cn(css.popup, isPreview && "z-10")}
style={userStyles}
onOpenAutoFocus={noFocus}
{...onHover(handleOnHover, true)}
@@ -136,7 +136,7 @@ export function PreviewDesc(props: PopupProps) {
const { userSettings } = useUserSettings()
const key = userSettings?.startupMethod?.keyboardParam
- const os = isMac() ? 'mac' : 'windows'
+ const os = isMac() ? "mac" : "windows"
const keyLabel = t(`Option_keyboardParam_${key}_${os}`)
return (
@@ -145,16 +145,16 @@ export function PreviewDesc(props: PopupProps) {
Preview...
{isContextMenu && (
- {t('previewOnContextMenu')}
+ {t("previewOnContextMenu")}
)}
{!visible && isKeyboard && (
- {t('previewOnKeyboard', [keyLabel])}
+ {t("previewOnKeyboard", [keyLabel])}
)}
{!visible && isLeftClickHold && (
- {t('previewOnLeftClickHold', [keyLabel])}
+ {t("previewOnLeftClickHold", [keyLabel])}
)}
>
diff --git a/src/hooks/useDetectStartup.ts b/src/hooks/useDetectStartup.ts
index e696bdf2..88d6378e 100644
--- a/src/hooks/useDetectStartup.ts
+++ b/src/hooks/useDetectStartup.ts
@@ -1,11 +1,11 @@
-import { useState, useEffect } from 'react'
-import type { PopupProps } from '@/components/Popup'
-import { useSetting } from '@/hooks/useSetting'
-import { useLeftClickHold } from '@/hooks/useLeftClickHold'
-import { useSelectContext } from '@/hooks/useSelectContext'
-import { POPUP_ENABLED, STARTUP_METHOD, KEYBOARD } from '@/const'
-import { Ipc, TabCommand } from '@/services/ipc'
-import { isEmpty } from '@/lib/utils'
+import { useState, useEffect } from "react"
+import type { PopupProps } from "@/components/Popup"
+import { useSetting } from "@/hooks/useSetting"
+import { useLeftClickHold } from "@/hooks/useLeftClickHold"
+import { useSelectContext } from "@/hooks/useSelectContext"
+import { POPUP_ENABLED, STARTUP_METHOD, KEYBOARD } from "@/const"
+import { Ipc, TabCommand } from "@/services/ipc"
+import { isEmpty } from "@/lib/utils"
type Props = PopupProps & {
isHover?: boolean
@@ -85,9 +85,9 @@ export function useKeyboard(_: Props) {
setDetectKey((prev) => !prev)
}
}
- window.addEventListener('keyup', handleKeyUp)
+ window.addEventListener("keyup", handleKeyUp)
return () => {
- window.removeEventListener('keyup', handleKeyUp)
+ window.removeEventListener("keyup", handleKeyUp)
}
}, [method, keyboardParam])
diff --git a/src/hooks/useSetting.ts b/src/hooks/useSetting.ts
index 8fceb839..803a0834 100644
--- a/src/hooks/useSetting.ts
+++ b/src/hooks/useSetting.ts
@@ -1,10 +1,10 @@
-import { useState, useEffect, useCallback, useRef, useMemo } from 'react'
-import { enhancedSettings } from '../services/enhancedSettings'
+import { useState, useEffect, useCallback, useRef, useMemo } from "react"
+import { enhancedSettings } from "../services/enhancedSettings"
import {
settingsCache,
CacheSection,
CACHE_SECTIONS,
-} from '../services/settingsCache'
+} from "../services/settingsCache"
import type {
SettingsType,
@@ -14,9 +14,43 @@ import type {
ShortcutSettings,
UserSettings,
PageRule,
-} from '@/types'
-import { isEmpty } from '@/lib/utils'
-import { INHERIT } from '@/const'
+} from "@/types"
+import { isEmpty } from "@/lib/utils"
+import { INHERIT } from "@/const"
+
+// Find page rule that matches current URL
+function findMatchingPageRule(
+ settings: Partial,
+): PageRule | undefined {
+ if (!settings || typeof window === "undefined") return undefined
+
+ const rule = (settings.pageRules || [])
+ .filter((r) => !isEmpty(r.urlPattern))
+ .find((rule) => {
+ try {
+ const re = new RegExp(rule.urlPattern)
+ return window.location.href.match(re) != null
+ } catch {
+ return false
+ }
+ })
+
+ return rule
+}
+
+// Apply page rule to settings, modifying popupPlacement if needed
+function applyPageRuleToSettings(
+ settings: Partial,
+ pageRule: PageRule | undefined,
+): void {
+ if (
+ pageRule != null &&
+ pageRule.popupPlacement !== INHERIT &&
+ settings.popupPlacement
+ ) {
+ settings.popupPlacement = pageRule.popupPlacement
+ }
+}
// Type definitions for section-specific hook return values
type SectionData =
@@ -64,7 +98,7 @@ function useAsyncData(
}
} catch (err) {
if (mountedRef.current) {
- setError(err instanceof Error ? err : new Error('Unknown error'))
+ setError(err instanceof Error ? err : new Error("Unknown error"))
}
} finally {
if (mountedRef.current) {
@@ -140,8 +174,23 @@ export function useUserSettings(forceFresh = false) {
forceFresh,
)
+ // Find matching page rule and apply to settings
+ const pageRule = useMemo(() => {
+ if (!data) return undefined
+ return findMatchingPageRule(data)
+ }, [data])
+
+ const userSettings = useMemo(() => {
+ if (!data) return {} as UserSettings
+
+ const settings = { ...data } as UserSettings
+ applyPageRuleToSettings(settings, pageRule)
+ return settings
+ }, [data, pageRule])
+
return {
- userSettings: (data || {}) as UserSettings,
+ userSettings,
+ pageRule,
loading,
error,
refetch,
@@ -159,24 +208,16 @@ export function useSetting(
settings: Partial
pageRule: PageRule | undefined
loading: boolean
- error: Error | null
- refetch: () => Promise
- invalidateCache: (sectionsToInvalidate?: CacheSection[]) => void
} {
const sectionsRef = useRef(sections)
- const sectionsKey = useMemo(() => sections.join(','), [sections])
+ const sectionsKey = useMemo(() => sections.join(","), [sections])
// Update when sections change
useEffect(() => {
sectionsRef.current = sections
}, [sections])
- const {
- data: settings,
- loading,
- error,
- refetch,
- } = useAsyncData>(
+ const { data: settings, loading } = useAsyncData>(
() =>
enhancedSettings.get({
sections: sectionsRef.current,
@@ -189,53 +230,26 @@ export function useSetting(
})),
)
- // Page rule calculation
+ // Find matching page rule using centralized logic
const pageRule = useMemo(() => {
- if (!settings || typeof window === 'undefined') return undefined
-
- const rule = (settings.pageRules || [])
- .filter((r) => !isEmpty(r.urlPattern))
- .find((rule) => {
- try {
- const re = new RegExp(rule.urlPattern)
- return window.location.href.match(re) != null
- } catch {
- return false
- }
- })
-
- if (
- rule != null &&
- rule.popupPlacement !== INHERIT &&
- settings.popupPlacement
- ) {
- settings.popupPlacement = rule.popupPlacement
+ if (!settings) return undefined
+ const rule = findMatchingPageRule(settings)
+ if (rule) {
+ applyPageRuleToSettings(settings, rule)
}
-
return rule
}, [settings])
- const invalidateCache = useCallback(
- (sectionsToInvalidate?: CacheSection[]) => {
- const targetSections = sectionsToInvalidate || sectionsRef.current
- enhancedSettings.invalidateCache(targetSections)
- },
- [],
- )
-
return {
settings: settings || {},
pageRule,
loading,
- error,
- refetch,
- invalidateCache,
}
}
// Settings hook with image cache applied
export function useSettingsWithImageCache() {
- const { settings, pageRule, loading } = useSetting([
+ const { settings, loading } = useSetting([
CACHE_SECTIONS.COMMANDS,
CACHE_SECTIONS.USER_SETTINGS,
CACHE_SECTIONS.CACHES,
@@ -276,7 +290,5 @@ export function useSettingsWithImageCache() {
commands: commandsWithCache,
folders: foldersWithCache,
iconUrls,
- pageRule,
- loading,
}
}
From a0e7508362ce6f5119bb5c5739f13e1086aab38d Mon Sep 17 00:00:00 2001
From: ujiro99
Date: Mon, 7 Jul 2025 13:12:43 +0900
Subject: [PATCH 2/9] Update: Improve the display of Storage Usage.
---
src/components/option/StorageUsage.tsx | 6 ++---
src/services/storage/storageUsage.ts | 35 +++++++++++---------------
2 files changed, 18 insertions(+), 23 deletions(-)
diff --git a/src/components/option/StorageUsage.tsx b/src/components/option/StorageUsage.tsx
index 43b384ea..b46e65af 100644
--- a/src/components/option/StorageUsage.tsx
+++ b/src/components/option/StorageUsage.tsx
@@ -102,7 +102,7 @@ const StorageUsage: React.FC = () => {
Storage Usage
-
+
Sync Area
@@ -113,7 +113,7 @@ const StorageUsage: React.FC = () => {
className="w-full"
/>
-
+
System: {storageData.sync.systemPercent}%
@@ -135,7 +135,7 @@ const StorageUsage: React.FC = () => {
-
+
Local Area
diff --git a/src/services/storage/storageUsage.ts b/src/services/storage/storageUsage.ts
index 524b5ce8..35a43b6e 100644
--- a/src/services/storage/storageUsage.ts
+++ b/src/services/storage/storageUsage.ts
@@ -118,6 +118,13 @@ const getStorageUsage = async (): Promise => {
const localUsed = localTotalBytes
const localFree = localLimitTotal - localUsed
+ const formatPercentage = (value: number): number => {
+ const percentage = value * 100
+ return Number(
+ percentage >= 10 ? percentage.toFixed(0) : percentage.toFixed(1),
+ )
+ }
+
return {
sync: {
total: syncLimitTotal,
@@ -126,16 +133,10 @@ const getStorageUsage = async (): Promise => {
system: syncSystemBytes,
reservedRemain,
commands: syncCommandBytes,
- systemPercent: Number(
- ((syncSystemBytes / syncLimitTotal) * 100).toFixed(0),
- ),
- reservedPercent: Number(
- ((reservedRemain / syncLimitTotal) * 100).toFixed(0),
- ),
- commandsPercent: Number(
- ((syncCommandBytes / syncLimitTotal) * 100).toFixed(0),
- ),
- freePercent: Number(((syncFree / syncLimitTotal) * 100).toFixed(0)),
+ systemPercent: formatPercentage(syncSystemBytes / syncLimitTotal),
+ reservedPercent: formatPercentage(reservedRemain / syncLimitTotal),
+ commandsPercent: formatPercentage(syncCommandBytes / syncLimitTotal),
+ freePercent: formatPercentage(syncFree / syncLimitTotal),
},
local: {
total: localLimitTotal,
@@ -144,16 +145,10 @@ const getStorageUsage = async (): Promise => {
system: localSystemBytes,
backup: localBackupBytes,
commands: localCommandBytes,
- systemPercent: Number(
- ((localSystemBytes / localLimitTotal) * 100).toFixed(0),
- ),
- backupPercent: Number(
- ((localBackupBytes / localLimitTotal) * 100).toFixed(0),
- ),
- commandsPercent: Number(
- ((localCommandBytes / localLimitTotal) * 100).toFixed(0),
- ),
- freePercent: Number(((localFree / localLimitTotal) * 100).toFixed(0)),
+ systemPercent: formatPercentage(localSystemBytes / localLimitTotal),
+ backupPercent: formatPercentage(localBackupBytes / localLimitTotal),
+ commandsPercent: formatPercentage(localCommandBytes / localLimitTotal),
+ freePercent: formatPercentage(localFree / localLimitTotal),
},
}
} catch (error) {
From c763a5cd66c78ebb8c551a8626440fb1fa7b99ca Mon Sep 17 00:00:00 2001
From: ujiro99
Date: Mon, 7 Jul 2025 13:22:35 +0900
Subject: [PATCH 3/9] Update: If no backup data exists, disable the restore
button.
---
src/components/option/Dialog.tsx | 18 ++++++++++++------
src/components/option/ImportExport.tsx | 19 +++++--------------
2 files changed, 17 insertions(+), 20 deletions(-)
diff --git a/src/components/option/Dialog.tsx b/src/components/option/Dialog.tsx
index 7d320405..e7475fc1 100644
--- a/src/components/option/Dialog.tsx
+++ b/src/components/option/Dialog.tsx
@@ -1,4 +1,4 @@
-import React from 'react'
+import React from "react"
import {
Dialog as DialogRoot,
DialogContent,
@@ -7,11 +7,12 @@ import {
DialogFooter,
DialogTitle,
DialogPortal,
-} from '@/components/ui/dialog'
+} from "@/components/ui/dialog"
-import { t } from '@/services/i18n'
+import { t } from "@/services/i18n"
+import { cn } from "@/lib/utils"
-import css from './Dialog.module.css'
+import css from "./Dialog.module.css"
type Props = {
open: boolean
@@ -19,6 +20,7 @@ type Props = {
title: string
description: () => React.ReactNode
okText: string
+ okDisabled?: boolean
children?: React.ReactNode
}
@@ -40,14 +42,18 @@ export function Dialog(props: Props) {
{props.children}
-
diff --git a/src/components/option/ImportExport.tsx b/src/components/option/ImportExport.tsx
index 8baac1a7..1899327d 100644
--- a/src/components/option/ImportExport.tsx
+++ b/src/components/option/ImportExport.tsx
@@ -276,15 +276,6 @@ export function ImportExport() {
}
const handleRestore = async () => {
- const hasAnyBackup = Object.values(backupData).some(
- (backup) => backup.status === BACKUP_STATUS.AVAILABLE,
- )
-
- if (!hasAnyBackup) {
- alert(t("Option_RestoreFromBackup_no_data"))
- return
- }
-
setRestoreDialog(true)
}
@@ -379,11 +370,6 @@ export function ImportExport() {
onClick={handleRestore}
className={css.menuButton}
type="button"
- disabled={
- !Object.values(backupData).some(
- (backup) => backup.status === BACKUP_STATUS.AVAILABLE,
- )
- }
title={
Object.values(backupData).every(
(backup) => backup.status === BACKUP_STATUS.CHECKING,
@@ -451,6 +437,11 @@ export function ImportExport() {
return {t("Option_RestoreFromBackup_dialog_select")}
}}
okText={t("Option_RestoreFromBackup_dialog_restore")}
+ okDisabled={
+ !Object.values(backupData).some(
+ (backup) => backup.status === BACKUP_STATUS.AVAILABLE,
+ )
+ }
>
{(() => {
const availableBackups = Object.entries(backupData).filter(
From e2fb1146068c63fdc53dff870b29d207a796cb42 Mon Sep 17 00:00:00 2001
From: ujiro99
Date: Mon, 7 Jul 2025 21:06:13 +0900
Subject: [PATCH 4/9] Update: Prevent auto focus.
---
src/components/Popup.tsx | 1 +
src/components/menu/Menu.tsx | 1 +
2 files changed, 2 insertions(+)
diff --git a/src/components/Popup.tsx b/src/components/Popup.tsx
index 1bfe7ff9..b0cc1762 100644
--- a/src/components/Popup.tsx
+++ b/src/components/Popup.tsx
@@ -119,6 +119,7 @@ export const Popup = forwardRef(
className={cn(css.popup, isPreview && "z-10")}
style={userStyles}
onOpenAutoFocus={noFocus}
+ onCloseAutoFocus={noFocus}
{...onHover(handleOnHover, true)}
>
{!isContextMenu ? : null}
diff --git a/src/components/menu/Menu.tsx b/src/components/menu/Menu.tsx
index 93a94052..c050b63f 100644
--- a/src/components/menu/Menu.tsx
+++ b/src/components/menu/Menu.tsx
@@ -186,6 +186,7 @@ const MenuFolder = (props: {
sideOffset={isHorizontal ? 2 : -2}
className={clsx({ flex: isHorizontal })}
ref={contentRef}
+ onCloseAutoFocus={(e) => e.preventDefault()}
{...onHover(props.onHoverContent, folder.id)}
>
{!isHorizontal ? (
From 9019c2263175aff126c8fc73bc5b6e181b854fa9 Mon Sep 17 00:00:00 2001
From: ujiro99
Date: Mon, 7 Jul 2025 21:53:58 +0900
Subject: [PATCH 5/9] Update: Use Japanese in CLAUDE.md.
---
CLAUDE.md | 86 +++++++++++++++++++++++++++----------------------------
1 file changed, 43 insertions(+), 43 deletions(-)
diff --git a/CLAUDE.md b/CLAUDE.md
index 266d9572..177595c6 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -1,60 +1,60 @@
# CLAUDE.md
-This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
+このファイルは、このリポジトリのコードを扱う際にClaude Code (claude.ai/code)に対するガイダンスを提供します。
-## Development Commands
+## 開発コマンド
-- `yarn dev` - Start development mode with Vite
-- `yarn build` - Build the extension (runs TypeScript compilation + Vite build)
-- `yarn lint` - Run ESLint to check code quality
-- `yarn zip` - Create a distributable extension zip file from the built dist folder
+- `yarn dev` - Viteを使用した開発モードの開始
+- `yarn build` - 拡張機能のビルド(TypeScriptコンパイル + Viteビルドを実行)
+- `yarn lint` - ESLintを実行してコード品質をチェック
+- `yarn zip` - ビルドされたdistフォルダから配布可能な拡張機能のzipファイルを作成
-## Architecture Overview
+## アーキテクチャ概要
-This is a Chrome Extension (Manifest V3) called **Selection Command** that allows users to perform various actions on selected text on web pages.
+これは**Selection Command**と呼ばれるChrome拡張機能(Manifest V3)で、ユーザーがWebページ上で選択したテキストに対してさまざまなアクションを実行できます。
-### Key Components
+### 主要コンポーネント
-**Chrome Extension Structure:**
+**Chrome拡張機能の構造:**
-- `manifest.json` - Extension manifest defining permissions, content scripts, and background workers
-- `src/background_script.ts` - Service worker handling extension lifecycle and background operations
-- `src/content_script.tsx` - Main content script injected into web pages
-- `src/options_page.tsx` - Extension options/settings page
+- `manifest.json` - 権限、コンテンツスクリプト、バックグラウンドワーカーを定義する拡張機能マニフェスト
+- `src/background_script.ts` - 拡張機能のライフサイクルとバックグラウンド操作を処理するサービスワーカー
+- `src/content_script.tsx` - Webページに注入されるメインのコンテンツスクリプト
+- `src/options_page.tsx` - 拡張機能のオプション/設定ページ
-**Core Architecture:**
+**コアアーキテクチャ:**
-- **Actions** (`src/action/`) - Core functionality modules including background operations, popup handling, page actions, and command execution
-- **Components** (`src/components/`) - React components organized by feature:
- - `menu/` - Context menu and menu item components
- - `option/` - Settings and configuration UI
- - `pageAction/` - Page automation and recording components
- - `result/` - Result display and popup components
- - `ui/` - Reusable UI components (uses Radix UI)
-- **Services** (`src/services/`) - Business logic and utilities including settings management, storage, analytics, and page action handling
-- **Hooks** (`src/hooks/`) - Custom React hooks for state management and Chrome extension APIs
+- **Actions** (`src/action/`) - バックグラウンド操作、ポップアップ処理、ページアクション、コマンド実行を含むコア機能モジュール
+- **Components** (`src/components/`) - 機能別に整理されたReactコンポーネント:
+ - `menu/` - コンテキストメニューとメニューアイテムコンポーネント
+ - `option/` - 設定と構成UI
+ - `pageAction/` - ページ自動化と記録コンポーネント
+ - `result/` - 結果表示とポップアップコンポーネント
+ - `ui/` - 再利用可能なUIコンポーネント(Radix UIを使用)
+- **Services** (`src/services/`) - 設定管理、ストレージ、分析、ページアクション処理を含むビジネスロジックとユーティリティ
+- **Hooks** (`src/hooks/`) - 状態管理とChrome拡張機能APIのためのカスタムReactフック
-**Key Features:**
+**主要機能:**
-- **Page Actions** - Record and replay browser automation sequences
-- **Command Hub** - Web interface for sharing and discovering commands (separate Next.js app in `pages/`)
-- **Context Menus** - Right-click actions on selected text
-- **Settings Management** - Import/export configurations and user preferences
+- **ページアクション** - ブラウザ自動化シーケンスの記録と再生
+- **コマンドハブ** - コマンドの共有と発見のためのWebインターフェース(`pages/`内の独立したNext.jsアプリ)
+- **コンテキストメニュー** - 選択したテキストに対する右クリックアクション
+- **設定管理** - 構成とユーザー設定のインポート/エクスポート
-### Technical Stack
+### 技術スタック
-- **Frontend**: React 18 with TypeScript
-- **Build System**: Vite with `@crxjs/vite-plugin` for Chrome extension development
-- **UI Components**: Shadcn
-- **Form and Validation**: react-hook-form and zod
-- **Styling**: CSS Modules + Tailwind CSS(ver.3)
-- **State Management**: React hooks with Chrome extension storage APIs
-- **Testing**: ESLint for code quality
+- **フロントエンド**: React 18 with TypeScript
+- **ビルドシステム**: Vite with `@crxjs/vite-plugin` for Chrome extension development
+- **UIコンポーネント**: Shadcn
+- **フォームとバリデーション**: react-hook-form and zod
+- **スタイリング**: CSS Modules + Tailwind CSS(ver.3)
+- **状態管理**: React hooks with Chrome extension storage APIs
+- **テスト**: ESLint for code quality
-### Project Structure Notes
+### プロジェクト構造の注意事項
-- The main extension code is in `src/`
-- The command hub website is a separate Next.js application in `pages/`
-- Extension supports internationalization with locale files in `public/_locales/`
-- Uses Shadow DOM for content script styling isolation
-- Implements Robula+ algorithm for robust XPath selector generation (`src/lib/robula-plus/`)
+- メインの拡張機能コードは`src/`内にある
+- コマンドハブのウェブサイトは`pages/`内の独立したNext.jsアプリケーション
+- 拡張機能は`public/_locales/`のロケールファイルによる国際化をサポート
+- コンテンツスクリプトのスタイリング分離にShadow DOMを使用
+- 堅牢なXPathセレクター生成のためのRobula+アルゴリズムを実装(`src/lib/robula-plus/`)
From 366529e59c90c8d417eab9ea1a72b6a165b7e9d9 Mon Sep 17 00:00:00 2001
From: ujiro99
Date: Mon, 7 Jul 2025 22:25:21 +0900
Subject: [PATCH 6/9] Update: Unified font color.
---
src/components/Popup.module.css | 12 +++----
src/components/Popup.tsx | 2 +-
src/components/option/HubBanner.module.css | 5 ++-
src/components/option/Option.module.css | 25 ++++++--------
src/components/option/Option.tsx | 30 ++++++++--------
src/components/option/StorageUsage.tsx | 8 ++---
.../option/TableOfContents.module.css | 2 +-
src/components/ui/form.tsx | 34 +++++++++----------
8 files changed, 56 insertions(+), 62 deletions(-)
diff --git a/src/components/Popup.module.css b/src/components/Popup.module.css
index 4aed3138..3fa37371 100644
--- a/src/components/Popup.module.css
+++ b/src/components/Popup.module.css
@@ -19,31 +19,29 @@
--background: var(--sc-bg-color-h) var(--sc-bg-color-s) var(--sc-bg-color-l);
--foreground: 222.2 47.4% 11.2%;
--border: var(--sc-bd-color-h) var(--sc-bd-color-s) var(--sc-bd-color-l);
- --accent: var(--sc-bg-color-h) var(--sc-bg-color-s)
- calc(var(--sc-bg-color-l) - 6%);
+ --accent: var(--sc-bg-color-h) var(--sc-bg-color-s) calc(var(--sc-bg-color-l) - 6%);
--accent-foreground: 224 71% 4%;
}
-.popup[data-state='open'] {
+.popup[data-state="open"] {
animation-duration: var(--sc-duration) !important;
}
-.popup[data-state='closed'] {
+.popup[data-state="closed"] {
animation-duration: 100ms !important;
}
.previewContainer {
position: relative;
- & > div {
+ &>div {
transform: translate(0, 0) !important;
}
}
.previewLabel {
- font-family: var(--font-monospace);
+ @apply font-mono text-gray-800;
font-size: 1.2rem;
- color: #444;
}
.previewDescription {
diff --git a/src/components/Popup.tsx b/src/components/Popup.tsx
index b0cc1762..81bbd4c6 100644
--- a/src/components/Popup.tsx
+++ b/src/components/Popup.tsx
@@ -116,7 +116,7 @@ export const Popup = forwardRef(
align={align}
sideOffset={sideOffset}
alignOffset={alignOffset}
- className={cn(css.popup, isPreview && "z-10")}
+ className={cn(css.popup, isPreview && "z-10 mt-2")}
style={userStyles}
onOpenAutoFocus={noFocus}
onCloseAutoFocus={noFocus}
diff --git a/src/components/option/HubBanner.module.css b/src/components/option/HubBanner.module.css
index 7dd2be32..7f1c8da0 100644
--- a/src/components/option/HubBanner.module.css
+++ b/src/components/option/HubBanner.module.css
@@ -1,7 +1,6 @@
.menuLabel {
- font-family: var(--font-monospace);
+ @apply font-mono text-gray-800;
font-size: 1.2rem;
- color: #444;
}
.banner {
@@ -16,7 +15,7 @@
}
.description {
+ @apply font-mono text-gray-800;
font-size: 0.8rem;
- font-family: var(--font-family);
padding-left: 4px;
}
diff --git a/src/components/option/Option.module.css b/src/components/option/Option.module.css
index 2db517c5..1bd60ac8 100644
--- a/src/components/option/Option.module.css
+++ b/src/components/option/Option.module.css
@@ -1,6 +1,6 @@
:root {
--font-family:
- -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue',
+ -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue",
Arial, sans-serif;
--font-color: #333;
--menu-top: 150px;
@@ -22,7 +22,7 @@
margin: 0;
}
-.titleSpan + .titleSpan {
+.titleSpan+.titleSpan {
margin-left: 4px;
}
@@ -45,9 +45,8 @@
}
.menuLabel {
- @apply font-mono;
+ @apply font-mono text-gray-800;
font-size: 1.2rem;
- color: #444;
margin-bottom: 16px;
}
@@ -57,13 +56,13 @@
}
.menuButton {
- @apply font-mono flex items-center;
+ @apply flex items-center;
+ @apply font-mono text-gray-800;
border: none;
background: none;
font-size: 0.9rem;
padding: 6px 8px;
border-radius: 4px;
- color: #444;
transition:
background-color,
ease-out 0.2s,
@@ -77,7 +76,7 @@
}
.buttonImport {
- composes: button from '../Common.module.css';
+ composes: button from "../Common.module.css";
width: 100%;
padding: 6px 8px;
@@ -93,11 +92,9 @@
.bgHatching {
background-color: #fff;
- background-image: repeating-linear-gradient(
- 45deg,
- #6b7280,
- #6b7280 1px,
- transparent 1px,
- transparent 4px
- );
+ background-image: repeating-linear-gradient(45deg,
+ #6b7280,
+ #6b7280 1px,
+ transparent 1px,
+ transparent 4px);
}
diff --git a/src/components/option/Option.tsx b/src/components/option/Option.tsx
index 72cab0fd..27707edb 100644
--- a/src/components/option/Option.tsx
+++ b/src/components/option/Option.tsx
@@ -1,17 +1,17 @@
-import { useState, useEffect } from 'react'
+import { useState, useEffect } from "react"
-import { Settings } from '@/services/settings'
-import { capitalize } from '@/lib/utils'
-import { APP_ID, VERSION } from '@/const'
+import { Settings } from "@/services/settings"
+import { capitalize } from "@/lib/utils"
+import { APP_ID, VERSION } from "@/const"
-import { Popup } from '@/components/Popup'
-import { TableOfContents } from '@/components/option/TableOfContents'
-import { ImportExport } from '@/components/option/ImportExport'
-import { HubBanner } from '@/components/option/HubBanner'
-import { SettingForm } from '@/components/option/SettingForm'
-import StorageUsage from '@/components/option/StorageUsage'
+import { Popup } from "@/components/Popup"
+import { TableOfContents } from "@/components/option/TableOfContents"
+import { ImportExport } from "@/components/option/ImportExport"
+import { HubBanner } from "@/components/option/HubBanner"
+import { SettingForm } from "@/components/option/SettingForm"
+import StorageUsage from "@/components/option/StorageUsage"
-import css from './Option.module.css'
+import css from "./Option.module.css"
const SCROLL_OFFSET = 80
@@ -49,7 +49,7 @@ export function Option() {
elm.getBoundingClientRect().top + window.scrollY - SCROLL_OFFSET
window.scrollTo({
top: targetPosition,
- behavior: 'smooth',
+ behavior: "smooth",
})
}
}
@@ -58,7 +58,7 @@ export function Option() {
- {APP_ID.split('-').map((n) => {
+ {APP_ID.split("-").map((n) => {
return (
{capitalize(n)}
@@ -90,10 +90,10 @@ export function Option() {
ref={setPopupElm}
/>
-
+
-
diff --git a/src/components/option/StorageUsage.tsx b/src/components/option/StorageUsage.tsx
index b46e65af..b6bc99ec 100644
--- a/src/components/option/StorageUsage.tsx
+++ b/src/components/option/StorageUsage.tsx
@@ -101,7 +101,7 @@ const StorageUsage: React.FC = () => {
return (
Storage Usage
-
+
Sync Area
@@ -112,7 +112,7 @@ const StorageUsage: React.FC = () => {
backgroundColor="#f3f4f6"
className="w-full"
/>
-
+
System: {storageData.sync.systemPercent}%
@@ -134,7 +134,7 @@ const StorageUsage: React.FC = () => {
-
+
Local Area
@@ -145,7 +145,7 @@ const StorageUsage: React.FC = () => {
backgroundColor="#f3f4f6"
className="w-full"
/>
-
+
System: {storageData.local.systemPercent}%
diff --git a/src/components/option/TableOfContents.module.css b/src/components/option/TableOfContents.module.css
index 56b14923..e1faf6d3 100644
--- a/src/components/option/TableOfContents.module.css
+++ b/src/components/option/TableOfContents.module.css
@@ -1,7 +1,7 @@
.container {
+ @apply text-gray-800;
margin: 0;
padding: 0;
- color: #444;
}
.label {
diff --git a/src/components/ui/form.tsx b/src/components/ui/form.tsx
index 10602807..ea4e7532 100644
--- a/src/components/ui/form.tsx
+++ b/src/components/ui/form.tsx
@@ -1,8 +1,8 @@
-'use client'
+"use client"
-import * as React from 'react'
-import * as LabelPrimitive from '@radix-ui/react-label'
-import { Slot } from '@radix-ui/react-slot'
+import * as React from "react"
+import * as LabelPrimitive from "@radix-ui/react-label"
+import { Slot } from "@radix-ui/react-slot"
import {
Controller,
ControllerProps,
@@ -10,10 +10,10 @@ import {
FieldValues,
FormProvider,
useFormContext,
-} from 'react-hook-form'
+} from "react-hook-form"
-import { cn } from '@/lib/utils'
-import { Label } from '@/components/ui/label'
+import { cn } from "@/lib/utils"
+import { Label } from "@/components/ui/label"
const Form = FormProvider
@@ -49,7 +49,7 @@ const useFormField = () => {
const fieldState = getFieldState(fieldContext.name, formState)
if (!fieldContext) {
- throw new Error('useFormField should be used within
')
+ throw new Error("useFormField should be used within ")
}
const { id } = itemContext
@@ -84,7 +84,7 @@ const FormItem = React.forwardRef<
)
})
-FormItem.displayName = 'FormItem'
+FormItem.displayName = "FormItem"
const FormLabel = React.forwardRef<
React.ElementRef,
@@ -96,8 +96,8 @@ const FormLabel = React.forwardRef<
)
})
-FormLabel.displayName = 'FormLabel'
+FormLabel.displayName = "FormLabel"
const FormControl = React.forwardRef<
React.ElementRef,
@@ -127,7 +127,7 @@ const FormControl = React.forwardRef<
/>
)
})
-FormControl.displayName = 'FormControl'
+FormControl.displayName = "FormControl"
const FormDescription = React.forwardRef<
HTMLParagraphElement,
@@ -139,12 +139,12 @@ const FormDescription = React.forwardRef<
)
})
-FormDescription.displayName = 'FormDescription'
+FormDescription.displayName = "FormDescription"
const FormMessage = React.forwardRef<
HTMLParagraphElement,
@@ -161,14 +161,14 @@ const FormMessage = React.forwardRef<
{body}
)
})
-FormMessage.displayName = 'FormMessage'
+FormMessage.displayName = "FormMessage"
export {
useFormField,
From e213ed5ac694bc9fdef34b5a8bd192a1dc176ff2 Mon Sep 17 00:00:00 2001
From: ujiro99
Date: Tue, 8 Jul 2025 12:03:00 +0900
Subject: [PATCH 7/9] Refactoring
---
src/components/menu/Menu.tsx | 62 ++++++++++---------------------
src/components/ui/scroll-area.tsx | 14 ++++++-
2 files changed, 32 insertions(+), 44 deletions(-)
diff --git a/src/components/menu/Menu.tsx b/src/components/menu/Menu.tsx
index c050b63f..844994a2 100644
--- a/src/components/menu/Menu.tsx
+++ b/src/components/menu/Menu.tsx
@@ -1,12 +1,11 @@
import React, { useState, useRef } from "react"
-import clsx from "clsx"
import {
Menubar,
MenubarMenu,
MenubarTrigger,
MenubarContent,
} from "@/components/ui/menubar"
-import { ScrollArea } from "@/components/ui/scroll-area"
+import { ScrollAreaConditional } from "@/components/ui/scroll-area"
import { STYLE, SIDE } from "@/const"
import { MenuItem } from "./MenuItem"
@@ -16,7 +15,7 @@ import { MenuImage } from "@/components/menu/MenuImage"
import css from "./Menu.module.css"
import type { Command, CommandFolder } from "@/types"
import { useSettingsWithImageCache, useUserSettings } from "@/hooks/useSetting"
-import { onHover, isMenuCommand } from "@/lib/utils"
+import { cn, onHover, isMenuCommand } from "@/lib/utils"
import {
toCommandTree,
type CommandTreeNode,
@@ -48,7 +47,7 @@ export function Menu(): JSX.Element {
return (
{
const { folder, children, isHorizontal, depth = 0 } = props
- const [hoverTrigger, setHoverTrigger] = useState("")
- const [hoverContent, setHoverContent] = useState("")
- const activeFolder = hoverTrigger || hoverContent
+ const [triggeredFolder, setTriggeredFolder] = useState("")
+ const [hoveredFolder, setHoveredFolder] = useState("")
+ const activeFolder = triggeredFolder || hoveredFolder
const menuSide = isHorizontal
? props.side === SIDE.bottom
@@ -141,20 +140,19 @@ const MenuFolder = (props: {
}, 200)
}
- const baseSize = anchorRef.current?.clientHeight ?? 0
+ const baseSize = anchorRef.current?.getBoundingClientRect().height ?? 0
const menubarStyle = isHorizontal
? {
maxWidth:
baseSize * 10 /* buttons */ +
1 * 9 /* gap */ +
2 * 2 /* padding */ +
- 1 * 2 /* border */ +
- 5,
+ 1 * 2 /* border */,
}
: {
maxHeight:
- baseSize * 11.4 /* buttons */ +
- 2 * 11 /* gap */ +
+ baseSize * 11.5 /* buttons */ +
+ 1 * 10 /* gap */ +
2 * 2 /* padding */ +
1 * 2 /* border */,
}
@@ -162,7 +160,7 @@ const MenuFolder = (props: {
return (
{!(folder.onlyIcon && isHorizontal) && (
- {folder.title}
+ {folder.title}
)}
{!isHorizontal && }
e.preventDefault()}
{...onHover(props.onHoverContent, folder.id)}
>
- {!isHorizontal ? (
-
-
- {children?.map((child) => (
-
- ))}
-
-
- ) : (
+
@@ -226,14 +202,14 @@ const MenuFolder = (props: {
isHorizontal={isHorizontal}
side={props.side}
menuRef={props.menuRef}
- onHoverTrigger={setHoverTrigger}
- onHoverContent={setHoverContent}
+ onHoverTrigger={setTriggeredFolder}
+ onHoverContent={setHoveredFolder}
depth={depth + 1}
key={child.content.id}
/>
))}
- )}
+
& {
+ scrollEnabled?: boolean
+}) {
+ if (!scrollEnabled) {
+ return <>{props.children}>
+ }
+ return {props.children}
+}
+
function ScrollBar({
className,
orientation = "vertical",
@@ -55,4 +67,4 @@ function ScrollBar({
)
}
-export { ScrollArea, ScrollBar }
+export { ScrollArea, ScrollAreaConditional, ScrollBar }
From 2efde51e50739b26bfa709507cbce95a59aa534e Mon Sep 17 00:00:00 2001
From: ujiro99
Date: Tue, 8 Jul 2025 13:47:18 +0900
Subject: [PATCH 8/9] Update: Use resizeObserver for hoverArea calculation.
---
src/components/menu/Menu.tsx | 60 +++++++++++++++++++++++++++++++-----
1 file changed, 53 insertions(+), 7 deletions(-)
diff --git a/src/components/menu/Menu.tsx b/src/components/menu/Menu.tsx
index 844994a2..fbdd370d 100644
--- a/src/components/menu/Menu.tsx
+++ b/src/components/menu/Menu.tsx
@@ -1,4 +1,4 @@
-import React, { useState, useRef } from "react"
+import React, { useState, useRef, useEffect } from "react"
import {
Menubar,
MenubarMenu,
@@ -128,18 +128,64 @@ const MenuFolder = (props: {
const contentRef = useRef(null)
const [anchorRect, setAnchorRect] = useState(null)
const [contentRect, setContentRect] = useState(null)
+ const resizeObserverRef = useRef(null)
+
+ const updateRects = () => {
+ if (anchorRef.current && contentRef.current) {
+ setAnchorRect(anchorRef.current.getBoundingClientRect())
+ setContentRect(contentRef.current.getBoundingClientRect())
+ }
+ }
const onHoverTrigger = (enterVal: any) => {
props.onHoverTrigger(enterVal)
- // Delay to wait finishing animation.
- setTimeout(() => {
- if (anchorRef.current && contentRef.current) {
- setAnchorRect(anchorRef.current?.getBoundingClientRect())
- setContentRect(contentRef.current?.getBoundingClientRect())
+
+ // Initial rect update
+ updateRects()
+
+ // Setup ResizeObserver if not already setup
+ if (!resizeObserverRef.current) {
+ resizeObserverRef.current = new ResizeObserver(() => {
+ updateRects()
+ })
+
+ // Observe anchor element
+ if (anchorRef.current) {
+ resizeObserverRef.current.observe(anchorRef.current)
}
- }, 200)
+
+ // Observe content element if available, otherwise retry once
+ if (contentRef.current) {
+ resizeObserverRef.current.observe(contentRef.current)
+ } else {
+ setTimeout(() => {
+ if (contentRef.current && resizeObserverRef.current) {
+ resizeObserverRef.current.observe(contentRef.current)
+ updateRects()
+ }
+ }, 50)
+ }
+ }
}
+ // Cleanup ResizeObserver on unmount
+ useEffect(() => {
+ return () => {
+ if (resizeObserverRef.current) {
+ resizeObserverRef.current.disconnect()
+ resizeObserverRef.current = null
+ }
+ }
+ }, [])
+
+ // Also cleanup when folder changes
+ useEffect(() => {
+ if (resizeObserverRef.current) {
+ resizeObserverRef.current.disconnect()
+ resizeObserverRef.current = null
+ }
+ }, [folder.id])
+
const baseSize = anchorRef.current?.getBoundingClientRect().height ?? 0
const menubarStyle = isHorizontal
? {
From 5fbf5b76316a24b888deb55bf6761fb6f9c39780 Mon Sep 17 00:00:00 2001
From: ujiro99
Date: Tue, 8 Jul 2025 21:24:39 +0900
Subject: [PATCH 9/9] Update: Don't limit to 100 commands.
---
public/_locales/de/messages.json | 2 +-
public/_locales/en/messages.json | 2 +-
public/_locales/es/messages.json | 2 +-
public/_locales/fr/messages.json | 2 +-
public/_locales/hi/messages.json | 2 +-
public/_locales/id/messages.json | 2 +-
public/_locales/it/messages.json | 2 +-
public/_locales/ja/messages.json | 2 +-
public/_locales/ko/messages.json | 2 +-
public/_locales/ms/messages.json | 2 +-
public/_locales/pt_BR/messages.json | 2 +-
public/_locales/pt_PT/messages.json | 2 +-
public/_locales/ru/messages.json | 2 +-
public/_locales/zh_CN/messages.json | 2 +-
.../option/editor/CommandListMenu.tsx | 24 +++++++++----------
15 files changed, 26 insertions(+), 26 deletions(-)
diff --git a/public/_locales/de/messages.json b/public/_locales/de/messages.json
index 3fcf4d6e..b06b8442 100644
--- a/public/_locales/de/messages.json
+++ b/public/_locales/de/messages.json
@@ -222,7 +222,7 @@
"message": "Befehle definieren, die basierend auf dem ausgewählten Text ausgeführt werden sollen."
},
"Option_commands_desc_count": {
- "message": " / 100 Einträge"
+ "message": " Einträge"
},
"Option_title": {
"message": "Titel"
diff --git a/public/_locales/en/messages.json b/public/_locales/en/messages.json
index 7ece6283..1fa6d703 100644
--- a/public/_locales/en/messages.json
+++ b/public/_locales/en/messages.json
@@ -222,7 +222,7 @@
"message": "Define commands to execute based on selected text."
},
"Option_commands_desc_count": {
- "message": " / 100 Records"
+ "message": " items"
},
"Option_title": {
"message": "Title"
diff --git a/public/_locales/es/messages.json b/public/_locales/es/messages.json
index 8b546eda..f6e51ae6 100644
--- a/public/_locales/es/messages.json
+++ b/public/_locales/es/messages.json
@@ -222,7 +222,7 @@
"message": "Definir comandos para ejecutar basados en el texto seleccionado."
},
"Option_commands_desc_count": {
- "message": " / 100 Registros"
+ "message": " registros"
},
"Option_title": {
"message": "Título"
diff --git a/public/_locales/fr/messages.json b/public/_locales/fr/messages.json
index 3698edcf..e956dbbc 100644
--- a/public/_locales/fr/messages.json
+++ b/public/_locales/fr/messages.json
@@ -222,7 +222,7 @@
"message": "Définir des commandes à exécuter en fonction du texte sélectionné."
},
"Option_commands_desc_count": {
- "message": " / 100 Enregistrements"
+ "message": " éléments"
},
"Option_title": {
"message": "Titre"
diff --git a/public/_locales/hi/messages.json b/public/_locales/hi/messages.json
index fbdd412b..e30f4ac8 100644
--- a/public/_locales/hi/messages.json
+++ b/public/_locales/hi/messages.json
@@ -222,7 +222,7 @@
"message": "चयनित टेक्स्ट के आधार पर निष्पादित करने के लिए कमांड परिभाषित करता है।"
},
"Option_commands_desc_count": {
- "message": " / 100 आइटम"
+ "message": " आइटम"
},
"Option_title": {
"message": "शीर्षक"
diff --git a/public/_locales/id/messages.json b/public/_locales/id/messages.json
index 0f74e261..3c270188 100644
--- a/public/_locales/id/messages.json
+++ b/public/_locales/id/messages.json
@@ -222,7 +222,7 @@
"message": "Tentukan perintah untuk dieksekusi berdasarkan teks yang dipilih."
},
"Option_commands_desc_count": {
- "message": " / 100 Catatan"
+ "message": " item"
},
"Option_title": {
"message": "Judul"
diff --git a/public/_locales/it/messages.json b/public/_locales/it/messages.json
index 94363fc4..5d869372 100644
--- a/public/_locales/it/messages.json
+++ b/public/_locales/it/messages.json
@@ -222,7 +222,7 @@
"message": "Definisce i comandi da eseguire in base al testo selezionato."
},
"Option_commands_desc_count": {
- "message": " / 100 elementi"
+ "message": " elementi"
},
"Option_title": {
"message": "Titolo"
diff --git a/public/_locales/ja/messages.json b/public/_locales/ja/messages.json
index f31492ac..ac4e67e6 100644
--- a/public/_locales/ja/messages.json
+++ b/public/_locales/ja/messages.json
@@ -222,7 +222,7 @@
"message": "選択したテキストを元に実行するコマンドを定義します。"
},
"Option_commands_desc_count": {
- "message": " / 100 件"
+ "message": " 件"
},
"Option_title": {
"message": "タイトル"
diff --git a/public/_locales/ko/messages.json b/public/_locales/ko/messages.json
index 9997794f..a29e72d7 100644
--- a/public/_locales/ko/messages.json
+++ b/public/_locales/ko/messages.json
@@ -222,7 +222,7 @@
"message": "선택한 텍스트를 기반으로 실행할 명령을 정의합니다."
},
"Option_commands_desc_count": {
- "message": " / 100개 항목"
+ "message": " 개"
},
"Option_title": {
"message": "제목"
diff --git a/public/_locales/ms/messages.json b/public/_locales/ms/messages.json
index c8b1640b..6dde455f 100644
--- a/public/_locales/ms/messages.json
+++ b/public/_locales/ms/messages.json
@@ -222,7 +222,7 @@
"message": "Tentukan arahan untuk dilaksanakan berdasarkan teks yang dipilih."
},
"Option_commands_desc_count": {
- "message": " / 100 Rekod"
+ "message": " item"
},
"Option_title": {
"message": "Tajuk"
diff --git a/public/_locales/pt_BR/messages.json b/public/_locales/pt_BR/messages.json
index c9b1aeb7..d19dd0e3 100644
--- a/public/_locales/pt_BR/messages.json
+++ b/public/_locales/pt_BR/messages.json
@@ -222,7 +222,7 @@
"message": "Definir comandos para executar com base no texto selecionado."
},
"Option_commands_desc_count": {
- "message": " / 100 Registros"
+ "message": " itens"
},
"Option_title": {
"message": "Título"
diff --git a/public/_locales/pt_PT/messages.json b/public/_locales/pt_PT/messages.json
index 918ab745..206ab9ad 100644
--- a/public/_locales/pt_PT/messages.json
+++ b/public/_locales/pt_PT/messages.json
@@ -222,7 +222,7 @@
"message": "Definir comandos para executar com base no texto selecionado."
},
"Option_commands_desc_count": {
- "message": " / 100 Registos"
+ "message": " itens"
},
"Option_title": {
"message": "Título"
diff --git a/public/_locales/ru/messages.json b/public/_locales/ru/messages.json
index 2f5c167a..6bb4ca45 100644
--- a/public/_locales/ru/messages.json
+++ b/public/_locales/ru/messages.json
@@ -222,7 +222,7 @@
"message": "Определяет команды для выполнения на основе выбранного текста."
},
"Option_commands_desc_count": {
- "message": " / 100 элементов"
+ "message": " элементов"
},
"Option_title": {
"message": "Название"
diff --git a/public/_locales/zh_CN/messages.json b/public/_locales/zh_CN/messages.json
index 13db41c1..84dfeaa9 100644
--- a/public/_locales/zh_CN/messages.json
+++ b/public/_locales/zh_CN/messages.json
@@ -222,7 +222,7 @@
"message": "定义基于选中文本执行的命令。"
},
"Option_commands_desc_count": {
- "message": " / 100个项目"
+ "message": " 个"
},
"Option_title": {
"message": "标题"
diff --git a/src/components/option/editor/CommandListMenu.tsx b/src/components/option/editor/CommandListMenu.tsx
index 12604e3c..23e59ed1 100644
--- a/src/components/option/editor/CommandListMenu.tsx
+++ b/src/components/option/editor/CommandListMenu.tsx
@@ -1,9 +1,9 @@
-import React from 'react'
-import { Button } from '@/components/ui/button'
-import { Terminal, FolderPlus, Search } from 'lucide-react'
-import { Tooltip } from '@/components/Tooltip'
-import { HUB_URL } from '@/const'
-import { t as _t } from '@/services/i18n'
+import React from "react"
+import { Button } from "@/components/ui/button"
+import { Terminal, FolderPlus, Search } from "lucide-react"
+import { Tooltip } from "@/components/Tooltip"
+import { HUB_URL } from "@/const"
+import { t as _t } from "@/services/i18n"
const t = (key: string, p?: string[]) => _t(`Option_${key}`, p)
interface Props {
@@ -25,7 +25,7 @@ export const CommandListMenu: React.FC = ({
{commandCount ?? 0}
- {t('commands_desc_count')}
+ {t("commands_desc_count")}
= ({
onClick={onAddFolder}
>
- {t('folders')}
+ {t("folders")}
= ({
onClick={onAddCommand}
>
- {t('Command')}
+ {t("Command")}
= ({
)
-}
\ No newline at end of file
+}