diff --git a/src/locales/ach-UG/translation.json b/src/locales/ach-UG/translation.json index cf254e349..f4d8ceec2 100644 --- a/src/locales/ach-UG/translation.json +++ b/src/locales/ach-UG/translation.json @@ -39,6 +39,7 @@ "cloud_sync_account_verification": "crwdns7954:0crwdne7954:0", "cloud_sync_verification_failed": "crwdns7956:0crwdne7956:0", "save_success": "crwdns7958:0crwdne7958:0", + "reset_success": "Reset successfully", "update": "crwdns7960:0crwdne7960:0", "check_update": "crwdns7962:0crwdne7962:0", "script_subscription_check_interval": "crwdns7964:0crwdne7964:0", diff --git a/src/locales/de-DE/translation.json b/src/locales/de-DE/translation.json index 6ea643df8..1b453a77a 100644 --- a/src/locales/de-DE/translation.json +++ b/src/locales/de-DE/translation.json @@ -40,6 +40,7 @@ "cloud_sync_account_verification": "Cloud-Sync-Kontoinformationen werden überprüft...", "cloud_sync_verification_failed": "Cloud-Sync-Kontoinformationen-Überprüfung fehlgeschlagen", "save_success": "Erfolgreich gespeichert", + "reset_success": "Erfolgreich zurückgesetzt", "update": "Aktualisieren", "check_update": "Nach Updates suchen", "script_subscription_check_interval": "Skript-/Abonnement-Update-Überprüfungsintervall", diff --git a/src/locales/en-US/translation.json b/src/locales/en-US/translation.json index 1091314dd..8e1b3d74b 100644 --- a/src/locales/en-US/translation.json +++ b/src/locales/en-US/translation.json @@ -39,7 +39,8 @@ "debug": "Debug", "cloud_sync_account_verification": "Cloud Sync Account Verification in Progress...", "cloud_sync_verification_failed": "Cloud Sync Account Verification Failed", - "save_success": "Save Successful", + "save_success": "Saved successfully", + "reset_success": "Reset successfully", "update": "Update", "check_update": "Check Update", "script_subscription_check_interval": "Script/Subscription Update Check Interval", diff --git a/src/locales/ja-JP/translation.json b/src/locales/ja-JP/translation.json index 346ac1524..7789d26f8 100644 --- a/src/locales/ja-JP/translation.json +++ b/src/locales/ja-JP/translation.json @@ -40,6 +40,7 @@ "cloud_sync_account_verification": "クラウド同期アカウント情報を確認中...", "cloud_sync_verification_failed": "クラウド同期アカウント情報の確認に失敗しました", "save_success": "保存に成功しました", + "reset_success": "リセットに成功しました", "update": "更新", "check_update": "更新をチェック", "script_subscription_check_interval": "スクリプト/サブスクライブの更新チェック間隔", diff --git a/src/locales/ru-RU/translation.json b/src/locales/ru-RU/translation.json index cc80fe49e..66dfe663e 100644 --- a/src/locales/ru-RU/translation.json +++ b/src/locales/ru-RU/translation.json @@ -39,7 +39,8 @@ "debug": "Отладка", "cloud_sync_account_verification": "Проверка учетной записи облачной синхронизации...", "cloud_sync_verification_failed": "Ошибка проверки учетной записи облачной синхронизации", - "save_success": "Успешно сохранено", + "save_success": "Сохранено успешно", + "reset_success": "Сброс выполнен успешно", "update": "Обновить", "check_update": "Проверить обновления", "script_subscription_check_interval": "Интервал проверки обновлений скриптов/подписок", diff --git a/src/locales/vi-VN/translation.json b/src/locales/vi-VN/translation.json index f16febc99..135da1557 100644 --- a/src/locales/vi-VN/translation.json +++ b/src/locales/vi-VN/translation.json @@ -40,6 +40,7 @@ "cloud_sync_account_verification": "Đang xác minh tài khoản đồng bộ đám mây...", "cloud_sync_verification_failed": "Xác minh tài khoản đồng bộ đám mây thất bại", "save_success": "Lưu thành công", + "reset_success": "Đặt lại thành công", "update": "Cập nhật", "check_update": "Kiểm tra cập nhật", "script_subscription_check_interval": "Khoảng thời gian kiểm tra cập nhật script/đăng ký", diff --git a/src/locales/zh-CN/translation.json b/src/locales/zh-CN/translation.json index 508c9ea3d..973b9418a 100644 --- a/src/locales/zh-CN/translation.json +++ b/src/locales/zh-CN/translation.json @@ -40,6 +40,7 @@ "cloud_sync_account_verification": "云同步账号信息验证中...", "cloud_sync_verification_failed": "云同步账号信息验证失败", "save_success": "保存成功", + "reset_success": "重置成功", "update": "更新", "check_update": "检查更新", "script_subscription_check_interval": "脚本/订阅检查更新间隔", diff --git a/src/locales/zh-TW/translation.json b/src/locales/zh-TW/translation.json index abb5c289d..3d2addcfa 100644 --- a/src/locales/zh-TW/translation.json +++ b/src/locales/zh-TW/translation.json @@ -40,6 +40,7 @@ "cloud_sync_account_verification": "雲端同步帳號資訊驗證中...", "cloud_sync_verification_failed": "雲端同步帳號資訊驗證失敗", "save_success": "儲存成功", + "reset_success": "重設成功", "update": "更新", "check_update": "檢查更新", "script_subscription_check_interval": "腳本/訂閱檢查更新間隔", diff --git a/src/pages/options/routes/ScriptList/ScriptTable.tsx b/src/pages/options/routes/ScriptList/ScriptTable.tsx index d9d64a5b3..058ba6c8b 100644 --- a/src/pages/options/routes/ScriptList/ScriptTable.tsx +++ b/src/pages/options/routes/ScriptList/ScriptTable.tsx @@ -532,26 +532,25 @@ const ScriptTable = ({ const [select, setSelect] = useState([]); const [selectColumn, setSelectColumn] = useState(0); const navigate = useNavigate(); - const [savedWidths, setSavedWidths] = useState<{ [key: string]: number } | null>(null); - const columns0: ColumnProps[] = [ - { - title: "#", - dataIndex: "sort", - width: 60, - key: "#", - sorter: useCallback((a: ListType, b: ListType) => a.sort - b.sort, []), - render: useCallback((col: number) => , []), - }, - { - key: "title", - title: t("enable"), - width: t("script_list_enable_width"), - dataIndex: "status", - className: "script-enable", - sorter: useCallback((a: ListType, b: ListType) => a.status - b.status, []), - filters: useMemo( - () => [ + const columns: ColumnProps[] = useMemo( + () => [ + { + title: "#", + dataIndex: "sort", + width: 60, + key: "#", + sorter: (a: ListType, b: ListType) => a.sort - b.sort, + render: (col: number) => , + }, + { + key: "title", + title: t("enable"), + width: t("script_list_enable_width"), + dataIndex: "status", + className: "script-enable", + sorter: (a: ListType, b: ListType) => a.status - b.status, + filters: [ { text: t("enable"), value: SCRIPT_STATUS_ENABLE, @@ -561,88 +560,79 @@ const ScriptTable = ({ value: SCRIPT_STATUS_DISABLE, }, ], - [t] - ), - onFilter: useCallback((value: any, row: any) => row.status === value, []), - render: useCallback( - (col: any, item: ListType) => , - [updateScripts] - ), - }, - { - key: "name", - title: t("name"), - dataIndex: "name", - sorter: useCallback((a: ListType, b: ListType) => a.name.localeCompare(b.name), []), - filterIcon: , - filterDropdown: useCallback(({ filterKeys, setFilterKeys, confirm }: any) => { - // setFilterKeys, confirm 会不断改变参考但又不影响元件绘画。用 filterDropdownFunctions 把它们抽出 React绘图 - filterDropdownFunctions.setFilterKeys = setFilterKeys; - filterDropdownFunctions.confirm = confirm; - return ; - }, []), - onFilter: useCallback((value: any, row: any) => { - if (!value || !value.keyword) { - return true; - } - return SearchFilter.checkByUUID(row.uuid); - }, []), - className: "tw-max-w-[240px] tw-min-w-[100px]", - render: useCallback((col: string, item: ListType) => , []), - }, - { - title: t("version"), - dataIndex: "version", - key: "version", - width: 120, - align: "center", - render: useCallback((col: any, item: ListType) => , []), - }, - { - key: "apply_to_run_status", - dataIndex: "apply_to_run_status", - title: t("apply_to_run_status"), - width: t("script_list_apply_to_run_status_width"), - className: "apply_to_run_status", - render: useCallback( - (col: any, item: ListType) => , - [navigate, t] - ), - }, - { - title: t("source"), - dataIndex: "origin", - key: "origin", - width: 100, - className: "source_cell", - render: useCallback((col: any, item: ListType) => , [t]), - }, - { - title: t("home"), - dataIndex: "home", - align: "center", - key: "home", - width: 100, - render: useCallback((col: any, item: ListType) => , []), - }, - { - title: t("last_updated"), - dataIndex: "updatetime", - align: "center", - key: "updatetime", - className: "script-updatetime", - width: t("script_list_last_updated_width"), - sorter: useCallback((a: ListType, b: ListType) => a.updatetime! - b.updatetime!, []), - render: useCallback((col: number, script: ListType) => , []), - }, - { - title: , - dataIndex: "action", - key: "action", - className: "script-action", - width: 160, - render: useCallback( - (col: any, item: ListType) => ( + onFilter: (value: any, row: any) => row.status === value, + render: (col: any, item: ListType) => , + }, + { + key: "name", + title: t("name"), + dataIndex: "name", + sorter: (a: ListType, b: ListType) => a.name.localeCompare(b.name), + filterIcon: , + filterDropdown: ({ filterKeys, setFilterKeys, confirm }: any) => { + // setFilterKeys, confirm 会不断改变参考但又不影响元件绘画。用 filterDropdownFunctions 把它们抽出 React绘图 + filterDropdownFunctions.setFilterKeys = setFilterKeys; + filterDropdownFunctions.confirm = confirm; + return ; + }, + onFilter: (value: any, row: any) => { + if (!value || !value.keyword) { + return true; + } + return SearchFilter.checkByUUID(row.uuid); + }, + className: "tw-max-w-[240px] tw-min-w-[100px]", + render: (col: string, item: ListType) => , + }, + { + title: t("version"), + dataIndex: "version", + key: "version", + width: 120, + align: "center", + render: (col: any, item: ListType) => , + }, + { + key: "apply_to_run_status", + dataIndex: "apply_to_run_status", + title: t("apply_to_run_status"), + width: t("script_list_apply_to_run_status_width"), + className: "apply_to_run_status", + render: (col: any, item: ListType) => , + }, + { + title: t("source"), + dataIndex: "origin", + key: "origin", + width: 100, + className: "source_cell", + render: (col: any, item: ListType) => , + }, + { + title: t("home"), + dataIndex: "home", + align: "center", + key: "home", + width: 100, + render: (col: any, item: ListType) => , + }, + { + title: t("last_updated"), + dataIndex: "updatetime", + align: "center", + key: "updatetime", + className: "script-updatetime", + width: t("script_list_last_updated_width"), + sorter: (a: ListType, b: ListType) => a.updatetime! - b.updatetime!, + render: (col: number, script: ListType) => , + }, + { + title: , + dataIndex: "action", + key: "action", + className: "script-action", + width: 160, + render: (col: any, item: ListType) => ( ), - [handleConfig, handleDelete, handleRunStop, setCloudScript, setUserConfig, t] - ), - }, - ]; + }, + ], + [ + handleConfig, + handleDelete, + handleRunStop, + navigate, + setCloudScript, + setSidebarOpen, + setUserConfig, + setViewMode, + sidebarOpen, + t, + updateScripts, + ] + ); - // 语言改变 或 sidebarOpen 改变时,更新 columns - // eslint-disable-next-line react-hooks/exhaustive-deps - const columns = useMemo(() => columns0, [t, sidebarOpen]); + // 1. Only store the width overrides, initialized from your saved settings + const [manualWidths, setManualWidths] = useState>({}); + // 2. Local state for the input field to make typing "instant" + const [inputBuffer, setInputBuffer] = useState(""); + const [typing, setTyping] = useState(false); - const [newColumns, setNewColumns] = useState([]); + // 3. Update the buffer when the selected column changes + useEffect(() => { + const activeCol = columns[selectColumn]; + if (!activeCol) return; // Safety check - const dealColumns = useMemo(() => { - const filtered = newColumns.filter((item) => item.width !== -1); - return filtered.length === 0 ? columns : filtered; - }, [newColumns, columns]); + // 1. Get width from manual overrides + // 2. Or get width from the column definition + // 3. Or fallback to 0 (or your preferred default) to avoid undefined + const currentWidth = manualWidths[activeCol.key as string] ?? activeCol.width ?? 0; + setTyping(false); + setInputBuffer(currentWidth.toString()); + }, [selectColumn, manualWidths, columns]); + // 4. Optimized setWidth using the Column Key + const setWidthByKey = (key: string, width: number) => { + setManualWidths((prev) => { + return prev[key] === width ? prev : { ...prev, [key]: width }; + }); + }; + // 2. Load initial widths once useEffect(() => { - if (savedWidths === null) return; + systemConfig.getScriptListColumnWidth().then((saved) => { + if (saved) setManualWidths(saved); + }); + }, []); - // 主要只需要处理列宽变化的情况 - setNewColumns( - columns.map((item, i) => { - if (savedWidths[item.key!] === undefined) { - return columns[i]; - } + // 3. MERGE logic: This is the high-performance "Source of Truth" + const dealColumns = useMemo(() => { + return columns + .map((col) => { + const customWidth = manualWidths[col.key as string]; return { - ...columns[i], - width: savedWidths[item.key!] ?? item.width, + ...col, + // If customWidth is undefined, use default. + // 0 = Auto, -1 = Hidden + width: customWidth !== undefined ? customWidth : col.width, }; }) - ); - }, [savedWidths, columns]); - - useEffect(() => { - systemConfig.getScriptListColumnWidth().then((columnWidth) => { - setSavedWidths({ ...columnWidth }); - }); - }, []); + .filter((col) => col.width !== -1); // Remove hidden columns + }, [columns, manualWidths]); - const components0: ComponentsProps = { - header: { - operations: useCallback( - ({ selectionNode, expandNode }: { selectionNode?: React.ReactNode; expandNode?: React.ReactNode }) => [ + const components: ComponentsProps = useMemo( + () => ({ + header: { + operations: ({ + selectionNode, + expandNode, + }: { + selectionNode?: React.ReactNode; + expandNode?: React.ReactNode; + }) => [ { node: , width: 34, @@ -709,12 +730,15 @@ const ScriptTable = ({ node: selectionNode, }, ], - [] - ), - }, - body: { - operations: useCallback( - ({ selectionNode, expandNode }: { selectionNode?: React.ReactNode; expandNode?: React.ReactNode }) => [ + }, + body: { + operations: ({ + selectionNode, + expandNode, + }: { + selectionNode?: React.ReactNode; + expandNode?: React.ReactNode; + }) => [ { node: ( @@ -734,21 +758,12 @@ const ScriptTable = ({ node: selectionNode, }, ], - [] - ), - tbody: DraggableContainer, - row: DraggableRow, - }, - }; - - // eslint-disable-next-line react-hooks/exhaustive-deps - const components = useMemo(() => components0, []); - - const setWidth = (selectColumn: number, width: string | number | undefined) => { - setNewColumns((cols) => - cols.map((col, i) => (i === selectColumn && col.width !== width ? { ...col, width } : col)) - ); - }; + tbody: DraggableContainer, + row: DraggableRow, + }, + }), + [] + ); // 处理拖拽排序 const sensors = useSensors( @@ -807,6 +822,9 @@ const ScriptTable = ({ [] ); + const currentActiveWidth = manualWidths[columns[selectColumn].key as string] ?? columns[selectColumn].width; + const isSpecialWidth = currentActiveWidth === 0 || currentActiveWidth === -1; + return ( {showAction && ( @@ -946,48 +964,36 @@ const ScriptTable = ({ {t("resize_column_width") + ":"} - { - setWidth(selectColumn, 0); - }} - > + setWidthByKey(columns[selectColumn].key as string, 0)}> {t("auto")} - { - setWidth(selectColumn, -1); - }} - > + setWidthByKey(columns[selectColumn].key as string, -1)}> {t("hide")} { - const width = - (newColumns[selectColumn].width as number) > 0 - ? newColumns[selectColumn].width - : columns[selectColumn].width; - setWidth(selectColumn, width); + // If current is auto/hide, reset to base width; otherwise keep it + const baseWidth = columns[selectColumn].width as number; + setWidthByKey(columns[selectColumn].key as string, baseWidth); }} > {t("custom")} @@ -997,45 +1003,64 @@ const ScriptTable = ({ position="bl" > { + setTyping(true); + }} onChange={(val) => { - const width = parseInt(val, 10); - setWidth(selectColumn, width); + // 數值輸入忽略 -1 和 0 + setInputBuffer(val); + }} + onPointerUp={() => { + setTyping(false); + const width = parseInt(inputBuffer, 10); + if (!isNaN(width)) setWidthByKey(columns[selectColumn].key as string, width); + }} + // Trigger the heavy table re-render only when finished + onBlur={() => { + setTyping(false); + const width = parseInt(inputBuffer, 10); + if (!isNaN(width)) setWidthByKey(columns[selectColumn].key as string, width); + }} + onPressEnter={() => { + setTyping(false); + const width = parseInt(inputBuffer, 10); + if (!isNaN(width)) setWidthByKey(columns[selectColumn].key as string, width); }} /> + +