fix(tui): first-run copy batch — sequential trust keys, path-safe wrapping, honest model width, goal-first Operate placeholder
Four first-run papercuts from the morning report: Trust dialog keys read 1/3/2. The number keys now mirror the footer's reading order — 1 trusts, 2 continues without trusting, 3 quits (Y/U/N/ Esc unchanged) — with the footer, the Enter hint, and all fourteen translated locales updated together so copy and handling cannot drift. Deep workspace paths hard-split mid-component because ratatui's Wrap breaks only at whitespace. The trust prompt now wraps its location line at '/' boundaries with indented continuations, using the panel's real inner width. The header's proportional width split let a long workspace squeeze the model identity to a meaningless 'deep…' at 120 cols. The model — a trust fact — now keeps up to 14 columns before the workspace takes the rest. The Operate composer greeted first-run users with 'Coordinate parallel tasks...'; it now says what to type: 'Describe the goal — Codewhale keeps working until it's done'. Verified: onboarding 52, trust 69, localization 31, widgets 284, header 127 suites green; cargo fmt clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -479,7 +479,7 @@
|
||||
"OnboardTrustFooterMiddle": " per confiar-hi i continuar, ",
|
||||
"OnboardTrustFooterUntrustedMiddle": " per continuar sense confiar-hi, ",
|
||||
"OnboardTrustFooterSuffix": " per sortir de Codewhale",
|
||||
"OnboardTrustEnterHint": "Prem 1 o Y per confiar-hi, 3 o U per continuar sense confiar-hi, o 2, N o Esc per sortir de Codewhale.",
|
||||
"OnboardTrustEnterHint": "Prem 1 o Y per confiar-hi, 2 o U per continuar sense confiar-hi, o 3, N o Esc per sortir de Codewhale.",
|
||||
"OnboardTrustUntrustedNotice": "S'ha continuat sense confiança a l'espai de treball — les eines i els hooks continuen restringits.",
|
||||
"OnboardMentalTitle": "Com funciona Codewhale",
|
||||
"OnboardMentalModesLabel": "Modes",
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
"OnboardTrustFooterMiddle": " zum Vertrauen und Fortfahren, ",
|
||||
"OnboardTrustFooterUntrustedMiddle": " zum Fortfahren ohne Vertrauen, ",
|
||||
"OnboardTrustFooterSuffix": " zum Beenden von Codewhale",
|
||||
"OnboardTrustEnterHint": "Drücke 1 oder Y zum Vertrauen, 3 oder U, um ohne Vertrauen fortzufahren, oder 2, N oder Esc, um Codewhale zu beenden.",
|
||||
"OnboardTrustEnterHint": "Drücke 1 oder Y zum Vertrauen, 2 oder U, um ohne Vertrauen fortzufahren, oder 3, N oder Esc, um Codewhale zu beenden.",
|
||||
"OnboardTrustUntrustedNotice": "Fortfahren ohne Workspace-Vertrauen — Tools und Hooks bleiben eingeschränkt.",
|
||||
"OnboardMentalTitle": "Wie Codewhale funktioniert",
|
||||
"OnboardMentalModesLabel": "Modi",
|
||||
|
||||
@@ -502,7 +502,7 @@
|
||||
"OnboardTrustFooterMiddle": " to trust and continue, ",
|
||||
"OnboardTrustFooterUntrustedMiddle": " to continue without trusting, ",
|
||||
"OnboardTrustFooterSuffix": " to quit Codewhale",
|
||||
"OnboardTrustEnterHint": "Press 1 or Y to trust, 3 or U to continue without trusting, or 2, N, or Esc to quit Codewhale.",
|
||||
"OnboardTrustEnterHint": "Press 1 or Y to trust, 2 or U to continue without trusting, or 3, N, or Esc to quit Codewhale.",
|
||||
"OnboardTrustUntrustedNotice": "Continuing without workspace trust — tools and hooks stay restricted.",
|
||||
"OnboardMentalTitle": "How Codewhale works",
|
||||
"OnboardMentalModesLabel": "Modes",
|
||||
|
||||
@@ -502,7 +502,7 @@
|
||||
"OnboardTrustFooterMiddle": " para confiar y continuar, ",
|
||||
"OnboardTrustFooterUntrustedMiddle": " para continuar sin confiar, ",
|
||||
"OnboardTrustFooterSuffix": " para salir de Codewhale",
|
||||
"OnboardTrustEnterHint": "Pulsa 1 o Y para confiar, 3 o U para continuar sin confiar, o 2, N o Esc para salir de Codewhale.",
|
||||
"OnboardTrustEnterHint": "Pulsa 1 o Y para confiar, 2 o U para continuar sin confiar, o 3, N o Esc para salir de Codewhale.",
|
||||
"OnboardTrustUntrustedNotice": "Continuando sin confiar en el workspace: las herramientas y los hooks siguen restringidos.",
|
||||
"OnboardMentalTitle": "Cómo funciona Codewhale",
|
||||
"OnboardMentalModesLabel": "Modos",
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
"OnboardTrustFooterMiddle": " pour faire confiance et continuer, ",
|
||||
"OnboardTrustFooterUntrustedMiddle": " pour continuer sans confiance, ",
|
||||
"OnboardTrustFooterSuffix": " pour quitter Codewhale",
|
||||
"OnboardTrustEnterHint": "Appuyez sur 1 ou Y pour faire confiance, 3 ou U pour continuer sans confiance, ou 2, N ou Esc pour quitter Codewhale.",
|
||||
"OnboardTrustEnterHint": "Appuyez sur 1 ou Y pour faire confiance, 2 ou U pour continuer sans confiance, ou 3, N ou Esc pour quitter Codewhale.",
|
||||
"OnboardTrustUntrustedNotice": "Poursuite sans confiance du workspace — les outils et hooks restent restreints.",
|
||||
"OnboardMentalTitle": "Comment fonctionne Codewhale",
|
||||
"OnboardMentalModesLabel": "Modes",
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
"OnboardTrustFooterMiddle": " विश्वास करके जारी रखें, ",
|
||||
"OnboardTrustFooterUntrustedMiddle": " बिना विश्वास के जारी रखें, ",
|
||||
"OnboardTrustFooterSuffix": " Codewhale बंद करें",
|
||||
"OnboardTrustEnterHint": "विश्वास के लिए 1 या Y, बिना विश्वास के जारी रखने के लिए 3 या U, या Codewhale बंद करने के लिए 2, N या Esc दबाएँ।",
|
||||
"OnboardTrustEnterHint": "विश्वास के लिए 1 या Y, बिना विश्वास के जारी रखने के लिए 2 या U, या Codewhale बंद करने के लिए 3, N या Esc दबाएँ।",
|
||||
"OnboardTrustUntrustedNotice": "वर्कस्पेस ट्रस्ट के बिना जारी — टूल और हुक प्रतिबंधित रहेंगे।",
|
||||
"OnboardMentalTitle": "Codewhale कैसे काम करता है",
|
||||
"OnboardMentalModesLabel": "मोड",
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
"OnboardTrustFooterMiddle": " untuk mempercayai dan lanjut, ",
|
||||
"OnboardTrustFooterUntrustedMiddle": " untuk lanjut tanpa mempercayai, ",
|
||||
"OnboardTrustFooterSuffix": " untuk keluar dari Codewhale",
|
||||
"OnboardTrustEnterHint": "Tekan 1 atau Y untuk mempercayai, 3 atau U untuk lanjut tanpa mempercayai, atau 2, N, atau Esc untuk keluar dari Codewhale.",
|
||||
"OnboardTrustEnterHint": "Tekan 1 atau Y untuk mempercayai, 2 atau U untuk lanjut tanpa mempercayai, atau 3, N, atau Esc untuk keluar dari Codewhale.",
|
||||
"OnboardTrustUntrustedNotice": "Melanjutkan tanpa kepercayaan workspace — alat dan hook tetap dibatasi.",
|
||||
"OnboardMentalTitle": "Cara kerja Codewhale",
|
||||
"OnboardMentalModesLabel": "Mode",
|
||||
|
||||
@@ -502,7 +502,7 @@
|
||||
"OnboardTrustFooterMiddle": " で信頼して続行、",
|
||||
"OnboardTrustFooterUntrustedMiddle": " で信頼せずに続行、",
|
||||
"OnboardTrustFooterSuffix": " で Codewhale を終了",
|
||||
"OnboardTrustEnterHint": "1 または Y で信頼、3 または U で信頼せずに続行、2・N・Esc で Codewhale を終了します。",
|
||||
"OnboardTrustEnterHint": "1 または Y で信頼、2 または U で信頼せずに続行、3・N・Esc で Codewhale を終了します。",
|
||||
"OnboardTrustUntrustedNotice": "ワークスペースを信頼せずに続行します — ツールとフックは制限されたままです。",
|
||||
"OnboardMentalTitle": "Codewhale のしくみ",
|
||||
"OnboardMentalModesLabel": "モード",
|
||||
|
||||
@@ -502,7 +502,7 @@
|
||||
"OnboardTrustFooterMiddle": " 신뢰하고 계속, ",
|
||||
"OnboardTrustFooterUntrustedMiddle": " 신뢰하지 않고 계속, ",
|
||||
"OnboardTrustFooterSuffix": " Codewhale 종료",
|
||||
"OnboardTrustEnterHint": "1 또는 Y로 신뢰, 3 또는 U로 신뢰 없이 계속, 2·N·Esc로 Codewhale을 종료합니다.",
|
||||
"OnboardTrustEnterHint": "1 또는 Y로 신뢰, 2 또는 U로 신뢰 없이 계속, 3·N·Esc로 Codewhale을 종료합니다.",
|
||||
"OnboardTrustUntrustedNotice": "작업 공간을 신뢰하지 않고 계속합니다 — 도구와 훅은 제한된 상태로 유지됩니다.",
|
||||
"OnboardMentalTitle": "Codewhale 작동 방식",
|
||||
"OnboardMentalModesLabel": "모드",
|
||||
|
||||
@@ -502,7 +502,7 @@
|
||||
"OnboardTrustFooterMiddle": " para confiar e continuar, ",
|
||||
"OnboardTrustFooterUntrustedMiddle": " para continuar sem confiar, ",
|
||||
"OnboardTrustFooterSuffix": " para sair do Codewhale",
|
||||
"OnboardTrustEnterHint": "Pressione 1 ou Y para confiar, 3 ou U para continuar sem confiar, ou 2, N ou Esc para sair do Codewhale.",
|
||||
"OnboardTrustEnterHint": "Pressione 1 ou Y para confiar, 2 ou U para continuar sem confiar, ou 3, N ou Esc para sair do Codewhale.",
|
||||
"OnboardTrustUntrustedNotice": "Continuando sem confiar no workspace — ferramentas e hooks permanecem restritos.",
|
||||
"OnboardMentalTitle": "Como o Codewhale funciona",
|
||||
"OnboardMentalModesLabel": "Modos",
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
"OnboardTrustFooterMiddle": " — доверить и продолжить, ",
|
||||
"OnboardTrustFooterUntrustedMiddle": " — продолжить без доверия, ",
|
||||
"OnboardTrustFooterSuffix": " — выйти из Codewhale",
|
||||
"OnboardTrustEnterHint": "Нажмите 1 или Y — доверить, 3 или U — продолжить без доверия, 2, N или Esc — выйти из Codewhale.",
|
||||
"OnboardTrustEnterHint": "Нажмите 1 или Y — доверить, 2 или U — продолжить без доверия, 3, N или Esc — выйти из Codewhale.",
|
||||
"OnboardTrustUntrustedNotice": "Продолжение без доверия к рабочей области — инструменты и хуки останутся ограниченными.",
|
||||
"OnboardMentalTitle": "Как работает Codewhale",
|
||||
"OnboardMentalModesLabel": "Режимы",
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
"OnboardTrustFooterMiddle": " щоб довірити й продовжити, ",
|
||||
"OnboardTrustFooterUntrustedMiddle": " щоб продовжити без довіри, ",
|
||||
"OnboardTrustFooterSuffix": " щоб вийти з Codewhale",
|
||||
"OnboardTrustEnterHint": "Натисніть 1 або Y, щоб довірити; 3 або U, щоб продовжити без довіри; або 2, N чи Esc, щоб вийти з Codewhale.",
|
||||
"OnboardTrustEnterHint": "Натисніть 1 або Y, щоб довірити; 2 або U, щоб продовжити без довіри; або 3, N чи Esc, щоб вийти з Codewhale.",
|
||||
"OnboardTrustUntrustedNotice": "Продовження без довіри до робочої області — інструменти та хуки лишаються обмеженими.",
|
||||
"OnboardMentalTitle": "Як працює Codewhale",
|
||||
"OnboardMentalModesLabel": "Режими",
|
||||
|
||||
@@ -502,7 +502,7 @@
|
||||
"OnboardTrustFooterMiddle": " để tin cậy và tiếp tục, ",
|
||||
"OnboardTrustFooterUntrustedMiddle": " để tiếp tục mà không tin cậy, ",
|
||||
"OnboardTrustFooterSuffix": " để thoát Codewhale",
|
||||
"OnboardTrustEnterHint": "Nhấn 1 hoặc Y để tin cậy, 3 hoặc U để tiếp tục mà không tin cậy, hoặc 2, N, Esc để thoát Codewhale.",
|
||||
"OnboardTrustEnterHint": "Nhấn 1 hoặc Y để tin cậy, 2 hoặc U để tiếp tục mà không tin cậy, hoặc 3, N, Esc để thoát Codewhale.",
|
||||
"OnboardTrustUntrustedNotice": "Tiếp tục mà không tin cậy workspace — công cụ và hook vẫn bị hạn chế.",
|
||||
"OnboardMentalTitle": "Cách Codewhale hoạt động",
|
||||
"OnboardMentalModesLabel": "Chế độ",
|
||||
|
||||
@@ -502,7 +502,7 @@
|
||||
"OnboardTrustFooterMiddle": " 信任并继续,",
|
||||
"OnboardTrustFooterUntrustedMiddle": " 不信任并继续,",
|
||||
"OnboardTrustFooterSuffix": " 退出 Codewhale",
|
||||
"OnboardTrustEnterHint": "按 1 或 Y 信任,按 3 或 U 在不信任的情况下继续,按 2、N 或 Esc 退出 Codewhale。",
|
||||
"OnboardTrustEnterHint": "按 1 或 Y 信任,按 2 或 U 在不信任的情况下继续,按 3、N 或 Esc 退出 Codewhale。",
|
||||
"OnboardTrustUntrustedNotice": "在未信任工作区的情况下继续 — 工具与钩子保持受限。",
|
||||
"OnboardMentalTitle": "Codewhale 的工作方式",
|
||||
"OnboardMentalModesLabel": "模式",
|
||||
|
||||
@@ -39,7 +39,10 @@ pub fn render(f: &mut Frame, area: Rect, app: &App) {
|
||||
OnboardingState::Language => language::lines(app),
|
||||
OnboardingState::Appearance => appearance_lines(app),
|
||||
OnboardingState::Provider => provider_lines(app),
|
||||
OnboardingState::TrustDirectory => trust_directory::lines(app),
|
||||
OnboardingState::TrustDirectory => {
|
||||
// Inner text width: panel borders (2) plus horizontal padding (4).
|
||||
trust_directory::lines(app, usize::from(content_width.saturating_sub(6)))
|
||||
}
|
||||
OnboardingState::MentalModels => mental_models::lines(app),
|
||||
OnboardingState::Tips => tips_lines(app),
|
||||
OnboardingState::None => Vec::new(),
|
||||
@@ -433,7 +436,7 @@ mod tests {
|
||||
#[test]
|
||||
fn trust_footer_advertises_only_explicit_trust_keys() {
|
||||
let app = test_app_with_locale(Locale::En);
|
||||
let lines = trust_directory::lines(&app);
|
||||
let lines = trust_directory::lines(&app, 70);
|
||||
let footer = lines
|
||||
.last()
|
||||
.expect("trust footer")
|
||||
@@ -444,7 +447,7 @@ mod tests {
|
||||
|
||||
assert_eq!(
|
||||
footer,
|
||||
"Press 1/Y to trust and continue, 3/U to continue without trusting, 2/N/Esc to quit Codewhale"
|
||||
"Press 1/Y to trust and continue, 2/U to continue without trusting, 3/N/Esc to quit Codewhale"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,44 @@ use crate::localization::MessageId;
|
||||
use crate::palette;
|
||||
use crate::tui::app::App;
|
||||
|
||||
pub fn lines(app: &App) -> Vec<Line<'static>> {
|
||||
/// Wrap a path-bearing line at `/` boundaries so a deep workspace never
|
||||
/// hard-splits mid-component under ratatui's whitespace-only `Wrap`.
|
||||
/// Continuation lines are indented to read as one location.
|
||||
fn wrap_on_path_separators(text: &str, width: usize) -> Vec<String> {
|
||||
let width = width.max(8);
|
||||
let mut out: Vec<String> = Vec::new();
|
||||
let mut current = String::new();
|
||||
let mut chunk = String::new();
|
||||
let mut flush = |current: &mut String, chunk: &mut String, out: &mut Vec<String>| {
|
||||
if chunk.is_empty() {
|
||||
return;
|
||||
}
|
||||
let candidate_len = current.chars().count() + chunk.chars().count();
|
||||
if candidate_len > width && !current.is_empty() {
|
||||
out.push(std::mem::take(current));
|
||||
current.push_str(" ");
|
||||
}
|
||||
current.push_str(chunk);
|
||||
chunk.clear();
|
||||
};
|
||||
for ch in text.chars() {
|
||||
chunk.push(ch);
|
||||
if ch == '/' {
|
||||
flush(&mut current, &mut chunk, &mut out);
|
||||
}
|
||||
}
|
||||
flush(&mut current, &mut chunk, &mut out);
|
||||
if !current.is_empty() {
|
||||
out.push(current);
|
||||
}
|
||||
if out.is_empty() {
|
||||
vec![String::new()]
|
||||
} else {
|
||||
out
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lines(app: &App, content_width: usize) -> Vec<Line<'static>> {
|
||||
let mut lines = Vec::new();
|
||||
lines.push(Line::from(Span::styled(
|
||||
app.tr(MessageId::OnboardTrustTitle).to_string(),
|
||||
@@ -20,14 +57,17 @@ pub fn lines(app: &App) -> Vec<Line<'static>> {
|
||||
app.tr(MessageId::OnboardTrustQuestion).to_string(),
|
||||
Style::default().fg(palette::TEXT_PRIMARY),
|
||||
)));
|
||||
lines.push(Line::from(Span::styled(
|
||||
format!(
|
||||
"{}{}",
|
||||
app.tr(MessageId::OnboardTrustLocationPrefix),
|
||||
crate::utils::display_path(&app.workspace)
|
||||
),
|
||||
Style::default().fg(palette::TEXT_MUTED),
|
||||
)));
|
||||
let location = format!(
|
||||
"{}{}",
|
||||
app.tr(MessageId::OnboardTrustLocationPrefix),
|
||||
crate::utils::display_path(&app.workspace)
|
||||
);
|
||||
for segment in wrap_on_path_separators(&location, content_width) {
|
||||
lines.push(Line::from(Span::styled(
|
||||
segment,
|
||||
Style::default().fg(palette::TEXT_MUTED),
|
||||
)));
|
||||
}
|
||||
lines.push(Line::from(""));
|
||||
lines.push(Line::from(Span::styled(
|
||||
app.tr(MessageId::OnboardTrustRiskHint).to_string(),
|
||||
@@ -61,7 +101,7 @@ pub fn lines(app: &App) -> Vec<Line<'static>> {
|
||||
Style::default().fg(palette::TEXT_MUTED),
|
||||
),
|
||||
Span::styled(
|
||||
"3/U",
|
||||
"2/U",
|
||||
Style::default()
|
||||
.fg(palette::TEXT_PRIMARY)
|
||||
.add_modifier(Modifier::BOLD),
|
||||
@@ -72,7 +112,7 @@ pub fn lines(app: &App) -> Vec<Line<'static>> {
|
||||
Style::default().fg(palette::TEXT_MUTED),
|
||||
),
|
||||
Span::styled(
|
||||
"2/N/Esc",
|
||||
"3/N/Esc",
|
||||
Style::default()
|
||||
.fg(palette::TEXT_PRIMARY)
|
||||
.add_modifier(Modifier::BOLD),
|
||||
@@ -100,7 +140,7 @@ mod tests {
|
||||
};
|
||||
let mut app = App::new(options, &Config::default());
|
||||
app.ui_locale = crate::localization::Locale::En;
|
||||
let body = lines(&app)
|
||||
let body = lines(&app, 70)
|
||||
.into_iter()
|
||||
.flat_map(|line| line.spans.into_iter().map(|span| span.content.to_string()))
|
||||
.collect::<Vec<_>>()
|
||||
@@ -111,9 +151,9 @@ mod tests {
|
||||
assert!(body.contains("prompt injection"));
|
||||
assert!(body.contains("tools and hooks"));
|
||||
assert!(body.contains("1/Y"));
|
||||
assert!(body.contains("3/U"));
|
||||
assert!(body.contains("2/U"));
|
||||
assert!(body.contains("continue without trusting"));
|
||||
assert!(body.contains("2/N/Esc"));
|
||||
assert!(body.contains("3/N/Esc"));
|
||||
assert!(body.contains("quit Codewhale"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6071,12 +6071,15 @@ async fn run_event_loop(
|
||||
app.status_message = Some(format!("Failed to trust workspace: {err}"));
|
||||
}
|
||||
}
|
||||
KeyCode::Char('u') | KeyCode::Char('U') | KeyCode::Char('3')
|
||||
// Number keys mirror the footer's reading order (1 trust,
|
||||
// 2 continue untrusted, 3 quit) so the displayed digits
|
||||
// are sequential instead of 1/3/2.
|
||||
KeyCode::Char('u') | KeyCode::Char('U') | KeyCode::Char('2')
|
||||
if app.onboarding == OnboardingState::TrustDirectory =>
|
||||
{
|
||||
continue_without_trusting_directory(app);
|
||||
}
|
||||
KeyCode::Char('n') | KeyCode::Char('N') | KeyCode::Char('2')
|
||||
KeyCode::Char('n') | KeyCode::Char('N') | KeyCode::Char('3')
|
||||
if app.onboarding == OnboardingState::TrustDirectory =>
|
||||
{
|
||||
let _ = engine_handle.send(Op::Shutdown).await;
|
||||
|
||||
@@ -477,7 +477,11 @@ impl<'a> HeaderWidget<'a> {
|
||||
let model_width = model.width();
|
||||
let total_width = workspace_width + model_width;
|
||||
let min_workspace = 4;
|
||||
let min_model = 4;
|
||||
// The model identity is a trust fact ("what am I talking to?"), so it
|
||||
// keeps enough room to stay recognizable — "deepseek-v4-…" — before
|
||||
// the proportional split lets a long workspace name squeeze it to a
|
||||
// meaningless "deep…" (morning-report first-run copy batch).
|
||||
let min_model = model_width.min(14).max(4);
|
||||
|
||||
let proportional_workspace =
|
||||
((content_width as f64 * workspace_width as f64) / total_width as f64).round() as usize;
|
||||
|
||||
@@ -3383,7 +3383,9 @@ pub(crate) fn composer_empty_hint_text(app: &App) -> Cow<'static, str> {
|
||||
if app.is_history_search_active() {
|
||||
app.tr(crate::localization::MessageId::HistorySearchPlaceholder)
|
||||
} else if app.mode == crate::tui::app::AppMode::Operate {
|
||||
Cow::Borrowed("Coordinate parallel tasks...")
|
||||
// Operate is goal-driven; the empty composer says what to type, not
|
||||
// orchestration jargon a first-run user has no model for.
|
||||
Cow::Borrowed("Describe the goal — Codewhale keeps working until it's done")
|
||||
} else {
|
||||
app.tr(crate::localization::MessageId::ComposerPlaceholder)
|
||||
}
|
||||
@@ -5993,7 +5995,7 @@ mod tests {
|
||||
|
||||
assert_eq!(
|
||||
composer_empty_hint_text(&app),
|
||||
"Coordinate parallel tasks..."
|
||||
"Describe the goal — Codewhale keeps working until it's done"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user