mirror of
https://github.com/zhom/donutbrowser.git
synced 2026-08-09 20:54:28 +02:00
feat: profile sorting
This commit is contained in:
@@ -51,6 +51,12 @@ import {
|
|||||||
CommandItem,
|
CommandItem,
|
||||||
CommandList,
|
CommandList,
|
||||||
} from "@/components/ui/command";
|
} from "@/components/ui/command";
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from "@/components/ui/dropdown-menu";
|
||||||
import {
|
import {
|
||||||
Popover,
|
Popover,
|
||||||
PopoverContent,
|
PopoverContent,
|
||||||
@@ -2381,28 +2387,89 @@ export function ProfilesDataTable({
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// Hidden, sort-only column so profiles can be sorted by creation date
|
||||||
|
// without showing a Created column in the table (issue #454). Kept
|
||||||
|
// hidden via columnVisibility; sorting still works on hidden columns.
|
||||||
|
id: "created_at",
|
||||||
|
accessorFn: (row) => row.created_at ?? 0,
|
||||||
|
enableSorting: true,
|
||||||
|
enableHiding: true,
|
||||||
|
sortingFn: "basic",
|
||||||
|
header: () => null,
|
||||||
|
cell: () => null,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
accessorKey: "name",
|
accessorKey: "name",
|
||||||
// The only column without a fixed width: table-fixed hands it all
|
// The only column without a fixed width: table-fixed hands it all
|
||||||
// remaining space as the window grows or shrinks.
|
// remaining space as the window grows or shrinks.
|
||||||
meta: { flexWidth: true },
|
meta: { flexWidth: true },
|
||||||
header: ({ column, table }) => {
|
// The Name header doubles as the sort control: clicking opens a menu to
|
||||||
|
// sort by name (A–Z / Z–A) or by creation date (newest / oldest), so
|
||||||
|
// creation-date sorting needs no visible column.
|
||||||
|
header: ({ table }) => {
|
||||||
const meta = table.options.meta as TableMeta;
|
const meta = table.options.meta as TableMeta;
|
||||||
|
const sort = table.getState().sorting[0];
|
||||||
|
const isActive = (id: string, desc: boolean) =>
|
||||||
|
sort?.id === id && !!sort.desc === desc;
|
||||||
return (
|
return (
|
||||||
<Button
|
<DropdownMenu>
|
||||||
variant="ghost"
|
<DropdownMenuTrigger asChild>
|
||||||
onClick={() => {
|
<Button
|
||||||
column.toggleSorting(column.getIsSorted() === "asc");
|
variant="ghost"
|
||||||
}}
|
className="justify-start p-0 h-auto font-semibold text-left cursor-pointer"
|
||||||
className="justify-start p-0 h-auto font-semibold text-left cursor-pointer"
|
>
|
||||||
>
|
{meta.t("common.labels.name")}
|
||||||
{meta.t("common.labels.name")}
|
{isActive("name", false) ? (
|
||||||
{column.getIsSorted() === "asc" ? (
|
<LuChevronUp className="ml-2 size-4" />
|
||||||
<LuChevronUp className="ml-2 size-4" />
|
) : isActive("name", true) ? (
|
||||||
) : column.getIsSorted() === "desc" ? (
|
<LuChevronDown className="ml-2 size-4" />
|
||||||
<LuChevronDown className="ml-2 size-4" />
|
) : (
|
||||||
) : null}
|
<LuChevronDown className="ml-2 size-4 opacity-50" />
|
||||||
</Button>
|
)}
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent align="start">
|
||||||
|
<DropdownMenuItem
|
||||||
|
onClick={() =>
|
||||||
|
table.setSorting([{ id: "name", desc: false }])
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{isActive("name", false) && (
|
||||||
|
<LuCheck className="mr-2 size-3.5" />
|
||||||
|
)}
|
||||||
|
{meta.t("profiles.sort.nameAsc")}
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem
|
||||||
|
onClick={() => table.setSorting([{ id: "name", desc: true }])}
|
||||||
|
>
|
||||||
|
{isActive("name", true) && (
|
||||||
|
<LuCheck className="mr-2 size-3.5" />
|
||||||
|
)}
|
||||||
|
{meta.t("profiles.sort.nameDesc")}
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem
|
||||||
|
onClick={() =>
|
||||||
|
table.setSorting([{ id: "created_at", desc: true }])
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{isActive("created_at", true) && (
|
||||||
|
<LuCheck className="mr-2 size-3.5" />
|
||||||
|
)}
|
||||||
|
{meta.t("profiles.sort.newest")}
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem
|
||||||
|
onClick={() =>
|
||||||
|
table.setSorting([{ id: "created_at", desc: false }])
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{isActive("created_at", false) && (
|
||||||
|
<LuCheck className="mr-2 size-3.5" />
|
||||||
|
)}
|
||||||
|
{meta.t("profiles.sort.oldest")}
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
enableSorting: true,
|
enableSorting: true,
|
||||||
@@ -2934,7 +3001,7 @@ export function ProfilesDataTable({
|
|||||||
// expendable first); their data stays reachable via the profile info
|
// expendable first); their data stays reachable via the profile info
|
||||||
// dialog. Visibility (not CSS hiding) so table-fixed reclaims the width.
|
// dialog. Visibility (not CSS hiding) so table-fixed reclaims the width.
|
||||||
const [columnVisibility, setColumnVisibility] =
|
const [columnVisibility, setColumnVisibility] =
|
||||||
React.useState<VisibilityState>({});
|
React.useState<VisibilityState>({ created_at: false });
|
||||||
|
|
||||||
// Content columns grow proportionally with the container but never drop
|
// Content columns grow proportionally with the container but never drop
|
||||||
// below the compact-layout floor; the name column takes the remainder.
|
// below the compact-layout floor; the name column takes the remainder.
|
||||||
@@ -2994,6 +3061,8 @@ export function ProfilesDataTable({
|
|||||||
setContainerWidth(Math.round(w / 8) * 8);
|
setContainerWidth(Math.round(w / 8) * 8);
|
||||||
setColumnVisibility((prev) => {
|
setColumnVisibility((prev) => {
|
||||||
const next: VisibilityState = {
|
const next: VisibilityState = {
|
||||||
|
// Always hidden — sort-only column (issue #454).
|
||||||
|
created_at: false,
|
||||||
dns: w >= 768,
|
dns: w >= 768,
|
||||||
ext: w >= 672,
|
ext: w >= 672,
|
||||||
note: w >= 576,
|
note: w >= 576,
|
||||||
|
|||||||
@@ -878,6 +878,17 @@ function ProfileInfoLayout({
|
|||||||
{t("profileInfo.sections.activity")}
|
{t("profileInfo.sections.activity")}
|
||||||
</span>
|
</span>
|
||||||
<div className="grid grid-cols-2 gap-2">
|
<div className="grid grid-cols-2 gap-2">
|
||||||
|
<InfoCard
|
||||||
|
label={t("profileInfo.fields.created")}
|
||||||
|
value={
|
||||||
|
profile.created_at
|
||||||
|
? new Date(profile.created_at * 1000).toLocaleString(
|
||||||
|
undefined,
|
||||||
|
{ dateStyle: "medium", timeStyle: "short" },
|
||||||
|
)
|
||||||
|
: t("profileInfo.values.unknown")
|
||||||
|
}
|
||||||
|
/>
|
||||||
<InfoCard
|
<InfoCard
|
||||||
label={t("profileInfo.fields.lastLaunched")}
|
label={t("profileInfo.fields.lastLaunched")}
|
||||||
value={
|
value={
|
||||||
|
|||||||
@@ -325,6 +325,12 @@
|
|||||||
"confirmDescription": "Stop {{count}} running profiles?",
|
"confirmDescription": "Stop {{count}} running profiles?",
|
||||||
"confirmButton": "Stop {{count}}",
|
"confirmButton": "Stop {{count}}",
|
||||||
"noneToStop": "No selected profiles to stop."
|
"noneToStop": "No selected profiles to stop."
|
||||||
|
},
|
||||||
|
"sort": {
|
||||||
|
"nameAsc": "Name (A–Z)",
|
||||||
|
"nameDesc": "Name (Z–A)",
|
||||||
|
"newest": "Newest first",
|
||||||
|
"oldest": "Oldest first"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"createProfile": {
|
"createProfile": {
|
||||||
@@ -1149,7 +1155,8 @@
|
|||||||
"proxy": "PROXY",
|
"proxy": "PROXY",
|
||||||
"vpn": "VPN",
|
"vpn": "VPN",
|
||||||
"cookieCount": "Cookies stored",
|
"cookieCount": "Cookies stored",
|
||||||
"localDataTransfer": "Local data transfer"
|
"localDataTransfer": "Local data transfer",
|
||||||
|
"created": "Created"
|
||||||
},
|
},
|
||||||
"values": {
|
"values": {
|
||||||
"none": "None",
|
"none": "None",
|
||||||
@@ -1158,7 +1165,8 @@
|
|||||||
"yes": "Yes",
|
"yes": "Yes",
|
||||||
"activeNow": "Active now",
|
"activeNow": "Active now",
|
||||||
"direct": "Direct",
|
"direct": "Direct",
|
||||||
"loading": "Loading…"
|
"loading": "Loading…",
|
||||||
|
"unknown": "Unknown"
|
||||||
},
|
},
|
||||||
"network": {
|
"network": {
|
||||||
"bypassRules": "Proxy Bypass Rules",
|
"bypassRules": "Proxy Bypass Rules",
|
||||||
|
|||||||
@@ -325,6 +325,12 @@
|
|||||||
"confirmDescription": "¿Detener {{count}} perfiles en ejecución?",
|
"confirmDescription": "¿Detener {{count}} perfiles en ejecución?",
|
||||||
"confirmButton": "Detener {{count}}",
|
"confirmButton": "Detener {{count}}",
|
||||||
"noneToStop": "No hay perfiles seleccionados para detener."
|
"noneToStop": "No hay perfiles seleccionados para detener."
|
||||||
|
},
|
||||||
|
"sort": {
|
||||||
|
"nameAsc": "Nombre (A–Z)",
|
||||||
|
"nameDesc": "Nombre (Z–A)",
|
||||||
|
"newest": "Más recientes primero",
|
||||||
|
"oldest": "Más antiguos primero"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"createProfile": {
|
"createProfile": {
|
||||||
@@ -1149,7 +1155,8 @@
|
|||||||
"proxy": "PROXY",
|
"proxy": "PROXY",
|
||||||
"vpn": "VPN",
|
"vpn": "VPN",
|
||||||
"cookieCount": "Cookies guardadas",
|
"cookieCount": "Cookies guardadas",
|
||||||
"localDataTransfer": "Transferencia de datos local"
|
"localDataTransfer": "Transferencia de datos local",
|
||||||
|
"created": "Creado"
|
||||||
},
|
},
|
||||||
"values": {
|
"values": {
|
||||||
"none": "Ninguno",
|
"none": "Ninguno",
|
||||||
@@ -1158,7 +1165,8 @@
|
|||||||
"yes": "Sí",
|
"yes": "Sí",
|
||||||
"activeNow": "Activo ahora",
|
"activeNow": "Activo ahora",
|
||||||
"direct": "Directa",
|
"direct": "Directa",
|
||||||
"loading": "Cargando…"
|
"loading": "Cargando…",
|
||||||
|
"unknown": "Desconocido"
|
||||||
},
|
},
|
||||||
"network": {
|
"network": {
|
||||||
"bypassRules": "Reglas de Omisión de Proxy",
|
"bypassRules": "Reglas de Omisión de Proxy",
|
||||||
|
|||||||
@@ -325,6 +325,12 @@
|
|||||||
"confirmDescription": "Arrêter {{count}} profils en cours d’exécution ?",
|
"confirmDescription": "Arrêter {{count}} profils en cours d’exécution ?",
|
||||||
"confirmButton": "Arrêter {{count}}",
|
"confirmButton": "Arrêter {{count}}",
|
||||||
"noneToStop": "Aucun profil sélectionné à arrêter."
|
"noneToStop": "Aucun profil sélectionné à arrêter."
|
||||||
|
},
|
||||||
|
"sort": {
|
||||||
|
"nameAsc": "Nom (A–Z)",
|
||||||
|
"nameDesc": "Nom (Z–A)",
|
||||||
|
"newest": "Plus récents d’abord",
|
||||||
|
"oldest": "Plus anciens d’abord"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"createProfile": {
|
"createProfile": {
|
||||||
@@ -1149,7 +1155,8 @@
|
|||||||
"proxy": "PROXY",
|
"proxy": "PROXY",
|
||||||
"vpn": "VPN",
|
"vpn": "VPN",
|
||||||
"cookieCount": "Cookies stockés",
|
"cookieCount": "Cookies stockés",
|
||||||
"localDataTransfer": "Transfert de données local"
|
"localDataTransfer": "Transfert de données local",
|
||||||
|
"created": "Créé le"
|
||||||
},
|
},
|
||||||
"values": {
|
"values": {
|
||||||
"none": "Aucun",
|
"none": "Aucun",
|
||||||
@@ -1158,7 +1165,8 @@
|
|||||||
"yes": "Oui",
|
"yes": "Oui",
|
||||||
"activeNow": "Actif maintenant",
|
"activeNow": "Actif maintenant",
|
||||||
"direct": "Direct",
|
"direct": "Direct",
|
||||||
"loading": "Chargement…"
|
"loading": "Chargement…",
|
||||||
|
"unknown": "Inconnu"
|
||||||
},
|
},
|
||||||
"network": {
|
"network": {
|
||||||
"bypassRules": "Règles de Contournement du Proxy",
|
"bypassRules": "Règles de Contournement du Proxy",
|
||||||
|
|||||||
@@ -325,6 +325,12 @@
|
|||||||
"confirmDescription": "実行中の {{count}} 個のプロファイルを停止しますか?",
|
"confirmDescription": "実行中の {{count}} 個のプロファイルを停止しますか?",
|
||||||
"confirmButton": "{{count}} 個を停止",
|
"confirmButton": "{{count}} 個を停止",
|
||||||
"noneToStop": "停止する選択中のプロファイルがありません。"
|
"noneToStop": "停止する選択中のプロファイルがありません。"
|
||||||
|
},
|
||||||
|
"sort": {
|
||||||
|
"nameAsc": "名前 (A→Z)",
|
||||||
|
"nameDesc": "名前 (Z→A)",
|
||||||
|
"newest": "新しい順",
|
||||||
|
"oldest": "古い順"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"createProfile": {
|
"createProfile": {
|
||||||
@@ -1149,7 +1155,8 @@
|
|||||||
"proxy": "プロキシ",
|
"proxy": "プロキシ",
|
||||||
"vpn": "VPN",
|
"vpn": "VPN",
|
||||||
"cookieCount": "保存された Cookie",
|
"cookieCount": "保存された Cookie",
|
||||||
"localDataTransfer": "ローカルデータ転送量"
|
"localDataTransfer": "ローカルデータ転送量",
|
||||||
|
"created": "作成日"
|
||||||
},
|
},
|
||||||
"values": {
|
"values": {
|
||||||
"none": "なし",
|
"none": "なし",
|
||||||
@@ -1158,7 +1165,8 @@
|
|||||||
"yes": "はい",
|
"yes": "はい",
|
||||||
"activeNow": "現在アクティブ",
|
"activeNow": "現在アクティブ",
|
||||||
"direct": "直接",
|
"direct": "直接",
|
||||||
"loading": "読み込み中…"
|
"loading": "読み込み中…",
|
||||||
|
"unknown": "不明"
|
||||||
},
|
},
|
||||||
"network": {
|
"network": {
|
||||||
"bypassRules": "プロキシバイパスルール",
|
"bypassRules": "プロキシバイパスルール",
|
||||||
|
|||||||
@@ -325,6 +325,12 @@
|
|||||||
"confirmDescription": "실행 중인 {{count}}개의 프로필을 중지할까요?",
|
"confirmDescription": "실행 중인 {{count}}개의 프로필을 중지할까요?",
|
||||||
"confirmButton": "{{count}}개 중지",
|
"confirmButton": "{{count}}개 중지",
|
||||||
"noneToStop": "중지할 선택된 프로필이 없습니다."
|
"noneToStop": "중지할 선택된 프로필이 없습니다."
|
||||||
|
},
|
||||||
|
"sort": {
|
||||||
|
"nameAsc": "이름 (A→Z)",
|
||||||
|
"nameDesc": "이름 (Z→A)",
|
||||||
|
"newest": "최신순",
|
||||||
|
"oldest": "오래된순"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"createProfile": {
|
"createProfile": {
|
||||||
@@ -1149,7 +1155,8 @@
|
|||||||
"proxy": "프록시",
|
"proxy": "프록시",
|
||||||
"vpn": "VPN",
|
"vpn": "VPN",
|
||||||
"cookieCount": "저장된 쿠키",
|
"cookieCount": "저장된 쿠키",
|
||||||
"localDataTransfer": "로컬 데이터 전송"
|
"localDataTransfer": "로컬 데이터 전송",
|
||||||
|
"created": "생성일"
|
||||||
},
|
},
|
||||||
"values": {
|
"values": {
|
||||||
"none": "없음",
|
"none": "없음",
|
||||||
@@ -1158,7 +1165,8 @@
|
|||||||
"yes": "예",
|
"yes": "예",
|
||||||
"activeNow": "지금 활성",
|
"activeNow": "지금 활성",
|
||||||
"direct": "직접",
|
"direct": "직접",
|
||||||
"loading": "불러오는 중…"
|
"loading": "불러오는 중…",
|
||||||
|
"unknown": "알 수 없음"
|
||||||
},
|
},
|
||||||
"network": {
|
"network": {
|
||||||
"bypassRules": "프록시 우회 규칙",
|
"bypassRules": "프록시 우회 규칙",
|
||||||
|
|||||||
@@ -325,6 +325,12 @@
|
|||||||
"confirmDescription": "Parar {{count}} perfis em execução?",
|
"confirmDescription": "Parar {{count}} perfis em execução?",
|
||||||
"confirmButton": "Parar {{count}}",
|
"confirmButton": "Parar {{count}}",
|
||||||
"noneToStop": "Nenhum perfil selecionado para parar."
|
"noneToStop": "Nenhum perfil selecionado para parar."
|
||||||
|
},
|
||||||
|
"sort": {
|
||||||
|
"nameAsc": "Nome (A–Z)",
|
||||||
|
"nameDesc": "Nome (Z–A)",
|
||||||
|
"newest": "Mais recentes primeiro",
|
||||||
|
"oldest": "Mais antigos primeiro"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"createProfile": {
|
"createProfile": {
|
||||||
@@ -1149,7 +1155,8 @@
|
|||||||
"proxy": "PROXY",
|
"proxy": "PROXY",
|
||||||
"vpn": "VPN",
|
"vpn": "VPN",
|
||||||
"cookieCount": "Cookies armazenados",
|
"cookieCount": "Cookies armazenados",
|
||||||
"localDataTransfer": "Transferência de dados local"
|
"localDataTransfer": "Transferência de dados local",
|
||||||
|
"created": "Criado em"
|
||||||
},
|
},
|
||||||
"values": {
|
"values": {
|
||||||
"none": "Nenhum",
|
"none": "Nenhum",
|
||||||
@@ -1158,7 +1165,8 @@
|
|||||||
"yes": "Sim",
|
"yes": "Sim",
|
||||||
"activeNow": "Ativo agora",
|
"activeNow": "Ativo agora",
|
||||||
"direct": "Direto",
|
"direct": "Direto",
|
||||||
"loading": "Carregando…"
|
"loading": "Carregando…",
|
||||||
|
"unknown": "Desconhecido"
|
||||||
},
|
},
|
||||||
"network": {
|
"network": {
|
||||||
"bypassRules": "Regras de Bypass de Proxy",
|
"bypassRules": "Regras de Bypass de Proxy",
|
||||||
|
|||||||
@@ -325,6 +325,12 @@
|
|||||||
"confirmDescription": "Остановить {{count}} запущенных профилей?",
|
"confirmDescription": "Остановить {{count}} запущенных профилей?",
|
||||||
"confirmButton": "Остановить {{count}}",
|
"confirmButton": "Остановить {{count}}",
|
||||||
"noneToStop": "Нет выбранных профилей для остановки."
|
"noneToStop": "Нет выбранных профилей для остановки."
|
||||||
|
},
|
||||||
|
"sort": {
|
||||||
|
"nameAsc": "Имя (А–Я)",
|
||||||
|
"nameDesc": "Имя (Я–А)",
|
||||||
|
"newest": "Сначала новые",
|
||||||
|
"oldest": "Сначала старые"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"createProfile": {
|
"createProfile": {
|
||||||
@@ -1149,7 +1155,8 @@
|
|||||||
"proxy": "ПРОКСИ",
|
"proxy": "ПРОКСИ",
|
||||||
"vpn": "VPN",
|
"vpn": "VPN",
|
||||||
"cookieCount": "Хранится Cookie",
|
"cookieCount": "Хранится Cookie",
|
||||||
"localDataTransfer": "Локальный трафик"
|
"localDataTransfer": "Локальный трафик",
|
||||||
|
"created": "Создан"
|
||||||
},
|
},
|
||||||
"values": {
|
"values": {
|
||||||
"none": "Нет",
|
"none": "Нет",
|
||||||
@@ -1158,7 +1165,8 @@
|
|||||||
"yes": "Да",
|
"yes": "Да",
|
||||||
"activeNow": "Сейчас активен",
|
"activeNow": "Сейчас активен",
|
||||||
"direct": "Без прокси",
|
"direct": "Без прокси",
|
||||||
"loading": "Загрузка…"
|
"loading": "Загрузка…",
|
||||||
|
"unknown": "Неизвестно"
|
||||||
},
|
},
|
||||||
"network": {
|
"network": {
|
||||||
"bypassRules": "Правила обхода прокси",
|
"bypassRules": "Правила обхода прокси",
|
||||||
|
|||||||
@@ -325,6 +325,12 @@
|
|||||||
"confirmDescription": "Dừng {{count}} hồ sơ đang chạy?",
|
"confirmDescription": "Dừng {{count}} hồ sơ đang chạy?",
|
||||||
"confirmButton": "Dừng {{count}}",
|
"confirmButton": "Dừng {{count}}",
|
||||||
"noneToStop": "Không có hồ sơ nào được chọn để dừng."
|
"noneToStop": "Không có hồ sơ nào được chọn để dừng."
|
||||||
|
},
|
||||||
|
"sort": {
|
||||||
|
"nameAsc": "Tên (A–Z)",
|
||||||
|
"nameDesc": "Tên (Z–A)",
|
||||||
|
"newest": "Mới nhất trước",
|
||||||
|
"oldest": "Cũ nhất trước"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"createProfile": {
|
"createProfile": {
|
||||||
@@ -1149,7 +1155,8 @@
|
|||||||
"proxy": "PROXY",
|
"proxy": "PROXY",
|
||||||
"vpn": "VPN",
|
"vpn": "VPN",
|
||||||
"cookieCount": "Cookie đã lưu",
|
"cookieCount": "Cookie đã lưu",
|
||||||
"localDataTransfer": "Truyền dữ liệu cục bộ"
|
"localDataTransfer": "Truyền dữ liệu cục bộ",
|
||||||
|
"created": "Đã tạo"
|
||||||
},
|
},
|
||||||
"values": {
|
"values": {
|
||||||
"none": "Không có",
|
"none": "Không có",
|
||||||
@@ -1158,7 +1165,8 @@
|
|||||||
"yes": "Có",
|
"yes": "Có",
|
||||||
"activeNow": "Đang hoạt động",
|
"activeNow": "Đang hoạt động",
|
||||||
"direct": "Trực tiếp",
|
"direct": "Trực tiếp",
|
||||||
"loading": "Đang tải…"
|
"loading": "Đang tải…",
|
||||||
|
"unknown": "Không rõ"
|
||||||
},
|
},
|
||||||
"network": {
|
"network": {
|
||||||
"bypassRules": "Quy tắc bỏ qua proxy",
|
"bypassRules": "Quy tắc bỏ qua proxy",
|
||||||
|
|||||||
@@ -325,6 +325,12 @@
|
|||||||
"confirmDescription": "停止 {{count}} 个正在运行的配置文件?",
|
"confirmDescription": "停止 {{count}} 个正在运行的配置文件?",
|
||||||
"confirmButton": "停止 {{count}} 个",
|
"confirmButton": "停止 {{count}} 个",
|
||||||
"noneToStop": "没有选中可停止的配置文件。"
|
"noneToStop": "没有选中可停止的配置文件。"
|
||||||
|
},
|
||||||
|
"sort": {
|
||||||
|
"nameAsc": "名称 (A–Z)",
|
||||||
|
"nameDesc": "名称 (Z–A)",
|
||||||
|
"newest": "最新优先",
|
||||||
|
"oldest": "最早优先"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"createProfile": {
|
"createProfile": {
|
||||||
@@ -1149,7 +1155,8 @@
|
|||||||
"proxy": "代理",
|
"proxy": "代理",
|
||||||
"vpn": "VPN",
|
"vpn": "VPN",
|
||||||
"cookieCount": "存储的 Cookie",
|
"cookieCount": "存储的 Cookie",
|
||||||
"localDataTransfer": "本地数据传输"
|
"localDataTransfer": "本地数据传输",
|
||||||
|
"created": "创建时间"
|
||||||
},
|
},
|
||||||
"values": {
|
"values": {
|
||||||
"none": "无",
|
"none": "无",
|
||||||
@@ -1158,7 +1165,8 @@
|
|||||||
"yes": "是",
|
"yes": "是",
|
||||||
"activeNow": "当前活动",
|
"activeNow": "当前活动",
|
||||||
"direct": "直连",
|
"direct": "直连",
|
||||||
"loading": "加载中…"
|
"loading": "加载中…",
|
||||||
|
"unknown": "未知"
|
||||||
},
|
},
|
||||||
"network": {
|
"network": {
|
||||||
"bypassRules": "代理绕过规则",
|
"bypassRules": "代理绕过规则",
|
||||||
|
|||||||
@@ -36,6 +36,9 @@ export interface BrowserProfile {
|
|||||||
proxy_bypass_rules?: string[];
|
proxy_bypass_rules?: string[];
|
||||||
created_by_id?: string;
|
created_by_id?: string;
|
||||||
created_by_email?: string;
|
created_by_email?: string;
|
||||||
|
/** Profile creation timestamp (epoch seconds, UTC). Undefined for legacy
|
||||||
|
* profiles created before this field existed. */
|
||||||
|
created_at?: number;
|
||||||
dns_blocklist?: string;
|
dns_blocklist?: string;
|
||||||
password_protected?: boolean;
|
password_protected?: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user