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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions client_v2/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,10 @@ must not depend on stores or components. Helpers are pure and dependency-free.
'./Foo.module.pcss'` — and reference classes as `s.button`, `s[variant]`.
Compose conditional classes with `clsx`, imported as `cn`:
`import cn from 'clsx'` (e.g. `cn(s.button, s.primary, { [s.active]: on() })`).
- **Hover styles**: Wrap `:hover` styling in `@media (hover: hover)` so touch
devices do not keep a stuck highlight after a tap. Gate `:hover` together
with `:focus` on row links, since a tap also focuses an `<a>` in Chrome;
leave `:focus-visible` outlines ungated for keyboard users.
- **Colors**: Never hardcode color hex values in `.pcss` or inline styles.
Always reference the CSS custom properties defined in
`src/common/styles/vars.css` and `src/common/styles/colors/*.css`
Expand All @@ -346,6 +350,14 @@ must not depend on stores or components. Helpers are pure and dependency-free.
a conditional class via `cn(...)`, not an inline `style`. Exception: a single
computed pixel offset for a dragging/positioning edge case may be acceptable
when no class-based solution exists — justify it in a comment.
- **Comments**: Write comments only where they earn their place, and keep
them useful and compact — one or two lines explaining a non-obvious
*why*: a workaround, a browser quirk, or a cross-file constraint a
reader would otherwise break. Do not narrate what a rule or function
does, restate selector/property names, or spell out measurements,
arithmetic, and design history; that belongs with the value it
constrains or in the PR description. Applies to `.pcss`, `.tsx`, and
tests alike. Delete a comment once the code it describes changes.
- **Reactivity**: Use `createSignal` for local state, `createMemo` for
derived values, `createEffect` for side effects, `onMount` for initial data
fetches, and `onCleanup` for teardown. Do not read signals imperatively
Expand Down
14 changes: 7 additions & 7 deletions client_v2/src/__locales/ar.json
Original file line number Diff line number Diff line change
Expand Up @@ -460,11 +460,11 @@
"ip_address": "عنوان IP",
"known_tracker": "متعقب معروف",
"language": "اللغة",
"last_days": "| آخر 0 يوم | آخر يوم واحد | آخر يومين | آخر %count% أيام | آخر %count% يومًا | آخر %count% يوم",
"last_hours": "| آخر 0 ساعة | آخر ساعة واحدة | آخر ساعتين | آخر %count% ساعات | آخر %count% ساعة | آخر %count% ساعة",
"last_days": "| آخر يوم واحد | آخر يومين | آخر %count% أيام | آخر %count% يومًا | آخر %count% يوم",
"last_hours": "| آخر ساعة واحدة | آخر ساعتين | آخر %count% ساعات | آخر %count% ساعة | آخر %count% ساعة",
"last_updated_label": "آخر تحديث",
"light_theme": "المظهر فاتح",
"list_updated": "| لا توجد قوائم محدثة | تم تحديث قائمة واحدة | تم تحديث قائمتين | تم تحديث %count% قوائم | تم تحديث %count% قائمة | تم تحديث %count% قائمة",
"list_updated": "| تم تحديث قائمة واحدة | تم تحديث قائمتين | تم تحديث %count% قوائم | تم تحديث %count% قائمة | تم تحديث %count% قائمة",
"login": "تسجيل الدخول",
"logout": "تسجيل خروج",
"make_static": "اجعلها ثابتة",
Expand Down Expand Up @@ -513,8 +513,8 @@
"password_requirements_uppercase": "على الأقل 1 حرف كبير",
"path_to_file_placeholder": "طريق الملف: عنوان محلي أو عنوان على الشبكة العنكبوتية",
"pause_for_hour": "إيقاف مؤقت لمدة %count% ساعة",
"pause_for_minutes": "| إيقاف مؤقت لمدة 0 دقيقة | إيقاف مؤقت لمدة دقيقة واحدة | إيقاف مؤقت لمدة دقيقتين | إيقاف مؤقت لمدة %count% دقائق | إيقاف مؤقت لمدة %count% دقيقة | إيقاف مؤقت لمدة %count% دقيقة",
"pause_for_seconds": "| إيقاف مؤقت لمدة 0 ثانية | إيقاف مؤقت لمدة ثانية واحدة | إيقاف مؤقت لمدة ثانيتين | إيقاف مؤقت لمدة %count% ثوانٍ | إيقاف مؤقت لمدة %count% ثانية | إيقاف مؤقت لمدة %count% ثانية",
"pause_for_minutes": "| إيقاف مؤقت لمدة دقيقة واحدة | إيقاف مؤقت لمدة دقيقتين | إيقاف مؤقت لمدة %count% دقائق | إيقاف مؤقت لمدة %count% دقيقة | إيقاف مؤقت لمدة %count% دقيقة",
"pause_for_seconds": "| إيقاف مؤقت لمدة ثانية واحدة | إيقاف مؤقت لمدة ثانيتين | إيقاف مؤقت لمدة %count% ثوانٍ | إيقاف مؤقت لمدة %count% ثانية | إيقاف مؤقت لمدة %count% ثانية",
"pause_until_tomorrow": "إيقاف مؤقت حتى الغد، %time%",
"period_notify": "لتغيير المدة الزمنية المعروضة، قم بتحرير <i>الاحتفاظ بالاحصائيات</i> في <a>إعدادات الوضع العام</a>",
"plain_dns": "عنوان DNS العادي",
Expand Down Expand Up @@ -635,14 +635,14 @@
"settings_confirm_decrease_stats_rotation_interval": "تقليل فترة الاحتفاظ بالإحصائيات؟",
"settings_confirm_decrease_stats_rotation_interval_desc": "سيؤدي هذا إلى حذف جميع الاحصائيات الأقدم من %value%",
"settings_custom": "مخصص",
"settings_days": "| 0 يوم | يوم واحد | يومان | %count% أيام | %count% يومًا | %count% يوم",
"settings_days": "| يوم واحد | يومان | %count% أيام | %count% يومًا | %count% يوم",
"settings_domain_names": "أسماء النطاقات، واحد في كل سطر",
"settings_filter_requests": "تصفية الطلبات",
"settings_filter_requests_desc": "يمكنك استخدام <a>Blocklists</a> و <b>Allowlists</b> و <c>قواعد المستخدم</c> لإعداد قواعد التصفية",
"settings_filtering_and_security": "التصفية والأمان",
"settings_general_short": "الوضع العام",
"settings_global": "عالمي",
"settings_hours": "| 0 ساعة | ساعة واحدة | ساعتان | %count% ساعات | %count% ساعة | %count% ساعة",
"settings_hours": "| ساعة واحدة | ساعتان | %count% ساعات | %count% ساعة | %count% ساعة",
"settings_log_rotation_hours": "تدوير السجل، بالساعات",
"settings_notify_changes_saved": "تم حفظ التغييرات",
"settings_notify_query_log_cleared": "تم مسح سجل الاستعلام",
Expand Down
25 changes: 9 additions & 16 deletions client_v2/src/__locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
"add_allowlist": "Add allowlist",
"add_blocklist": "Add blocklist",
"add_persistent_client": "Add as persistent client",
"add_to_allowlist": "Add to allowlist",
"ads_blocked": "Blocked ads",
"ads_blocked_card": "Blocked ads",
"ads_blocked_tooltip": "The number of DNS queries blocked by adblock filters and hosts blocklist",
Expand All @@ -12,7 +11,6 @@
"all_lists_up_to_date_toast": "All lists are already up-to-date",
"all_rewrites_disabled": "All custom DNS responses will be disabled",
"all_rewrites_enabled": "All custom DNS responses will be enabled",
"allow_service": "Allow service",
"allowed": "Allowed",
"allowlist_add": "Add allowlist",
"allowlist_edit": "Edit allowlist",
Expand Down Expand Up @@ -137,7 +135,7 @@
"dashboard": "Dashboard",
"dashboard_statistics_disabled": "Statistics retention is disabled in General settings",
"delete_btn": "Delete",
"delete_confirm": "Delete lease?",
"delete_confirm": "Remove lease?",
"delete_confirm_desc": "This will free <b>%ip%</b>",
"delete_table_action": "Delete",
"delete_table_action_confirm": "Yes, delete",
Expand Down Expand Up @@ -271,6 +269,7 @@
"dns_override_min_ttl_placeholder": "Time-to-live (TTL)",
"dns_override_min_ttl_title": "Override minimum TTL",
"dns_privacy": "DNS Privacy",
"dns_private_reverse_no_default_resolvers": "AdGuard Home could not determine suitable private reverse DNS resolvers for this system. Specify them to enable this setting.",
"dns_private_reverse_resolve_clients_desc": "Resolves client IP addresses to their hostnames using PTR queries. Uses private DNS servers for local clients and upstream servers for clients with public IP addresses",
"dns_private_reverse_resolve_clients_title": "Reverse lookup of client IP addresses",
"dns_private_reverse_resolvers": "Private reverse DNS resolvers",
Expand All @@ -280,9 +279,8 @@
"dns_private_reverse_servers_desc_2": "If this field is empty, your operating system's default DNS resolvers are used, except for AdGuard Home IP addresses. By default, AdGuard Home uses the following reverse DNS resolvers: %value_1% and %value_2%",
"dns_private_reverse_servers_label": "Private reverse DNS servers, one per line",
"dns_private_reverse_servers_placeholder": "IP addresses, sdns://, quic://, https://, h3://, or tls://",
"dns_private_reverse_servers_required": "Specify at least one private reverse DNS server or turn off private reverse DNS resolvers",
"dns_private_reverse_servers_resolvers": "Default DNS resolvers: %value_1% and %value_2%",
"dns_private_reverse_servers_title": "Private reverse DNS servers",
"dns_protocols_title": "DNS protocols",
"dns_queries": "DNS queries",
"dns_queries_tooltip": "The number of DNS queries processed for the last 24 hours",
"dns_query": "DNS queries",
Expand All @@ -299,7 +297,6 @@
"dns_rate_limit_title": "Rate limit",
"dns_rate_limit_value": "%value% req/s",
"dns_rewrite_exists": "This DNS rewrite already exists",
"dns_rewrite_removed": "DNS rewrite removed",
"dns_rewrite_same": "You can’t rewrite to the same domain or wildcard",
"dns_rewrites": "DNS rewrites",
"dns_rewrites_desc": "Lists your custom DNS responses for specific domain names",
Expand Down Expand Up @@ -372,8 +369,6 @@
"encryption_server_tooltip": "The domain name of your server. It must match a DNS name in the certificate. If set, AdGuard Home will detect ClientIDs, respond to DDR queries, and perform additional connection validations",
"encryption_subject": "Subject: %value%",
"encryption_title": "Encryption",
"encryption_unable_read_cert": "Unable to read certificate file",
"encryption_unable_read_key": "Unable to read private key file",
"error": "Error",
"filter_added_successfully": "Added blocklist: <strong>%value%</strong>",
"filter_added_successfully_allowlist": "Added allowlist: <strong>%value%</strong>",
Expand Down Expand Up @@ -404,7 +399,6 @@
"form_error_answer_format": "Invalid result format",
"form_error_client_id_format": "ClientID must contain only numbers, lowercase letters, and hyphens",
"form_error_domain_format": "Invalid domain format",
"form_error_equal": "Must not be equal",
"form_error_format": "Invalid format",
"form_error_format_line": "Invalid format on line %line%",
"form_error_format_lines": "Invalid format on lines %lines%",
Expand Down Expand Up @@ -447,15 +441,14 @@
"inactivity_schedule_edit": "Edit schedule",
"inactivity_schedule_no_schedules": "No schedule",
"inactivity_schedule_replace_desc": "This schedule will replace any existing schedules for the same day of the week. Each day can have only one inactivity period",
"inactivity_schedule_time_from": "Time from",
"inactivity_schedule_time_to": "Time to",
"inactivity_schedule_time_from": "Time from (HH:mm)",
"inactivity_schedule_time_to": "Time to (HH:mm)",
"inactivity_schedule_timezone": "Timezone",
"install_auth_confirm": "Confirm password",
"install_auth_password": "Password",
"install_auth_password_enter": "Enter password",
"install_auth_username": "Username",
"install_auth_username_enter": "Enter username",
"install_saved": "Saved successfully",
"install_settings_all_interfaces": "All interfaces",
"install_settings_port": "Port",
"install_static_configure": "AdGuard Home has detected that the dynamic IP address %ip% is used. Do you want it to be set as your static address?",
Expand Down Expand Up @@ -519,7 +512,6 @@
"password_requirements_match": "Passwords match",
"password_requirements_special": "English letters, numbers, and special characters",
"password_requirements_uppercase": "At least 1 uppercase letter",
"path_to_file_placeholder": "Path to file: local or Internet address",
"pause_for_hour": "Pause for %count% hour",
"pause_for_minutes": "| Pause for %count% minute | Pause for %count% minutes",
"pause_for_seconds": "| Pause for %count% second | Pause for %count% seconds",
Expand Down Expand Up @@ -582,7 +574,7 @@
"remove": "Remove",
"remove_tag": "Remove %value%",
"remove_tls_certificate": "Remove TLS certificate?",
"remove_tls_certificate_desc": "Clients using encrypted DNS won’t be able to connect until a new TLS certificate is added",
"remove_tls_certificate_desc": "Removing the certificate will disable HTTPS and reset TLS settings. If you are connected over HTTPS, your session will end and you will be redirected to http://%host%:%port%",
"report_an_issue": "Report a problem",
"request_table_header": "Request",
"requests_table_header": "Requests",
Expand Down Expand Up @@ -621,6 +613,7 @@
"safe_search_used_tooltip": "The number of DNS queries to search engines for which Safe Search was enforced",
"saturday": "Saturday",
"save": "Save",
"save_anyway": "Save anyway",
"save_btn": "Save",
"search_placeholder": "Search",
"select_language": "Select language",
Expand Down Expand Up @@ -658,7 +651,7 @@
"settings_notify_query_log_cleared": "Query log cleared",
"settings_notify_statistics_cleared": "Statistics cleared",
"settings_parental_control": "Parental control",
"settings_parental_control_desc": "Block websites with content inappropriate for children",
"settings_parental_control_desc": "Block websites with inappropriate content, such as pornography, violence, gore, and other disturbing material, using AdGuard’s continuously updated family filter database",
"settings_rotation_placeholder": "Use numbers",
"settings_rule_syntax": "Rule syntax",
"settings_safe_search": "Safe search",
Expand Down Expand Up @@ -793,7 +786,7 @@
"tls_setup_config_title": "Enable encrypted DNS",
"tls_setup_enable_anyway": "Enable anyway",
"tls_setup_error_cert_incomplete": "Certificate is incomplete. Paste the full text, including the BEGIN and END lines",
"tls_setup_error_duplicate_port": "Port %port% is used by multiple DNS protocols. It must be unique",
"tls_setup_error_duplicate_port": "Port %port% is used by more than one protocol. Choose a different port for each encrypted DNS protocol",
"tls_setup_error_ed25519_key": "This key type is not supported by browsers. Use an RSA or ECDSA key instead",
"tls_setup_error_enable_failed": "Unable to enable encrypted DNS. Check the settings and try again",
"tls_setup_error_key_incomplete": "Private key is incomplete. Paste the full text, including the BEGIN and END lines",
Expand Down
14 changes: 7 additions & 7 deletions client_v2/src/__locales/hr.json
Original file line number Diff line number Diff line change
Expand Up @@ -460,11 +460,11 @@
"ip_address": "IP adresa",
"known_tracker": "Poznati pratitelj",
"language": "Jezik",
"last_days": "| Zadnji %count% dan | Zadnjih %count% dana",
"last_hours": "| Zadnjih %count% sat | Zadnjih %count% sati",
"last_days": "| Zadnji %count% dan | Zadnja %count% dana | Zadnjih %count% dana",
"last_hours": "| Zadnjih %count% sat | Zadnja %count% sata | Zadnjih %count% sati",
"last_updated_label": "Zadnja izmjena",
"light_theme": "Svijetla tema",
"list_updated": "| %count% popis ažuriran | %count% popisi ažurirani",
"list_updated": "| %count% popis ažuriran | %count% popisa ažurirana | %count% popisi ažurirani",
"login": "PRIJAVA",
"logout": "Odjava",
"make_static": "Učini statičnim",
Expand Down Expand Up @@ -513,8 +513,8 @@
"password_requirements_uppercase": "Najmanje jedno veliko slovo",
"path_to_file_placeholder": "Put do datoteke: lokalna ili internetska adresa",
"pause_for_hour": "Pauza na %count% sat",
"pause_for_minutes": "| Pauza od %count% minute | Pauza od %count% minuta",
"pause_for_seconds": "| Pauza %count% sekunde | Pauza na %count% sekunde",
"pause_for_minutes": "| Pauza od %count% minute | Pauza od %count% minute | Pauza od %count% minuta",
"pause_for_seconds": "| Pauza %count% sekunde | Pauza na %count% sekunde | Pauza na %count% sekunde",
"pause_until_tomorrow": "Pauza do sutra, %time%",
"period_notify": "Za promjenu prikazanih razdoblja uredite <i>Zadržavanje statistike</i> u <a>Općim postavkama</a>",
"plain_dns": "Obični DNS",
Expand Down Expand Up @@ -635,14 +635,14 @@
"settings_confirm_decrease_stats_rotation_interval": "Smanjiti interval zadržavanja statistika?",
"settings_confirm_decrease_stats_rotation_interval_desc": "To će izbrisati sve statistike starije od %value%",
"settings_custom": "Prilagođeno",
"settings_days": "| %count% dan | %count% dana",
"settings_days": "| %count% dan | %count% dana | %count% dana",
"settings_domain_names": "Domene, po jedna po liniji",
"settings_filter_requests": "Zahtjevi za filtriranje",
"settings_filter_requests_desc": "Možete koristiti <a>Blocklists</a>, <b>Allowlists</b> i <c>Korisnička pravila</c> za postavljanje pravila filtriranja",
"settings_filtering_and_security": "Filtriranje i sigurnost",
"settings_general_short": "Općenito",
"settings_global": "Globalno",
"settings_hours": "| %count% sat | %count% sati",
"settings_hours": "| %count% sat | %count% sata | %count% sati",
"settings_log_rotation_hours": "Rotacija trupaca, u satima",
"settings_notify_changes_saved": "Promjene su spremljene",
"settings_notify_query_log_cleared": "Dnevnik upita očišćen",
Expand Down
32 changes: 32 additions & 0 deletions client_v2/src/__tests__/common/controls/Textarea.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,35 @@ describe('Textarea — comment highlight', () => {
expect(overlay?.parentElement).toBe(textarea?.parentElement);
});
});

describe('Textarea — onCard', () => {
it('applies the onCard class to the field when set', () => {
const { container } = render(() => <Textarea value="text" onCard />);

expect(container.querySelector('textarea')?.className).toContain('onCard');
});

it('does not apply the onCard class by default', () => {
const { container } = render(() => <Textarea value="text" />);

expect(container.querySelector('textarea')?.className).not.toContain('onCard');
});

it('applies the onCard class to the scroll container in highlighting mode', () => {
const { container } = render(() => (
<Textarea
value="# comment"
highlightComments
commentPrefixes={COMMENT_LINE_TOKENS}
onCard
/>
));

// The visible surface in highlighting mode is the scroll container,
// not the transparent textarea layered on top of it.
const textarea = container.querySelector('textarea');
const scrollArea = textarea?.closest('[class*="scrollArea"]');
expect(scrollArea?.className).toContain('onCard');
expect(textarea?.className).not.toContain('onCard');
});
});
Loading
Loading