From 4be044a60afec35dc53201e1dfa1b3452e29d103 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Sat, 16 May 2026 07:07:16 +0200 Subject: [PATCH 1/7] feat: Redesign env confirmation dialog with horizontal layout and scrollable details - Create custom WebView dialog instead of native modal - Layout: Message on left, env details on right (scrollable) - Env details section only scrolls, dialog fits within screen height - Wider popup reduces vertical scrolling and improves readability - Add confirmEnvAction() method for environment-specific dialogs - Update env-service to use new dialog for env selection confirmation --- src/configuration.ts | 34 ++++---- src/services/env-service.ts | 2 +- src/utils.ts | 155 ++++++++++++++++++++++++++++++++++++ 3 files changed, 173 insertions(+), 18 deletions(-) diff --git a/src/configuration.ts b/src/configuration.ts index 180a3ce..68618f2 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -225,25 +225,25 @@ export class Configuration { this.rag_max_embedding_filter_chunks = Number(config.get("rag_max_embedding_filter_chunks")); this.rag_max_context_files = Number(config.get("rag_max_context_files")); this.rag_max_context_file_chars = Number(config.get("rag_max_context_file_chars")); - this.tool_run_terminal_command_enabled = Boolean(config.get("tool_run_terminal_command_enabled")); - this.tool_create_agent_enabled = Boolean(config.get("tool_create_agent_enabled")); - this.tool_search_source_enabled = Boolean(config.get("tool_search_source_enabled")); - this.tool_read_file_enabled = Boolean(config.get("tool_read_file_enabled")); - this.tool_list_directory_enabled = Boolean(config.get("tool_list_directory_enabled")); - this.tool_regex_search_enabled = Boolean(config.get("tool_regex_search_enabled")); - this.tool_delete_file_enabled = Boolean(config.get("tool_delete_file_enabled")); - this.tool_permit_some_terminal_commands = Boolean(config.get("tool_permit_some_terminal_commands")); - this.tool_permit_file_changes = Boolean(config.get("tool_permit_file_changes")); - this.tool_get_diff_enabled = Boolean(config.get("tool_get_diff_enabled")); - this.tool_edit_file_enabled = Boolean(config.get("tool_edit_file_enabled")); - this.tool_ask_user_enabled = Boolean(config.get("tool_ask_user_enabled")); - this.tool_custom_tool_enabled = Boolean(config.get("tool_custom_tool_enabled")); - this.tool_update_todo_list_enabled = Boolean(config.get("tool_update_todo_list_enabled")); - this.tool_delegate_task_enabled = Boolean(config.get("tool_delegate_task_enabled")); - this.tool_llama_vscode_help_enabled = Boolean(config.get("tool_llama_vscode_help_enabled")); + this.tool_run_terminal_command_enabled = config.get("tool_run_terminal_command_enabled", true); + this.tool_create_agent_enabled = config.get("tool_create_agent_enabled", true); + this.tool_search_source_enabled = config.get("tool_search_source_enabled", true); + this.tool_read_file_enabled = config.get("tool_read_file_enabled", true); + this.tool_list_directory_enabled = config.get("tool_list_directory_enabled", true); + this.tool_regex_search_enabled = config.get("tool_regex_search_enabled", true); + this.tool_delete_file_enabled = config.get("tool_delete_file_enabled", true); + this.tool_permit_some_terminal_commands = config.get("tool_permit_some_terminal_commands", false); + this.tool_permit_file_changes = config.get("tool_permit_file_changes", false); + this.tool_get_diff_enabled = config.get("tool_get_diff_enabled", false); + this.tool_edit_file_enabled = config.get("tool_edit_file_enabled", true); + this.tool_ask_user_enabled = config.get("tool_ask_user_enabled", true); + this.tool_custom_tool_enabled = config.get("tool_custom_tool_enabled", false); + this.tool_update_todo_list_enabled = config.get("tool_update_todo_list_enabled", true); + this.tool_delegate_task_enabled = config.get("tool_delegate_task_enabled", true); + this.tool_llama_vscode_help_enabled = config.get("tool_llama_vscode_help_enabled", true); this.tool_custom_tool_description = String(config.get("tool_custom_tool_description")); this.tool_custom_tool_source = String(config.get("tool_custom_tool_source")); - this.tool_custom_eval_tool_enabled = Boolean(config.get("tool_custom_eval_tool_enabled")); + this.tool_custom_eval_tool_enabled = config.get("tool_custom_eval_tool_enabled", false); this.tool_custom_eval_tool_property_description = String(config.get("tool_custom_eval_tool_property_description")); this.tool_custom_eval_tool_description = String(config.get("tool_custom_eval_tool_description")); this.tool_custom_eval_tool_code = String(config.get("tool_custom_eval_tool_code")); diff --git a/src/services/env-service.ts b/src/services/env-service.ts index 2c87661..e361c17 100644 --- a/src/services/env-service.ts +++ b/src/services/env-service.ts @@ -132,7 +132,7 @@ export class EnvService { envStartLastUsed: env.envStartLastUsed ?? currentEnvStartLastUsed, complEnabled: env.complEnabled ?? currentComplEnabled, }; - shouldSelect = await Utils.confirmAction( + shouldSelect = await Utils.confirmEnvAction( "You are about to select the env below. If there are local models inside, they will be downloaded (if not yet done) and llama.cpp server(s) will be started.\n\n Do you want to continue?", this.getEnvDetailsAsString(tempEnv) ); diff --git a/src/utils.ts b/src/utils.ts index 8c3f342..1fa0c6c 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -707,6 +707,157 @@ export class Utils { }); } + static showEnvConfirmationDialog = (message: string, details: string): Promise => { + return new Promise((resolve) => { + const panel = vscode.window.createWebviewPanel( + 'envConfirmation', + 'Environment Selection', + { viewColumn: vscode.ViewColumn.One, preserveFocus: true }, + { enableScripts: true } + ); + + // Calculate max height to fit screen (approximate - 80% of viewport) + const maxDetailHeight = 'calc(80vh - 200px)'; + + panel.webview.html = ` + + + + + + + + +
+
${message.replace(/\n/g, '
')}
+
+
Env Details
+
${details.replace(/\n/g, '
').replace(/: /g, ': ')}
+
+
+
+ + +
+ + + + `; + + panel.webview.onDidReceiveMessage((message) => { + if (message.command === 'answer') { + resolve(message.value); + panel.dispose(); + } + }); + }); + } + static fetchWebPage = async (url: string): Promise => { // Validate the URL let parsedUrl: URL; @@ -835,6 +986,10 @@ export class Utils { return Utils.showYesNoDialog(fullMessage); } + static async confirmEnvAction(message: string, details: string = ""): Promise { + return Utils.showEnvConfirmationDialog(message, details); + } + static getFunctionFromFile = (filePath: string) => { let functionCode = fs.readFileSync(filePath, 'utf-8'); const functionString = '(' + functionCode + ')'; From e4b3bf4ab6318b846e4c4241b7418e3e31c82c0f Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Sat, 16 May 2026 07:29:43 +0200 Subject: [PATCH 2/7] refactor: Change env confirmation dialog from 2-column to 1-column layout - Message now displayed at top (row 1) - Env details section below (row 2, scrollable) - Buttons remain at bottom - More intuitive vertical flow for sequential information reading --- src/utils.ts | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/src/utils.ts b/src/utils.ts index 1fa0c6c..887612f 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -716,9 +716,6 @@ export class Utils { { enableScripts: true } ); - // Calculate max height to fit screen (approximate - 80% of viewport) - const maxDetailHeight = 'calc(80vh - 200px)'; - panel.webview.html = ` @@ -741,30 +738,27 @@ export class Utils { } .container { display: flex; - flex-direction: row; - gap: 16px; + flex-direction: column; + gap: 12px; flex: 1; min-height: 0; } .message { - flex: 0 0 280px; - display: flex; - align-items: center; - justify-content: center; - white-space: pre-wrap; - word-wrap: break-word; + flex: 0 0 auto; padding: 12px; border-radius: 4px; background: var(--vscode-editor-background); border: 1px solid var(--vscode-panel-border); font-size: 13px; line-height: 1.5; + white-space: pre-wrap; + word-wrap: break-word; } .details { flex: 1; display: flex; flex-direction: column; - min-width: 0; + min-height: 0; border: 1px solid var(--vscode-panel-border); border-radius: 4px; background: var(--vscode-editor-background); @@ -776,6 +770,7 @@ export class Utils { font-size: 12px; color: var(--vscode-editor-foreground); background: var(--vscode-sideBar-background); + flex: 0 0 auto; } .details-content { flex: 1; @@ -800,7 +795,7 @@ export class Utils { .buttons { display: flex; gap: 8px; - margin-top: 16px; + margin-top: 12px; justify-content: flex-end; flex: 0 0 auto; } From dcfce0aaa932837f14e7269e5ad0d9f324eb7819 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Sat, 16 May 2026 07:42:33 +0200 Subject: [PATCH 3/7] remove: Revert configuration.ts changes (not part of env dialog feature) --- src/configuration.ts | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/configuration.ts b/src/configuration.ts index 68618f2..180a3ce 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -225,25 +225,25 @@ export class Configuration { this.rag_max_embedding_filter_chunks = Number(config.get("rag_max_embedding_filter_chunks")); this.rag_max_context_files = Number(config.get("rag_max_context_files")); this.rag_max_context_file_chars = Number(config.get("rag_max_context_file_chars")); - this.tool_run_terminal_command_enabled = config.get("tool_run_terminal_command_enabled", true); - this.tool_create_agent_enabled = config.get("tool_create_agent_enabled", true); - this.tool_search_source_enabled = config.get("tool_search_source_enabled", true); - this.tool_read_file_enabled = config.get("tool_read_file_enabled", true); - this.tool_list_directory_enabled = config.get("tool_list_directory_enabled", true); - this.tool_regex_search_enabled = config.get("tool_regex_search_enabled", true); - this.tool_delete_file_enabled = config.get("tool_delete_file_enabled", true); - this.tool_permit_some_terminal_commands = config.get("tool_permit_some_terminal_commands", false); - this.tool_permit_file_changes = config.get("tool_permit_file_changes", false); - this.tool_get_diff_enabled = config.get("tool_get_diff_enabled", false); - this.tool_edit_file_enabled = config.get("tool_edit_file_enabled", true); - this.tool_ask_user_enabled = config.get("tool_ask_user_enabled", true); - this.tool_custom_tool_enabled = config.get("tool_custom_tool_enabled", false); - this.tool_update_todo_list_enabled = config.get("tool_update_todo_list_enabled", true); - this.tool_delegate_task_enabled = config.get("tool_delegate_task_enabled", true); - this.tool_llama_vscode_help_enabled = config.get("tool_llama_vscode_help_enabled", true); + this.tool_run_terminal_command_enabled = Boolean(config.get("tool_run_terminal_command_enabled")); + this.tool_create_agent_enabled = Boolean(config.get("tool_create_agent_enabled")); + this.tool_search_source_enabled = Boolean(config.get("tool_search_source_enabled")); + this.tool_read_file_enabled = Boolean(config.get("tool_read_file_enabled")); + this.tool_list_directory_enabled = Boolean(config.get("tool_list_directory_enabled")); + this.tool_regex_search_enabled = Boolean(config.get("tool_regex_search_enabled")); + this.tool_delete_file_enabled = Boolean(config.get("tool_delete_file_enabled")); + this.tool_permit_some_terminal_commands = Boolean(config.get("tool_permit_some_terminal_commands")); + this.tool_permit_file_changes = Boolean(config.get("tool_permit_file_changes")); + this.tool_get_diff_enabled = Boolean(config.get("tool_get_diff_enabled")); + this.tool_edit_file_enabled = Boolean(config.get("tool_edit_file_enabled")); + this.tool_ask_user_enabled = Boolean(config.get("tool_ask_user_enabled")); + this.tool_custom_tool_enabled = Boolean(config.get("tool_custom_tool_enabled")); + this.tool_update_todo_list_enabled = Boolean(config.get("tool_update_todo_list_enabled")); + this.tool_delegate_task_enabled = Boolean(config.get("tool_delegate_task_enabled")); + this.tool_llama_vscode_help_enabled = Boolean(config.get("tool_llama_vscode_help_enabled")); this.tool_custom_tool_description = String(config.get("tool_custom_tool_description")); this.tool_custom_tool_source = String(config.get("tool_custom_tool_source")); - this.tool_custom_eval_tool_enabled = config.get("tool_custom_eval_tool_enabled", false); + this.tool_custom_eval_tool_enabled = Boolean(config.get("tool_custom_eval_tool_enabled")); this.tool_custom_eval_tool_property_description = String(config.get("tool_custom_eval_tool_property_description")); this.tool_custom_eval_tool_description = String(config.get("tool_custom_eval_tool_description")); this.tool_custom_eval_tool_code = String(config.get("tool_custom_eval_tool_code")); From f0148f93209fa2740136b80d9c1fa64d6d5c2aff Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Sat, 16 May 2026 09:26:54 +0200 Subject: [PATCH 4/7] feat: Add file permission dialog with improved layout - Create showFilePermissionDialog() for scrollable file path display - Add confirmFilePermissionAction() helper method - Update tools.ts to use new file permission dialog - File path no longer cut off - scrollable section for full visibility - Consistent styling with env confirmation dialog - Supports Yes/No and Don't Ask Again options --- src/tools.ts | 2 +- src/utils.ts | 153 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 154 insertions(+), 1 deletion(-) diff --git a/src/tools.ts b/src/tools.ts index cda02f6..6bbe0ca 100644 --- a/src/tools.ts +++ b/src/tools.ts @@ -268,7 +268,7 @@ export class Tools { try { if (!this.app.configuration.tool_permit_file_changes){ - let [yesApply, yesDontAsk] = await Utils.showYesYesdontaskNoDialog("Do you permit file " + filePath + " to be changed?") + let [yesApply, yesDontAsk] = await Utils.confirmFilePermissionAction("Do you permit file to be changed?", filePath) if (yesDontAsk) { this.app.configuration.updateConfigValue("tool_permit_file_changes", true) vscode.window.showInformationMessage("Setting tool_permit_file_changes is set to true.") diff --git a/src/utils.ts b/src/utils.ts index 887612f..eed7f8b 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -853,6 +853,155 @@ export class Utils { }); } + static showFilePermissionDialog = (message: string, filePath: string): Promise<[boolean, boolean]> => { + return new Promise((resolve) => { + const panel = vscode.window.createWebviewPanel( + 'filePermission', + 'File Permission', + { viewColumn: vscode.ViewColumn.One, preserveFocus: true }, + { enableScripts: true } + ); + + panel.webview.html = ` + + + + + + + + +
+
${message.replace(/\n/g, '
')}
+
+
File Path
+
${filePath.replace(/\n/g, '
')}
+
+
+
+ + + + +
+ + + + `; + + panel.webview.onDidReceiveMessage((message) => { + if (message.command === 'answer') { + resolve(message.value); + panel.dispose(); + } + }); + }); + } + static fetchWebPage = async (url: string): Promise => { // Validate the URL let parsedUrl: URL; @@ -985,6 +1134,10 @@ export class Utils { return Utils.showEnvConfirmationDialog(message, details); } + static async confirmFilePermissionAction(message: string, filePath: string): Promise<[boolean, boolean]> { + return Utils.showFilePermissionDialog(message, filePath); + } + static getFunctionFromFile = (filePath: string) => { let functionCode = fs.readFileSync(filePath, 'utf-8'); const functionString = '(' + functionCode + ')'; From d9e3947159492e132572805a4095d6240821db9d Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Sat, 16 May 2026 09:31:08 +0200 Subject: [PATCH 5/7] feat: Update all env confirmation dialogs to use custom WebView format - Convert showYesNoDialog() to use custom WebView instead of native modal - Convert showOkDialog() to use custom WebView instead of native modal - Update confirmAction() to use showEnvConfirmationDialog() for consistency - All env popups now display full details without text truncation - Fixes env add, delete, view, export confirmations --- src/utils.ts | 208 +++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 194 insertions(+), 14 deletions(-) diff --git a/src/utils.ts b/src/utils.ts index eed7f8b..30704b6 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -312,14 +312,107 @@ export class Utils { } static showYesNoDialog = async (message: string): Promise => { - const choice = await vscode.window.showInformationMessage( - "llama-vscode \n\n" + message, - { modal: true }, // Makes the dialog modal (blocks interaction until resolved) - 'Yes', - 'No' - ); + return new Promise((resolve) => { + const panel = vscode.window.createWebviewPanel( + 'yesNoDialog', + 'Confirmation', + { viewColumn: vscode.ViewColumn.One, preserveFocus: true }, + { enableScripts: true } + ); - return choice === 'Yes'; + panel.webview.html = ` + + + + + + + + +
+
${message.replace(/\n/g, '
')}
+
+
+ + +
+ + + + `; + + panel.webview.onDidReceiveMessage((message) => { + if (message.command === 'answer') { + resolve(message.value); + panel.dispose(); + } + }); + }); } static showUserChoiceDialog = async (message: string, acceptLabel: string): Promise => { @@ -357,11 +450,99 @@ export class Utils { } static showOkDialog = async (message: string) => { - const choice = await vscode.window.showInformationMessage( - "llama-vscode \n\n" + message, - { modal: true }, // Makes the dialog modal (blocks interaction until resolved) - 'OK' - ); + return new Promise((resolve) => { + const panel = vscode.window.createWebviewPanel( + 'okDialog', + 'Information', + { viewColumn: vscode.ViewColumn.One, preserveFocus: true }, + { enableScripts: true } + ); + + panel.webview.html = ` + + + + + + + + +
+
${message.replace(/\n/g, '
')}
+
+
+ +
+ + + + `; + + panel.webview.onDidReceiveMessage((message) => { + if (message.command === 'answer') { + resolve(message.value); + panel.dispose(); + } + }); + }); } static getAbsolutePath = async (shortFileName: string): Promise => { @@ -1126,8 +1307,7 @@ export class Utils { } static async confirmAction(message: string, details: string = ""): Promise { - const fullMessage = message + (details ? "\n\n" + details : ""); - return Utils.showYesNoDialog(fullMessage); + return Utils.showEnvConfirmationDialog(message, details); } static async confirmEnvAction(message: string, details: string = ""): Promise { From 792bd9ba35a29e5610053b098a2250458cd07716 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Sat, 16 May 2026 09:35:35 +0200 Subject: [PATCH 6/7] feat: Convert all remaining native modals to custom WebView dialogs - Update showYesYesdontaskNoDialog() to use custom WebView format - Update showYesNoNodontAskDialog() to use custom WebView format - Update showUserChoiceDialog() to use custom WebView format - All dialogs now support scrollable message areas - Fixes env startup confirmation (architect.ts) - Fixes installation confirmations - Ensures consistent UI across all confirmation dialogs --- src/utils.ts | 424 ++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 402 insertions(+), 22 deletions(-) diff --git a/src/utils.ts b/src/utils.ts index 30704b6..4a6937d 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -416,37 +416,417 @@ export class Utils { } static showUserChoiceDialog = async (message: string, acceptLabel: string): Promise => { - const choice = await vscode.window.showInformationMessage( - "llama-vscode \n\n" + message, - { modal: true }, // Makes the dialog modal (blocks interaction until resolved) - acceptLabel - ); + return new Promise((resolve) => { + const panel = vscode.window.createWebviewPanel( + 'userChoice', + 'Confirmation', + { viewColumn: vscode.ViewColumn.One, preserveFocus: true }, + { enableScripts: true } + ); - return choice === acceptLabel; + panel.webview.html = ` + + + + + + + + +
+
${message.replace(/\\n/g, '
')}
+
+
+ + +
+ + + + `; + + panel.webview.onDidReceiveMessage((message) => { + if (message.command === 'answer') { + resolve(message.value); + panel.dispose(); + } + }); + }); } static showYesYesdontaskNoDialog = async (message: string): Promise<[boolean, boolean]> => { - const choice = await vscode.window.showInformationMessage( - "llama-vscode \n\n" + message, - { modal: true }, // Makes the dialog modal (blocks interaction until resolved) - 'Yes', - "Yes, don't ask again", - 'No' - ); + return new Promise((resolve) => { + const panel = vscode.window.createWebviewPanel( + 'yesYesDontAsk', + 'Confirmation', + { viewColumn: vscode.ViewColumn.One, preserveFocus: true }, + { enableScripts: true } + ); + + // Split message into title and details + const parts = message.split('\n\n'); + const title = parts[0]; + const details = parts.slice(1).join('\n\n'); + + panel.webview.html = ` + + + + + + + + +
+
${title.replace(/\\n/g, '
')}
+
+
Details
+
${details.replace(/\\n/g, '
')}
+
+
+
+ + + + +
+ + + + `; - return [choice === 'Yes' || choice === "Yes, don't ask again", choice === "Yes, don't ask again"]; + panel.webview.onDidReceiveMessage((message) => { + if (message.command === 'answer') { + resolve(message.value); + panel.dispose(); + } + }); + }); } static showYesNoNodontAskDialog = async (message: string, acceptLabel: string): Promise<[boolean, boolean]> => { - const choice = await vscode.window.showInformationMessage( - "llama-vscode \n\n" + message, - { modal: true }, // Makes the dialog modal (blocks interaction until resolved) - acceptLabel, - 'No', - "No, don't ask again" - ); + return new Promise((resolve) => { + const panel = vscode.window.createWebviewPanel( + 'yesNoDontAsk', + 'Confirmation', + { viewColumn: vscode.ViewColumn.One, preserveFocus: true }, + { enableScripts: true } + ); + + // Split message into title and details + const parts = message.split('\n\n'); + const title = parts[0]; + const details = parts.slice(1).join('\n\n'); - return [choice === acceptLabel, choice === "No, don't ask again"]; + panel.webview.html = ` + + + + + + + + +
+
${title.replace(/\\n/g, '
')}
+ ${details ? `
+
Details
+
${details.replace(/\\n/g, '
')}
+
` : ''} +
+
+ + + + +
+ + + + `; + + panel.webview.onDidReceiveMessage((message) => { + if (message.command === 'answer') { + resolve(message.value); + panel.dispose(); + } + }); + }); } static showOkDialog = async (message: string) => { From ca88463348b566dd16a40a744b6b3d60654aa751 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Sat, 16 May 2026 09:40:17 +0200 Subject: [PATCH 7/7] fix: Correct escape sequences in WebView dialog templates - Fix \\n to \n in showYesYesdontaskNoDialog() - Fix \\n to \n in showYesNoNodontAskDialog() - Fix \\n to \n in showUserChoiceDialog() - Ensures newline characters are properly converted to HTML
tags - Fixes text truncation issues in all confirmation dialogs --- src/utils.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/utils.ts b/src/utils.ts index 4a6937d..799c53d 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -494,7 +494,7 @@ export class Utils {
-
${message.replace(/\\n/g, '
')}
+
${message.replace(/\n/g, '
')}
@@ -642,10 +642,10 @@ export class Utils {
-
${title.replace(/\\n/g, '
')}
+
${title.replace(/\n/g, '
')}
Details
-
${details.replace(/\\n/g, '
')}
+
${details.replace(/\n/g, '
')}
@@ -798,10 +798,10 @@ export class Utils {
-
${title.replace(/\\n/g, '
')}
+
${title.replace(/\n/g, '
')}
${details ? `
Details
-
${details.replace(/\\n/g, '
')}
+
${details.replace(/\n/g, '
')}
` : ''}