1export class HTMLBuilder {
2 static createBaseHTML(): string {
3 const faviconData = this.getFaviconDataURL();
4 return `
5<!DOCTYPE html>
6<html lang="en">
7<head>
8 <meta charset="UTF-8">
9 <meta name="viewport" content="width=device-width, initial-scale=1.0">
10 <title>Dumber Browser</title>
11 <link rel="icon" type="image/svg+xml" href="${faviconData}">
12 <style>
13 ${this.getStyles()}
14 </style>
15</head>
16<body>
17 <div class="container">
18 <div class="history-section">
19 <h2 class="section-title">Recent History</h2>
20 <div id="historyList" class="history-list">
21 <div class="loading">Loading history...</div>
22 </div>
23 </div>
24
25 <div class="shortcuts-section">
26 <h2 class="section-title">Search Shortcuts</h2>
27 <div id="shortcuts" class="shortcuts-grid">
28 <div class="loading">Loading shortcuts...</div>
29 </div>
30 </div>
31 </div>
32 <script type="module" src="./main.js"></script>
33</body>
34</html>`;
35 }
36
37 private static getFaviconDataURL(): string {
38 const svg = HTMLBuilder.getFaviconSVG();
39 // Minimal encoding for safe inline usage
40 const encoded = encodeURIComponent(svg)
41 .replace(/'/g, '%27')
42 .replace(/\(/g, '%28')
43 .replace(/\)/g, '%29');
44 return `data:image/svg+xml;utf8,${encoded}`;
45 }
46
47 static getFaviconSVG(): string {
48 // Stylized D with speed bolt on a subtle dark rounded card
49 return `<?xml version="1.0" encoding="UTF-8"?>
50<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
51 <defs>
52 <linearGradient id="bg" x1="0" y1="0" x2="1" y2="1">
53 <stop offset="0%" stop-color="#121212"/>
54 <stop offset="100%" stop-color="#1e1e1e"/>
55 </linearGradient>
56 <filter id="inset" x="-20%" y="-20%" width="140%" height="140%">
57 <feOffset dx="0" dy="1"/>
58 <feGaussianBlur stdDeviation="0.6" result="blur"/>
59 <feComposite in2="SourceAlpha" operator="out"/>
60 <feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.5 0"/>
61 <feBlend in="SourceGraphic" mode="normal"/>
62 </filter>
63 </defs>
64 <rect x="4" y="4" width="56" height="56" rx="14" ry="14" fill="url(#bg)" filter="url(#inset)"/>
65 <rect x="4.5" y="4.5" width="55" height="55" rx="13.5" ry="13.5" fill="none" stroke="#252a2f" stroke-width="1"/>
66 <!-- D glyph (slightly larger and wider) -->
67 <path d="M20 16 L20 48 M20 16 H34 q12 0 12 16 t-12 16 H20" fill="none" stroke="#e9eef3" stroke-width="4.5" stroke-linecap="round" stroke-linejoin="round" transform="translate(33,32) scale(1.12,1.10) translate(-33,-32)"/>
68 <!-- Speed bolt accent (significantly larger, centered) -->
69 <path d="M36 22 L32 34 h6 l-8 16 4-12 h-6 z" fill="#ff9800" opacity="0.95" transform="translate(33,32) scale(1.8) translate(-33,-32) translate(0,-3)"/>
70</svg>`;
71 }
72
73 private static getStyles(): string {
74 return `
75 * {
76 margin: 0;
77 padding: 0;
78 box-sizing: border-box;
79 }
80
81 body {
82 font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
83 background: #1a1a1a;
84 color: #ffffff;
85 height: 100vh;
86 display: flex;
87 flex-direction: column;
88 overflow-x: hidden; /* prevent horizontal scroll from long content */
89 }
90
91 .container {
92 flex: 1;
93 display: flex;
94 max-width: 1200px;
95 margin: 0 auto;
96 width: 100%;
97 padding: 2rem;
98 gap: 2rem;
99 overflow-x: hidden;
100 }
101
102 .history-section {
103 flex: 1;
104 min-height: 0;
105 min-width: 0; /* allow flex child to shrink */
106 }
107
108 .shortcuts-section {
109 flex: 1;
110 min-height: 0;
111 min-width: 0; /* allow flex child to shrink */
112 }
113
114 .section-title {
115 font-size: 1.5rem;
116 margin-bottom: 1rem;
117 color: #ffffff;
118 border-bottom: 2px solid #404040;
119 padding-bottom: 0.5rem;
120 }
121
122 .history-list {
123 overflow-y: auto;
124 overflow-x: hidden;
125 max-height: calc(100vh - 8rem);
126 max-width: 100%;
127 }
128
129 .history-item {
130 padding: 0.75rem;
131 margin-bottom: 0.5rem;
132 background: #2d2d2d;
133 border-radius: 6px;
134 cursor: pointer;
135 transition: background 0.2s;
136 border-left: 3px solid #404040;
137 overflow: hidden;
138 max-width: 100%;
139 }
140
141 .history-item:hover {
142 background: #3d3d3d;
143 border-left-color: #0066cc;
144 }
145
146 .history-line {
147 display: flex;
148 gap: 0.5rem;
149 white-space: nowrap;
150 align-items: center;
151 overflow: hidden;
152 width: 100%;
153 min-width: 0; /* allow flex children to shrink */
154 }
155 .history-favicon-chip {
156 flex: 0 0 20px;
157 width: 20px; height: 20px;
158 border-radius: 50%;
159 background: #ccc;
160 border: 1px solid rgba(0,0,0,.12);
161 box-shadow: 0 1px 2px rgba(0,0,0,.12);
162 display: flex;
163 align-items: center;
164 justify-content: center;
165 }
166 .history-favicon-img {
167 width: 16px; height: 16px;
168 filter: brightness(1.06) contrast(1.03);
169 image-rendering: -webkit-optimize-contrast;
170 }
171
172 .history-title { font-size: 0.95rem; color: #e6e6e6; flex: 0 0 auto; }
173 .history-domain { font-size: 0.9rem; color: #a5a5a5; flex: 0 0 auto; }
174 .history-sep { color: #666; flex: 0 0 auto; }
175 .history-url {
176 font-size: 0.9rem;
177 color: #7a7a7a; /* darker than domain */
178 flex: 1 1 auto;
179 min-width: 0; /* critical for flex truncation */
180 overflow: hidden;
181 text-overflow: ellipsis; /* fallback */
182 -webkit-mask-image: linear-gradient(to right, rgba(0,0,0,1) 85%, rgba(0,0,0,0) 100%);
183 mask-image: linear-gradient(to right, rgba(0,0,0,1) 85%, rgba(0,0,0,0) 100%);
184 }
185
186 .shortcuts-grid {
187 display: grid;
188 grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
189 gap: 1rem;
190 max-height: calc(100vh - 8rem);
191 overflow-y: auto;
192 }
193
194 .shortcut {
195 padding: 1rem;
196 background: #2d2d2d;
197 border: 2px solid #404040;
198 border-radius: 8px;
199 cursor: pointer;
200 transition: all 0.2s;
201 text-align: center;
202 }
203
204 .shortcut:hover {
205 background: #3d3d3d;
206 border-color: #0066cc;
207 transform: translateY(-2px);
208 }
209
210 .shortcut-key {
211 font-weight: bold;
212 color: #0066cc;
213 margin-bottom: 0.5rem;
214 font-size: 1.1rem;
215 }
216
217 .shortcut-desc {
218 color: #888;
219 font-size: 0.9rem;
220 }
221
222 .loading {
223 padding: 2rem;
224 text-align: center;
225 color: #888;
226 }
227
228 .empty-state {
229 padding: 3rem 2rem;
230 text-align: center;
231 color: #666;
232 }
233
234 .empty-state h3 {
235 margin-bottom: 1rem;
236 color: #888;
237 }
238
239 @media (max-width: 768px) {
240 .container {
241 flex-direction: column;
242 padding: 1rem;
243 }
244
245 .shortcuts-grid {
246 grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
247 }
248 }
249 `;
250 }
251}