|
| 1 | +/* |
| 2 | + * Copyright 2026, Salesforce, Inc. |
| 3 | + * |
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | + * you may not use this file except in compliance with the License. |
| 6 | + * You may obtain a copy of the License at |
| 7 | + * |
| 8 | + * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | + * |
| 10 | + * Unless required by applicable law or agreed to in writing, software |
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | + * See the License for the specific language governing permissions and |
| 14 | + * limitations under the License. |
| 15 | + */ |
| 16 | + |
| 17 | +/** |
| 18 | + * HTML template for dev server error pages. |
| 19 | + * Placeholders: {{PAGE_TITLE}}, {{META_REFRESH}}, {{ERROR_TITLE}}, {{STATUS_CLASS}}, |
| 20 | + * {{ERROR_STATUS}}, {{MESSAGE_CONTENT}}, {{ERROR_MESSAGE_TEXT}}, {{STDERR_OUTPUT}}, |
| 21 | + * {{SUGGESTIONS_TITLE}}, {{SUGGESTIONS_LIST}}, {{DEV_SERVER_URL}}, {{PROXY_URL}}, |
| 22 | + * {{PROXY_PORT}}, {{ORG_TARGET}}, {{WORKSPACE_SCRIPT}}, {{LAST_CHECK_TIME}}, |
| 23 | + * {{SIMPLE_SECTION_CLASS}}, {{RUNTIME_SECTION_CLASS}}, {{DEV_SERVER_SECTION_CLASS}}, |
| 24 | + * {{SUGGESTIONS_SECTION_CLASS}}, {{AUTO_REFRESH_CLASS}}, {{AUTO_REFRESH_TEXT}} |
| 25 | + */ |
| 26 | +export const ERROR_PAGE_TEMPLATE = `<!DOCTYPE html> |
| 27 | +<html lang="en"> |
| 28 | + <head> |
| 29 | + <meta charset="UTF-8" /> |
| 30 | + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
| 31 | + <title>{{PAGE_TITLE}} - Salesforce Local Dev Proxy</title> |
| 32 | + {{META_REFRESH}} |
| 33 | + <style> |
| 34 | + * { margin: 0; padding: 0; box-sizing: border-box; } |
| 35 | + body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: linear-gradient(135deg, #1e2a47 0%, #2d3e5f 100%); color: #333; min-height: 100vh; padding: 20px; } |
| 36 | + .main-container { max-width: 1400px; margin: 0 auto; background: #e8e8e8; border-radius: 16px; box-shadow: 0 10px 40px rgba(0,0,0,0.3); overflow: hidden; } |
| 37 | + .top-header { background: #e8e8e8; padding: 30px 40px; display: flex; justify-content: space-between; align-items: flex-start; border-bottom: 1px solid #ccc; } |
| 38 | + .header-left h1 { color: #1a1a1a; font-size: 2.2em; font-weight: 600; margin-bottom: 8px; } |
| 39 | + .header-left .subtitle { color: #666; font-size: 1.05em; } |
| 40 | + .status-badge { padding: 12px 24px; border-radius: 8px; font-weight: 600; font-size: 0.95em; } |
| 41 | + .status-badge.warning { background: #fff3e0; color: #e65100; } |
| 42 | + .status-badge.error { background: #ffebee; color: #c62828; } |
| 43 | + .content-wrapper { display: grid; grid-template-columns: 1fr 308px; gap: 0; background: #e8e8e8; } |
| 44 | + .main-content { padding: 40px; background: white; border-right: 1px solid #ccc; } |
| 45 | + .diagnostics-panel { padding: 40px 20px; background: #f5f5f5; } |
| 46 | + .content-section { margin-bottom: 30px; } |
| 47 | + .content-section h2 { color: #1a1a1a; font-size: 1.4em; margin-bottom: 15px; font-weight: 600; } |
| 48 | + .content-section h3 { color: #1a1a1a; font-size: 1.1em; margin-bottom: 12px; font-weight: 600; } |
| 49 | + .content-section p { color: #555; line-height: 1.6; margin-bottom: 15px; } |
| 50 | + .content-section ul { list-style: disc; padding-left: 25px; color: #555; line-height: 1.8; } |
| 51 | + .message-box { background: #fff3e0; border-left: 4px solid #ff9800; padding: 20px; border-radius: 6px; margin-bottom: 25px; } |
| 52 | + .message-box p { color: #555; margin: 0; } |
| 53 | + .code-output { background: #1e1e1e; border-radius: 8px; padding: 20px; max-height: 400px; overflow-y: auto; font-family: monospace; font-size: 0.9em; line-height: 1.5; margin: 20px 0; } |
| 54 | + .code-output pre { color: #ff6b6b; margin: 0; white-space: pre-wrap; word-wrap: break-word; } |
| 55 | + .suggestions-box { background: #e3f2fd; border-left: 4px solid #2196f3; padding: 20px; border-radius: 6px; margin: 20px 0; } |
| 56 | + .suggestions-box h3 { color: #1a1a1a; font-size: 1.1em; margin-bottom: 15px; } |
| 57 | + .suggestions-box ul { list-style: none; padding: 0; } |
| 58 | + .suggestions-box li { padding: 8px 0; color: #555; line-height: 1.6; } |
| 59 | + .diagnostics-panel h2 { color: #1a1a1a; font-size: 1.15em; margin-bottom: 18px; font-weight: 600; } |
| 60 | + .diagnostics-list { list-style: none; padding: 0; margin-bottom: 20px; } |
| 61 | + .diagnostics-list li { margin-bottom: 12px; color: #555; line-height: 1.4; font-size: 0.9em; } |
| 62 | + .diagnostics-list .label { display: block; font-weight: 600; color: #1a1a1a; margin-bottom: 3px; font-size: 0.85em; } |
| 63 | + .diagnostics-list .value { font-family: monospace; font-size: 0.85em; color: #666; word-break: break-word; } |
| 64 | + .diagnostics-list .value code { background: #263238; color: #aed581; padding: 2px 5px; border-radius: 3px; font-size: 0.8em; } |
| 65 | + .emergency-commands { background: #fff3cd; border: 1px solid #ffc107; border-radius: 4px; padding: 15px; margin-top: 20px; } |
| 66 | + .emergency-commands h3 { color: #856404; font-size: 0.8em; margin: 0 0 8px 0; font-weight: 600; } |
| 67 | + .emergency-commands p { color: #856404; font-size: 0.75em; margin: 0 0 8px 0; } |
| 68 | + .command-box { background: #263238; color: #aed581; padding: 10px; border-radius: 4px; font-family: monospace; font-size: 0.72em; word-break: break-all; } |
| 69 | + .footer-section { background: #e8e8e8; padding: 20px 40px; border-top: 1px solid #ccc; text-align: center; } |
| 70 | + .help-text { color: #666; font-size: 0.9em; line-height: 1.5; } |
| 71 | + .help-text strong { color: #333; } |
| 72 | + .hidden { display: none; } |
| 73 | + .auto-refresh-indicator { background: #e8f5e9; border-left: 4px solid #4caf50; padding: 12px 16px; border-radius: 6px; margin: 20px 0; color: #2e7d32; font-size: 0.9em; } |
| 74 | + @media (max-width: 900px) { .content-wrapper { grid-template-columns: 1fr; } .main-content { border-right: none; border-bottom: 1px solid #ccc; } } |
| 75 | + </style> |
| 76 | + </head> |
| 77 | + <body> |
| 78 | + <div class="main-container"> |
| 79 | + <div class="top-header"> |
| 80 | + <div class="header-left"> |
| 81 | + <h1>Local Dev Proxy</h1> |
| 82 | + <p class="subtitle">Salesforce preview → Proxy → Your dev server</p> |
| 83 | + </div> |
| 84 | + <div class="status-badge {{STATUS_CLASS}}">{{ERROR_STATUS}}</div> |
| 85 | + </div> |
| 86 | + <div class="content-wrapper"> |
| 87 | + <div class="main-content"> |
| 88 | + <div id="simple-error-section" class="{{SIMPLE_SECTION_CLASS}}"> |
| 89 | + <div class="content-section"> |
| 90 | + <h2>{{ERROR_TITLE}}</h2> |
| 91 | + {{MESSAGE_CONTENT}} |
| 92 | + </div> |
| 93 | + <div class="auto-refresh-indicator {{AUTO_REFRESH_CLASS}}">{{AUTO_REFRESH_TEXT}}</div> |
| 94 | + <div class="suggestions-box"> |
| 95 | + <h3>What to do next</h3> |
| 96 | + <ul> |
| 97 | + <li>Start your dev server using: <code>npm run dev</code> or <code>yarn dev</code></li> |
| 98 | + <li>Verify your dev server is running on the correct port</li> |
| 99 | + <li>Check webapplication.json for the correct dev server URL</li> |
| 100 | + <li>This page will auto-refresh when the server is detected</li> |
| 101 | + </ul> |
| 102 | + </div> |
| 103 | + </div> |
| 104 | + <div id="runtime-error-section" class="{{RUNTIME_SECTION_CLASS}}"></div> |
| 105 | + <div id="dev-server-error-section" class="{{DEV_SERVER_SECTION_CLASS}}"> |
| 106 | + <div class="content-section"> |
| 107 | + <h2>⚠️ {{ERROR_TITLE}}</h2> |
| 108 | + <div class="message-box"> |
| 109 | + <p>{{ERROR_MESSAGE_TEXT}}</p> |
| 110 | + </div> |
| 111 | + </div> |
| 112 | + <div class="content-section"> |
| 113 | + <h3>Error Output</h3> |
| 114 | + <div class="code-output"> |
| 115 | + <pre>{{STDERR_OUTPUT}}</pre> |
| 116 | + </div> |
| 117 | + </div> |
| 118 | + <div class="auto-refresh-indicator {{AUTO_REFRESH_CLASS}}">{{AUTO_REFRESH_TEXT}}</div> |
| 119 | + <div class="suggestions-box {{SUGGESTIONS_SECTION_CLASS}}"> |
| 120 | + <h3>{{SUGGESTIONS_TITLE}}</h3> |
| 121 | + <ul> |
| 122 | + {{SUGGESTIONS_LIST}} |
| 123 | + </ul> |
| 124 | + </div> |
| 125 | + </div> |
| 126 | + </div> |
| 127 | + <div class="diagnostics-panel"> |
| 128 | + <h2>Diagnostics</h2> |
| 129 | + <ul class="diagnostics-list"> |
| 130 | + <li class="{{SIMPLE_SECTION_CLASS}}"><span class="label">Dev Server URL:</span><span class="value"><code>{{DEV_SERVER_URL}}</code></span></li> |
| 131 | + <li class="{{SIMPLE_SECTION_CLASS}}"><span class="label">Proxy URL:</span><span class="value"><code>{{PROXY_URL}}</code></span></li> |
| 132 | + <li class="{{SIMPLE_SECTION_CLASS}}"><span class="label">Workspace Script:</span><span class="value"><code>{{WORKSPACE_SCRIPT}}</code></span></li> |
| 133 | + <li class="{{SIMPLE_SECTION_CLASS}}"><span class="label">Target Org:</span><span class="value">{{ORG_TARGET}}</span></li> |
| 134 | + <li class="{{SIMPLE_SECTION_CLASS}}"><span class="label">Last Check:</span><span class="value">{{LAST_CHECK_TIME}}</span></li> |
| 135 | + </ul> |
| 136 | + <div class="emergency-commands"> |
| 137 | + <h3>⚠️ If Ctrl+C doesn't work</h3> |
| 138 | + <p>Copy and run this command in a new terminal to force-stop the proxy:</p> |
| 139 | + <div class="command-box">lsof -ti:{{PROXY_PORT}} | xargs kill -9</div> |
| 140 | + </div> |
| 141 | + </div> |
| 142 | + </div> |
| 143 | + <div class="footer-section"> |
| 144 | + <p class="help-text">Salesforce Web App Development Proxy • Press <strong>Ctrl+C</strong> in the terminal to stop the proxy</p> |
| 145 | + </div> |
| 146 | + </div> |
| 147 | + </body> |
| 148 | +</html>`; |
0 commit comments