Merge pull request #321 from router-for-me/feat/config-editor-simple-full-mode

Feat/config editor simple full mode
This commit is contained in:
Supra4E8C
2026-06-19 03:34:32 +08:00
committed by GitHub
8 changed files with 2065 additions and 664 deletions
@@ -140,6 +140,9 @@
.overviewHeader {
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
gap: 10px;
min-width: 0;
@include mobile {
@@ -172,6 +175,240 @@
background: var(--warning-bg);
}
.modeSwitch {
display: inline-flex;
gap: 4px;
padding: 3px;
border: 1px solid var(--border-color);
border-radius: 8px;
background: var(--bg-secondary);
}
.modeButton {
@include button-reset;
display: inline-flex;
align-items: center;
gap: 6px;
min-height: 30px;
padding: 0 12px;
border-radius: 6px;
color: var(--text-secondary);
font-size: 13px;
font-weight: 700;
line-height: 1.2;
transition:
background-color 0.15s ease,
color 0.15s ease;
&:hover {
color: var(--text-primary);
}
}
.modeButtonActive {
background: var(--bg-primary);
color: var(--text-primary);
box-shadow: 0 1px 2px color-mix(in srgb, var(--text-primary) 10%, transparent);
}
// ── Global field search (P1) ────────────────────────────────────────────────
.searchBox {
position: relative;
min-width: 0;
}
.searchControl {
padding-right: 38px !important;
}
.searchIcon {
display: inline-flex;
align-items: center;
color: var(--text-tertiary);
pointer-events: none;
}
.searchResults {
position: absolute;
z-index: 20;
top: calc(100% + 6px);
left: 0;
right: 0;
display: flex;
flex-direction: column;
gap: 2px;
max-height: 320px;
overflow-y: auto;
padding: 6px;
border: 1px solid var(--border-color);
border-radius: 8px;
background: var(--bg-primary);
box-shadow: 0 14px 32px color-mix(in srgb, var(--text-primary) 16%, transparent);
}
.searchResultItem {
@include button-reset;
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
width: 100%;
padding: 9px 10px;
border-radius: 6px;
text-align: left;
transition: background-color 0.12s ease;
&:hover {
background: color-mix(in srgb, var(--text-primary) 6%, transparent);
}
}
// Keyboard/pointer "active" option — the aria-activedescendant target.
.searchResultItemActive {
background: color-mix(in srgb, var(--text-primary) 6%, transparent);
}
.searchResultLabel {
display: inline-flex;
align-items: baseline;
gap: 7px;
min-width: 0;
color: var(--text-primary);
font-size: 13px;
font-weight: 650;
line-height: 1.3;
}
.searchResultQualifier {
color: var(--text-tertiary);
font-size: 11px;
font-weight: 600;
white-space: nowrap;
}
.searchResultSection {
flex: 0 0 auto;
color: var(--text-secondary);
font-size: 11px;
font-weight: 650;
white-space: nowrap;
}
.searchEmpty {
padding: 12px 10px;
color: var(--text-secondary);
font-size: 13px;
text-align: center;
}
// Stateless wrapper around each searchable field; the search jump scrolls to and
// pulse-highlights it.
.fieldAnchor {
display: block;
min-width: 0;
scroll-margin-top: calc(var(--header-height, 64px) + 16px);
}
.fieldHighlightActive {
border-radius: 10px;
animation: cfgFieldHighlight 1.8s ease-out;
}
@keyframes cfgFieldHighlight {
0% {
box-shadow: 0 0 0 3px color-mix(in srgb, var(--text-primary) 55%, transparent);
}
55% {
box-shadow: 0 0 0 3px color-mix(in srgb, var(--text-primary) 32%, transparent);
}
100% {
box-shadow: 0 0 0 3px transparent;
}
}
.simpleView {
display: flex;
flex-direction: column;
gap: 16px;
width: 100%;
max-width: 760px;
min-width: 0;
}
.simpleForm {
display: flex;
flex-direction: column;
gap: 14px;
min-width: 0;
}
// Wrap bare inputs so they read as cards consistent with toggleRow / API key blocks.
.simpleField {
padding: 14px;
border: 1px solid var(--border-color);
border-radius: 8px;
background: transparent;
}
.simpleBanner {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
flex-wrap: wrap;
padding: 12px 14px;
border: 1px solid var(--warning-border);
border-radius: 8px;
background: var(--warning-bg);
color: var(--warning-text);
font-size: 13px;
font-weight: 600;
line-height: 1.5;
}
.simpleBannerAction {
@include button-reset;
flex: 0 0 auto;
padding: 6px 12px;
border: 1px solid var(--warning-border);
border-radius: 6px;
color: var(--warning-text);
font-size: 12px;
font-weight: 700;
transition: background-color 0.15s ease;
&:hover {
background: color-mix(in srgb, var(--warning-text) 10%, transparent);
}
}
.simpleMore {
@include button-reset;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
width: 100%;
min-height: 46px;
padding: 12px 16px;
border: 1px dashed var(--border-color);
border-radius: 8px;
color: var(--text-secondary);
font-size: 13px;
font-weight: 700;
text-align: center;
transition:
background-color 0.15s ease,
border-color 0.15s ease,
color 0.15s ease;
&:hover {
border-color: var(--text-primary);
color: var(--text-primary);
background: color-mix(in srgb, var(--text-primary) 4%, transparent);
}
}
.workspace {
display: flex;
flex-direction: column;
File diff suppressed because it is too large Load Diff
+499
View File
@@ -0,0 +1,499 @@
// Search index for the visual config editor's global "jump to field" search.
//
// IMPORTANT: this index is maintained by hand and is NOT what drives field
// rendering — it only powers search. When you add, remove, or move a field in
// VisualConfigEditor.tsx, update the matching entry here (and wrap the field's
// JSX in <FieldAnchor fieldId="..."> using the same `fieldId`).
export type VisualSectionId =
| 'connectivity'
| 'network'
| 'logging'
| 'quota'
| 'streaming'
| 'advanced'
| 'payload';
export interface ConfigFieldSearchEntry {
/** Stable anchor id; matches FieldAnchor's `fieldId` and the rendered DOM id. */
fieldId: string;
sectionId: VisualSectionId;
/** i18n key resolved with t() at search time so matching follows the active language. */
labelKey: string;
/** Optional secondary i18n key shown next to the label to disambiguate duplicates
* (e.g. Claude vs Codex "User-Agent"). Also searchable. */
qualifierKey?: string;
/** Optional hint i18n key — searchable but not shown in results. */
hintKey?: string;
/** Backend YAML key aliases, e.g. ['proxy-url']. Static strings (language-agnostic). */
yamlKeys?: string[];
/** Extra synonyms to match against (language-agnostic, lowercase). */
keywords?: string[];
}
/** DOM id for a field anchor — kept in one place so the index and the anchors agree. */
export const configFieldDomId = (fieldId: string) => `cfg-field-${fieldId}`;
type Translate = (key: string) => string;
// Compact helper: every label/hint key lives under config_management.visual.
const L = (key: string) => `config_management.visual.${key}`;
export const CONFIG_FIELD_SEARCH_INDEX: ConfigFieldSearchEntry[] = [
// ── connectivity ──────────────────────────────────────────────────────────
{
fieldId: 'host',
sectionId: 'connectivity',
labelKey: L('sections.server.host'),
yamlKeys: ['host'],
},
{
fieldId: 'port',
sectionId: 'connectivity',
labelKey: L('sections.server.port'),
yamlKeys: ['port'],
},
{
fieldId: 'authDir',
sectionId: 'connectivity',
labelKey: L('sections.auth.auth_dir'),
hintKey: L('sections.auth.auth_dir_hint'),
yamlKeys: ['auth-dir'],
},
{
fieldId: 'apiKeys',
sectionId: 'connectivity',
labelKey: L('api_keys.label'),
yamlKeys: ['api-keys'],
keywords: ['api key', 'apikey', 'token'],
},
{
fieldId: 'tlsEnable',
sectionId: 'connectivity',
labelKey: L('sections.tls.enable'),
hintKey: L('sections.tls.enable_desc'),
yamlKeys: ['tls'],
keywords: ['tls', 'ssl', 'https'],
},
{
fieldId: 'tlsCert',
sectionId: 'connectivity',
labelKey: L('sections.tls.cert'),
yamlKeys: ['tls', 'cert'],
keywords: ['tls', 'ssl', 'certificate'],
},
{
fieldId: 'tlsKey',
sectionId: 'connectivity',
labelKey: L('sections.tls.key'),
yamlKeys: ['tls', 'key'],
keywords: ['tls', 'ssl', 'private key'],
},
{
fieldId: 'rmAllowRemote',
sectionId: 'connectivity',
labelKey: L('sections.remote.allow_remote'),
hintKey: L('sections.remote.allow_remote_desc'),
yamlKeys: ['remote-management', 'allow-remote'],
},
{
fieldId: 'rmDisableControlPanel',
sectionId: 'connectivity',
labelKey: L('sections.remote.disable_panel'),
yamlKeys: ['remote-management', 'disable-control-panel'],
},
{
fieldId: 'rmDisableAutoUpdatePanel',
sectionId: 'connectivity',
labelKey: L('sections.remote.disable_auto_update_panel'),
yamlKeys: ['remote-management', 'disable-auto-update-panel'],
},
{
fieldId: 'rmSecretKey',
sectionId: 'connectivity',
labelKey: L('sections.remote.secret_key'),
yamlKeys: ['remote-management', 'secret-key'],
},
{
fieldId: 'rmPanelRepo',
sectionId: 'connectivity',
labelKey: L('sections.remote.panel_repo'),
yamlKeys: ['remote-management', 'panel-github-repository'],
},
// ── network ───────────────────────────────────────────────────────────────
{
fieldId: 'proxyUrl',
sectionId: 'network',
labelKey: L('sections.network.proxy_url'),
yamlKeys: ['proxy-url'],
},
{
fieldId: 'requestRetry',
sectionId: 'network',
labelKey: L('sections.network.request_retry'),
yamlKeys: ['request-retry'],
},
{
fieldId: 'maxRetryCredentials',
sectionId: 'network',
labelKey: L('sections.network.max_retry_credentials'),
hintKey: L('sections.network.max_retry_credentials_hint'),
yamlKeys: ['max-retry-credentials'],
},
{
fieldId: 'maxRetryInterval',
sectionId: 'network',
labelKey: L('sections.network.max_retry_interval'),
yamlKeys: ['max-retry-interval'],
},
{
fieldId: 'authAutoRefreshWorkers',
sectionId: 'network',
labelKey: L('sections.network.auth_auto_refresh_workers'),
hintKey: L('sections.network.auth_auto_refresh_workers_hint'),
yamlKeys: ['auth-auto-refresh-workers'],
},
{
fieldId: 'routingStrategy',
sectionId: 'network',
labelKey: L('sections.network.routing_strategy'),
hintKey: L('sections.network.routing_strategy_hint'),
yamlKeys: ['routing', 'strategy'],
keywords: ['round-robin', 'fill-first'],
},
{
fieldId: 'disableImageGeneration',
sectionId: 'network',
labelKey: L('sections.network.disable_image_generation'),
hintKey: L('sections.network.disable_image_generation_hint'),
yamlKeys: ['disable-image-generation'],
},
{
fieldId: 'gptImage2BaseModel',
sectionId: 'network',
labelKey: L('sections.network.gpt_image_2_base_model'),
hintKey: L('sections.network.gpt_image_2_base_model_hint'),
yamlKeys: ['gpt-image-2-base-model'],
},
{
fieldId: 'routingSessionAffinityTTL',
sectionId: 'network',
labelKey: L('sections.network.session_affinity_ttl'),
yamlKeys: ['routing', 'session-affinity-ttl'],
},
{
fieldId: 'forceModelPrefix',
sectionId: 'network',
labelKey: L('sections.network.force_model_prefix'),
hintKey: L('sections.network.force_model_prefix_desc'),
yamlKeys: ['force-model-prefix'],
},
{
fieldId: 'passthroughHeaders',
sectionId: 'network',
labelKey: L('sections.network.passthrough_headers'),
hintKey: L('sections.network.passthrough_headers_desc'),
yamlKeys: ['passthrough-headers'],
},
{
fieldId: 'disableCooling',
sectionId: 'network',
labelKey: L('sections.network.disable_cooling'),
hintKey: L('sections.network.disable_cooling_desc'),
yamlKeys: ['disable-cooling'],
},
{
fieldId: 'routingSessionAffinity',
sectionId: 'network',
labelKey: L('sections.network.session_affinity'),
yamlKeys: ['routing', 'session-affinity'],
},
{
fieldId: 'wsAuth',
sectionId: 'network',
labelKey: L('sections.network.ws_auth'),
hintKey: L('sections.network.ws_auth_desc'),
yamlKeys: ['ws-auth'],
keywords: ['websocket'],
},
{
fieldId: 'enableGeminiCliEndpoint',
sectionId: 'network',
labelKey: L('sections.network.enable_gemini_cli_endpoint'),
hintKey: L('sections.network.enable_gemini_cli_endpoint_desc'),
yamlKeys: ['enable-gemini-cli-endpoint'],
},
// ── logging ───────────────────────────────────────────────────────────────
{
fieldId: 'debug',
sectionId: 'logging',
labelKey: L('sections.system.debug'),
hintKey: L('sections.system.debug_desc'),
yamlKeys: ['debug'],
},
{
fieldId: 'commercialMode',
sectionId: 'logging',
labelKey: L('sections.system.commercial_mode'),
hintKey: L('sections.system.commercial_mode_desc'),
yamlKeys: ['commercial-mode'],
},
{
fieldId: 'loggingToFile',
sectionId: 'logging',
labelKey: L('sections.system.logging_to_file'),
hintKey: L('sections.system.logging_to_file_desc'),
yamlKeys: ['logging-to-file'],
},
{
fieldId: 'logsMaxTotalSizeMb',
sectionId: 'logging',
labelKey: L('sections.system.logs_max_size'),
yamlKeys: ['logs-max-total-size-mb'],
},
{
fieldId: 'errorLogsMaxFiles',
sectionId: 'logging',
labelKey: L('sections.system.error_logs_max_files'),
yamlKeys: ['error-logs-max-files'],
},
{
fieldId: 'redisUsageQueueRetentionSeconds',
sectionId: 'logging',
labelKey: L('sections.system.redis_usage_retention'),
hintKey: L('sections.system.redis_usage_retention_hint'),
yamlKeys: ['redis-usage-queue-retention-seconds'],
},
{
fieldId: 'usageStatisticsEnabled',
sectionId: 'logging',
labelKey: L('sections.system.usage_statistics_enabled'),
hintKey: L('sections.system.usage_statistics_enabled_desc'),
yamlKeys: ['usage-statistics-enabled'],
},
// ── quota ─────────────────────────────────────────────────────────────────
{
fieldId: 'quotaSwitchProject',
sectionId: 'quota',
labelKey: L('sections.quota.switch_project'),
hintKey: L('sections.quota.switch_project_desc'),
yamlKeys: ['quota-exceeded', 'switch-project'],
},
{
fieldId: 'quotaSwitchPreviewModel',
sectionId: 'quota',
labelKey: L('sections.quota.switch_preview_model'),
hintKey: L('sections.quota.switch_preview_model_desc'),
yamlKeys: ['quota-exceeded', 'switch-preview-model'],
},
{
fieldId: 'quotaAntigravityCredits',
sectionId: 'quota',
labelKey: L('sections.quota.antigravity_credits'),
yamlKeys: ['quota-exceeded', 'antigravity-credits'],
},
// ── streaming ─────────────────────────────────────────────────────────────
{
fieldId: 'streamingKeepaliveSeconds',
sectionId: 'streaming',
labelKey: L('sections.streaming.keepalive_seconds'),
hintKey: L('sections.streaming.keepalive_hint'),
yamlKeys: ['streaming', 'keepalive-seconds'],
},
{
fieldId: 'streamingBootstrapRetries',
sectionId: 'streaming',
labelKey: L('sections.streaming.bootstrap_retries'),
hintKey: L('sections.streaming.bootstrap_hint'),
yamlKeys: ['streaming', 'bootstrap-retries'],
},
{
fieldId: 'streamingNonstreamKeepalive',
sectionId: 'streaming',
labelKey: L('sections.streaming.nonstream_keepalive'),
hintKey: L('sections.streaming.nonstream_keepalive_hint'),
yamlKeys: ['streaming', 'nonstream-keepalive-interval'],
},
// ── advanced ──────────────────────────────────────────────────────────────
{
fieldId: 'pluginsEnabled',
sectionId: 'advanced',
labelKey: L('sections.system.plugins_enabled'),
hintKey: L('sections.system.plugins_enabled_desc'),
yamlKeys: ['plugins'],
},
{
fieldId: 'pluginStoreSources',
sectionId: 'advanced',
labelKey: L('sections.system.plugin_store_sources'),
hintKey: L('sections.system.plugin_store_sources_hint'),
yamlKeys: ['plugins', 'store-sources'],
},
{
fieldId: 'antigravitySignatureCacheEnabled',
sectionId: 'advanced',
labelKey: L('sections.system.antigravity_signature_cache'),
hintKey: L('sections.system.antigravity_signature_cache_desc'),
yamlKeys: ['antigravity-signature-cache-enabled'],
},
{
fieldId: 'antigravitySignatureBypassStrict',
sectionId: 'advanced',
labelKey: L('sections.system.antigravity_signature_strict'),
hintKey: L('sections.system.antigravity_signature_strict_desc'),
yamlKeys: ['antigravity-signature-bypass-strict'],
},
// Claude header defaults — qualifierKey disambiguates the shared "User-Agent" label.
{
fieldId: 'claudeHeaderUserAgent',
sectionId: 'advanced',
labelKey: L('sections.headers.user_agent'),
qualifierKey: L('sections.headers.claude_title'),
yamlKeys: ['claude-header-defaults', 'user-agent'],
keywords: ['claude'],
},
{
fieldId: 'claudeHeaderPackageVersion',
sectionId: 'advanced',
labelKey: L('sections.headers.package_version'),
qualifierKey: L('sections.headers.claude_title'),
yamlKeys: ['claude-header-defaults', 'package-version'],
keywords: ['claude'],
},
{
fieldId: 'claudeHeaderRuntimeVersion',
sectionId: 'advanced',
labelKey: L('sections.headers.runtime_version'),
qualifierKey: L('sections.headers.claude_title'),
yamlKeys: ['claude-header-defaults', 'runtime-version'],
keywords: ['claude'],
},
{
fieldId: 'claudeHeaderOs',
sectionId: 'advanced',
labelKey: L('sections.headers.os'),
qualifierKey: L('sections.headers.claude_title'),
yamlKeys: ['claude-header-defaults', 'os'],
keywords: ['claude'],
},
{
fieldId: 'claudeHeaderArch',
sectionId: 'advanced',
labelKey: L('sections.headers.arch'),
qualifierKey: L('sections.headers.claude_title'),
yamlKeys: ['claude-header-defaults', 'arch'],
keywords: ['claude'],
},
{
fieldId: 'claudeHeaderTimeout',
sectionId: 'advanced',
labelKey: L('sections.headers.timeout'),
qualifierKey: L('sections.headers.claude_title'),
yamlKeys: ['claude-header-defaults', 'timeout'],
keywords: ['claude'],
},
{
fieldId: 'claudeHeaderStabilizeDeviceProfile',
sectionId: 'advanced',
labelKey: L('sections.headers.stabilize_device'),
qualifierKey: L('sections.headers.claude_title'),
hintKey: L('sections.headers.stabilize_device_desc'),
yamlKeys: ['claude-header-defaults', 'stabilize-device-profile'],
keywords: ['claude'],
},
// Codex header defaults.
{
fieldId: 'codexHeaderUserAgent',
sectionId: 'advanced',
labelKey: L('sections.headers.user_agent'),
qualifierKey: L('sections.headers.codex_title'),
yamlKeys: ['codex-header-defaults', 'user-agent'],
keywords: ['codex'],
},
{
fieldId: 'codexHeaderBetaFeatures',
sectionId: 'advanced',
labelKey: L('sections.headers.beta_features'),
qualifierKey: L('sections.headers.codex_title'),
yamlKeys: ['codex-header-defaults', 'beta-features'],
keywords: ['codex'],
},
{
fieldId: 'codexIdentityConfuse',
sectionId: 'advanced',
labelKey: L('sections.headers.codex_identity_confuse'),
qualifierKey: L('sections.headers.codex_title'),
hintKey: L('sections.headers.codex_identity_confuse_desc'),
yamlKeys: ['codex-header-defaults', 'identity-confuse'],
keywords: ['codex'],
},
// ── payload (coarse: one entry per rule group) ──────────────────────────────
{
fieldId: 'payloadDefaultRules',
sectionId: 'payload',
labelKey: L('sections.payload.default_rules'),
hintKey: L('sections.payload.default_rules_desc'),
keywords: ['payload', 'rule'],
},
{
fieldId: 'payloadDefaultRawRules',
sectionId: 'payload',
labelKey: L('sections.payload.default_raw_rules'),
hintKey: L('sections.payload.default_raw_rules_desc'),
keywords: ['payload', 'rule', 'json'],
},
{
fieldId: 'payloadOverrideRules',
sectionId: 'payload',
labelKey: L('sections.payload.override_rules'),
hintKey: L('sections.payload.override_rules_desc'),
keywords: ['payload', 'rule'],
},
{
fieldId: 'payloadOverrideRawRules',
sectionId: 'payload',
labelKey: L('sections.payload.override_raw_rules'),
hintKey: L('sections.payload.override_raw_rules_desc'),
keywords: ['payload', 'rule', 'json'],
},
{
fieldId: 'payloadFilterRules',
sectionId: 'payload',
labelKey: L('sections.payload.filter_rules'),
hintKey: L('sections.payload.filter_rules_desc'),
keywords: ['payload', 'rule', 'filter'],
},
];
const MAX_RESULTS = 8;
/**
* Lowercase substring search over label + qualifier + hint + YAML keys + keywords.
* Returns the best ~8 matches, label/qualifier hits ranked above alias-only hits.
*/
export function searchConfigFields(query: string, t: Translate): ConfigFieldSearchEntry[] {
const q = query.trim().toLowerCase();
if (!q) return [];
const scored: { entry: ConfigFieldSearchEntry; score: number }[] = [];
for (const entry of CONFIG_FIELD_SEARCH_INDEX) {
const label = t(entry.labelKey).toLowerCase();
const qualifier = entry.qualifierKey ? t(entry.qualifierKey).toLowerCase() : '';
const hint = entry.hintKey ? t(entry.hintKey).toLowerCase() : '';
const yaml = (entry.yamlKeys ?? []).join(' ').toLowerCase();
const keywords = (entry.keywords ?? []).join(' ').toLowerCase();
let score = Number.POSITIVE_INFINITY;
if (label.startsWith(q)) score = 0;
else if (label.includes(q)) score = 1;
else if (qualifier.includes(q) || keywords.includes(q)) score = 2;
else if (yaml.includes(q)) score = 3;
else if (hint.includes(q)) score = 4;
if (Number.isFinite(score)) scored.push({ entry, score });
}
scored.sort((a, b) => a.score - b.score);
return scored.slice(0, MAX_RESULTS).map((item) => item.entry);
}
+20 -4
View File
@@ -1,4 +1,9 @@
import type { HTMLAttributes, PropsWithChildren, ReactNode } from 'react';
import {
useState,
type HTMLAttributes,
type PropsWithChildren,
type ReactNode,
} from 'react';
import { IconChevronDown } from '../icons';
import styles from './Collapsible.module.scss';
@@ -22,12 +27,23 @@ export function Collapsible({
className,
...rest
}: PropsWithChildren<CollapsibleProps>) {
const detailsProps =
open !== undefined ? { open } : { defaultOpen };
const [uncontrolledOpen, setUncontrolledOpen] = useState(defaultOpen);
const resolvedOpen = open ?? uncontrolledOpen;
const cls = [styles.root, className].filter(Boolean).join(' ');
const contentCls = flush ? styles.contentFlush : styles.content;
return (
<details className={cls} onToggle={onToggle} {...detailsProps} {...rest}>
<details
className={cls}
open={resolvedOpen}
onToggle={(event) => {
if (open === undefined) {
setUncontrolledOpen(event.currentTarget.open);
}
onToggle?.(event);
}}
{...rest}
>
<summary className={styles.summary}>
<span className={styles.summaryLabel}>
<span>{label}</span>
+26
View File
@@ -909,7 +909,33 @@
"visual": {
"notice": "Visual mode covers common fields. Review or edit unsupported config.yaml entries in source mode.",
"quick_jump": "Quick Jump",
"mode": {
"simple": "Simple",
"full": "Full",
"label": "Editor mode",
"more_settings": "Switch to full mode to see all {{total}} sections",
"validation_banner": "Some advanced settings have validation errors and are blocking save. Switch to full mode to fix them.",
"switch_to_full": "Switch to full mode"
},
"search": {
"placeholder": "Search settings (label or YAML key)",
"no_results": "No matching settings"
},
"sections": {
"connectivity": {
"title": "Access & Authentication",
"description": "Server address, port, auth directory, and API keys"
},
"logging": {
"title": "Logging & Diagnostics",
"description": "Debug, log output, and usage statistics"
},
"advanced": {
"title": "Advanced & Experimental",
"description": "Header spoofing, signature cache, plugin sources, and other rarely changed settings",
"plugins_title": "Plugins",
"signature_title": "Antigravity Signature"
},
"server": {
"title": "Server Configuration",
"description": "Basic server settings",
+26
View File
@@ -896,7 +896,33 @@
"visual": {
"notice": "Визуальный режим охватывает основные поля. Остальные параметры config.yaml по-прежнему нужно проверять или редактировать в режиме исходника.",
"quick_jump": "Быстрый переход",
"mode": {
"simple": "Простой",
"full": "Полный",
"label": "Режим редактора",
"more_settings": "Переключитесь в полный режим, чтобы увидеть все разделы ({{total}})",
"validation_banner": "В некоторых дополнительных параметрах есть ошибки проверки, сохранение заблокировано. Переключитесь в полный режим, чтобы исправить их.",
"switch_to_full": "Перейти в полный режим"
},
"search": {
"placeholder": "Поиск настроек (по названию или ключу YAML)",
"no_results": "Нет подходящих настроек"
},
"sections": {
"connectivity": {
"title": "Доступ и аутентификация",
"description": "Адрес сервера, порт, каталог аутентификации и ключи API"
},
"logging": {
"title": "Логи и диагностика",
"description": "Отладка, вывод логов и статистика использования"
},
"advanced": {
"title": "Дополнительно и эксперименты",
"description": "Подмена заголовков, кэш подписей, источники плагинов и другие редко изменяемые настройки",
"plugins_title": "Плагины",
"signature_title": "Подпись Antigravity"
},
"server": {
"title": "Настройки сервера",
"description": "Базовые параметры сервера",
+26
View File
@@ -909,7 +909,33 @@
"visual": {
"notice": "可视化模式覆盖常用字段,未覆盖的配置仍需在源文件模式中查看或编辑。",
"quick_jump": "快速跳转",
"mode": {
"simple": "简单",
"full": "完整",
"label": "编辑模式",
"more_settings": "切换到完整模式,查看全部 {{total}} 个配置分区",
"validation_banner": "部分高级配置项存在校验错误,已阻止保存。请切换到完整模式进行修复。",
"switch_to_full": "切换完整模式"
},
"search": {
"placeholder": "搜索配置项(标签或 YAML 键名)",
"no_results": "没有匹配的配置项"
},
"sections": {
"connectivity": {
"title": "接入与认证",
"description": "服务地址、端口、认证目录与 API 密钥"
},
"logging": {
"title": "日志与诊断",
"description": "调试、日志输出与使用统计"
},
"advanced": {
"title": "高级与实验",
"description": "请求头伪装、签名缓存、插件源等不常修改的设置",
"plugins_title": "插件",
"signature_title": "Antigravity 签名"
},
"server": {
"title": "服务器配置",
"description": "基础服务器设置",
+26
View File
@@ -935,7 +935,33 @@
"visual": {
"notice": "視覺化模式涵蓋常用欄位,未涵蓋的設定仍需在原始檔模式中查看或編輯。",
"quick_jump": "快速跳轉",
"mode": {
"simple": "簡單",
"full": "完整",
"label": "編輯模式",
"more_settings": "切換到完整模式,檢視全部 {{total}} 個設定分區",
"validation_banner": "部分進階設定項存在驗證錯誤,已阻止儲存。請切換到完整模式進行修正。",
"switch_to_full": "切換完整模式"
},
"search": {
"placeholder": "搜尋設定項(標籤或 YAML 鍵名)",
"no_results": "沒有符合的設定項"
},
"sections": {
"connectivity": {
"title": "接入與認證",
"description": "服務位址、連接埠、認證目錄與 API 金鑰"
},
"logging": {
"title": "日誌與診斷",
"description": "除錯、日誌輸出與使用統計"
},
"advanced": {
"title": "進階與實驗",
"description": "請求標頭偽裝、簽章快取、外掛來源等不常修改的設定",
"plugins_title": "外掛",
"signature_title": "Antigravity 簽章"
},
"server": {
"title": "伺服器設定",
"description": "基本伺服器設定",