|
1 (function () { |
|
2 const vscode = acquireVsCodeApi(); |
|
3 let allSymbols = {}; |
|
4 let allAbbrevsFromThy = []; |
|
5 let controlSup = ""; |
|
6 let controlSub = ""; |
|
7 |
|
8 function decodeHtmlEntity(code) { |
|
9 try { |
|
10 return String.fromCodePoint(code); |
|
11 } catch (error) { |
|
12 return "?"; |
|
13 } |
|
14 } |
|
15 |
|
16 function formatGroupName(group) { |
|
17 const groupNameMap = { |
|
18 "Z_Notation": "Z Notation", |
|
19 "Control_Block": "Control Block", |
|
20 "Arrow": "Arrow", |
|
21 "Control": "Control", |
|
22 "Digit": "Digit", |
|
23 "Document": "Document", |
|
24 "Greek": "Greek", |
|
25 "Icon": "Icon", |
|
26 "Letter": "Letter", |
|
27 "Logic": "Logic", |
|
28 "Operator": "Operator", |
|
29 "Punctuation": "Punctuation", |
|
30 "Relation": "Relation", |
|
31 "Unsorted": "Unsorted", |
|
32 "Search": "Search" |
|
33 }; |
|
34 |
|
35 return groupNameMap[group] || group.replace(/_/g, " ").replace(/\b\w/g, c => c.toUpperCase()); |
|
36 } |
|
37 |
|
38 function createSearchField() { |
|
39 const searchContainer = document.createElement('div'); |
|
40 searchContainer.classList.add('search-container'); |
|
41 |
|
42 const searchInput = document.createElement('input'); |
|
43 searchInput.type = "text"; |
|
44 searchInput.classList.add('search-input'); |
|
45 |
|
46 const searchResults = document.createElement('div'); |
|
47 searchResults.classList.add('search-results'); |
|
48 |
|
49 searchInput.addEventListener('input', () => filterSymbols(searchInput.value, searchResults)); |
|
50 |
|
51 searchContainer.appendChild(searchInput); |
|
52 searchContainer.appendChild(searchResults); |
|
53 return { searchContainer, searchResults }; |
|
54 } |
|
55 |
|
56 function filterSymbols(query, resultsContainer) { |
|
57 const normalizedQuery = query.toLowerCase().trim(); |
|
58 resultsContainer.innerHTML = ''; |
|
59 |
|
60 if (normalizedQuery === "") return; |
|
61 |
|
62 const matchingSymbols = []; |
|
63 Object.values(allSymbols).forEach(symbolList => { |
|
64 symbolList.forEach(symbol => { |
|
65 if (!symbol.code) return; |
|
66 |
|
67 if ( |
|
68 symbol.symbol.toLowerCase().includes(normalizedQuery) || |
|
69 (symbol.abbrevs && symbol.abbrevs.some(abbr => abbr.toLowerCase().includes(normalizedQuery))) |
|
70 ) { |
|
71 matchingSymbols.push(symbol); |
|
72 } |
|
73 }); |
|
74 }); |
|
75 |
|
76 const searchLimit = 50; |
|
77 if (matchingSymbols.length === 0) { |
|
78 resultsContainer.innerHTML = "<p>No symbols found</p>"; |
|
79 } else { |
|
80 matchingSymbols.slice(0, searchLimit).forEach(symbol => { |
|
81 const decodedSymbol = decodeHtmlEntity(symbol.code); |
|
82 if (!decodedSymbol) return; |
|
83 |
|
84 const button = document.createElement('div'); |
|
85 button.classList.add('symbol-button'); |
|
86 button.textContent = decodedSymbol; |
|
87 button.setAttribute("data-tooltip", `${symbol.symbol}\nAbbreviations: ${symbol.abbrevs.join(', ')}`); |
|
88 |
|
89 button.addEventListener('click', () => { |
|
90 vscode.postMessage({ command: 'insertSymbol', symbol: decodedSymbol }); |
|
91 }); |
|
92 |
|
93 resultsContainer.appendChild(button); |
|
94 }); |
|
95 |
|
96 if (matchingSymbols.length > searchLimit) { |
|
97 const moreResults = document.createElement('div'); |
|
98 moreResults.classList.add('more-results'); |
|
99 moreResults.textContent = `(${matchingSymbols.length - searchLimit} more...)`; |
|
100 resultsContainer.appendChild(moreResults); |
|
101 } |
|
102 } |
|
103 } |
|
104 |
|
105 function renderWithEffects(symbol) { |
|
106 if (!symbol) return ""; |
|
107 |
|
108 let result = ""; |
|
109 let i = 0; |
|
110 while (i < symbol.length) { |
|
111 const char = symbol[i]; |
|
112 if (char === "⇧") { |
|
113 i++; |
|
114 if (i < symbol.length) result += "<sup>" + symbol[i] + "</sup>"; |
|
115 } else if (char === "⇩") { |
|
116 i++; |
|
117 if (i < symbol.length) result += "<sub>" + symbol[i] + "</sub>"; |
|
118 } else { |
|
119 result += char; |
|
120 } |
|
121 i++; |
|
122 } |
|
123 return result; |
|
124 } |
|
125 |
|
126 function convertSymbolWithEffects(symbol) { |
|
127 let result = ""; |
|
128 let i = 0; |
|
129 while (i < symbol.length) { |
|
130 const char = symbol[i]; |
|
131 if (char === "⇧") { |
|
132 i++; |
|
133 if (i < symbol.length) { |
|
134 if (controlSup) result += controlSup + symbol[i]; |
|
135 else result += symbol[i]; |
|
136 } |
|
137 } else if (char === "⇩") { |
|
138 i++; |
|
139 if (i < symbol.length) { |
|
140 if (controlSub) result += controlSub + symbol[i]; |
|
141 else result += symbol[i]; |
|
142 } |
|
143 } else { |
|
144 result += char; |
|
145 } |
|
146 i++; |
|
147 } |
|
148 return result; |
|
149 } |
|
150 |
|
151 function sanitizeSymbolForInsert(symbol) { |
|
152 return symbol.replace(/\u0007/g, ''); |
|
153 } |
|
154 |
|
155 function extractControlSymbols(symbolGroups) { |
|
156 if (!symbolGroups || !symbolGroups["control"]) return; |
|
157 symbolGroups["control"].forEach(symbol => { |
|
158 if (symbol.name === "sup") controlSup = String.fromCodePoint(symbol.code); |
|
159 if (symbol.name === "sub") controlSub = String.fromCodePoint(symbol.code); |
|
160 }); |
|
161 } |
|
162 |
|
163 function renderSymbols(groupedSymbols, abbrevsFromThy) { |
|
164 const container = document.getElementById('symbols-container'); |
|
165 container.innerHTML = ''; |
|
166 |
|
167 allSymbols = groupedSymbols; |
|
168 allAbbrevsFromThy = abbrevsFromThy || []; |
|
169 |
|
170 extractControlSymbols(groupedSymbols); |
|
171 |
|
172 vscode.setState({ symbols: groupedSymbols, abbrevs_from_Thy: allAbbrevsFromThy }); |
|
173 |
|
174 if (!groupedSymbols || Object.keys(groupedSymbols).length === 0) { |
|
175 container.innerHTML = "<p>No symbols available.</p>"; |
|
176 return; |
|
177 } |
|
178 |
|
179 const tabs = document.createElement('div'); |
|
180 tabs.classList.add('tabs'); |
|
181 |
|
182 const content = document.createElement('div'); |
|
183 content.classList.add('content'); |
|
184 |
|
185 Object.keys(groupedSymbols).forEach((group, index) => { |
|
186 const tab = document.createElement('button'); |
|
187 tab.classList.add('tab'); |
|
188 tab.textContent = formatGroupName(group); |
|
189 if (index === 0) tab.classList.add('active'); |
|
190 |
|
191 tab.addEventListener('click', () => { |
|
192 document.querySelectorAll('.tab').forEach(t => t.classList.remove('active')); |
|
193 tab.classList.add('active'); |
|
194 document.querySelectorAll('.tab-content').forEach(c => c.classList.add('hidden')); |
|
195 document.getElementById(`content-${group}`).classList.remove('hidden'); |
|
196 }); |
|
197 |
|
198 tabs.appendChild(tab); |
|
199 |
|
200 const groupContent = document.createElement('div'); |
|
201 groupContent.classList.add('tab-content'); |
|
202 groupContent.id = `content-${group}`; |
|
203 if (index !== 0) groupContent.classList.add('hidden'); |
|
204 |
|
205 if (group === "control") { |
|
206 const resetButton = document.createElement('button'); |
|
207 resetButton.classList.add('reset-button'); |
|
208 resetButton.textContent = "Reset"; |
|
209 |
|
210 |
|
211 resetButton.addEventListener('click', () => { |
|
212 vscode.postMessage({ command: 'resetControlSymbols' }); |
|
213 }); |
|
214 groupContent.appendChild(resetButton); |
|
215 |
|
216 const controlButtons = ["sup", "sub", "bold"]; |
|
217 controlButtons.forEach(action => { |
|
218 const controlSymbol = groupedSymbols[group].find(s => s.name === action); |
|
219 if (controlSymbol) { |
|
220 const decodedSymbol = decodeHtmlEntity(controlSymbol.code); |
|
221 const button = document.createElement('button'); |
|
222 button.classList.add('control-button'); |
|
223 button.textContent = decodedSymbol; |
|
224 button.title = action.charAt(0).toUpperCase() + action.slice(1); |
|
225 button.addEventListener('click', () => { |
|
226 vscode.postMessage({ command: 'applyControl', action: action }); |
|
227 }); |
|
228 groupContent.appendChild(button); |
|
229 } |
|
230 }); |
|
231 } |
|
232 |
|
233 groupedSymbols[group].forEach(symbol => { |
|
234 if (!symbol.code) return; |
|
235 if (["sup", "sub", "bold"].includes(symbol.name)) return; |
|
236 const decodedSymbol = decodeHtmlEntity(symbol.code); |
|
237 if (!decodedSymbol) return; |
|
238 |
|
239 const button = document.createElement('div'); |
|
240 if (group === "arrow") { |
|
241 button.classList.add('arrow-button'); // Spezielle Klasse für Pfeile |
|
242 } else { |
|
243 button.classList.add('symbol-button'); |
|
244 } |
|
245 button.textContent = decodedSymbol; |
|
246 button.setAttribute("data-tooltip", `${symbol.symbol}\nAbbreviations: ${symbol.abbrevs.join(', ')}`); |
|
247 |
|
248 button.addEventListener('click', () => { |
|
249 vscode.postMessage({ command: 'insertSymbol', symbol: decodedSymbol }); |
|
250 }); |
|
251 |
|
252 groupContent.appendChild(button); |
|
253 }); |
|
254 |
|
255 content.appendChild(groupContent); |
|
256 }); |
|
257 |
|
258 const abbrevsTab = document.createElement('button'); |
|
259 abbrevsTab.classList.add('tab'); |
|
260 abbrevsTab.textContent = "Abbrevs"; |
|
261 abbrevsTab.addEventListener('click', () => { |
|
262 document.querySelectorAll('.tab').forEach(t => t.classList.remove('active')); |
|
263 abbrevsTab.classList.add('active'); |
|
264 document.querySelectorAll('.tab-content').forEach(c => c.classList.add('hidden')); |
|
265 document.getElementById("abbrevs-tab-content").classList.remove('hidden'); |
|
266 }); |
|
267 tabs.appendChild(abbrevsTab); |
|
268 |
|
269 const abbrevsContent = document.createElement('div'); |
|
270 abbrevsContent.classList.add('tab-content', 'hidden'); |
|
271 abbrevsContent.id = "abbrevs-tab-content"; |
|
272 |
|
273 allAbbrevsFromThy |
|
274 .filter(([abbr, symbol]) => symbol && symbol.trim() !== "" && !/^[a-zA-Z0-9 _-]+$/.test(symbol)) |
|
275 .forEach(([abbr, symbol]) => { |
|
276 const btn = document.createElement('div'); |
|
277 btn.classList.add('abbrevs-button'); |
|
278 btn.innerHTML = renderWithEffects(symbol); |
|
279 btn.title = abbr; |
|
280 btn.addEventListener('click', () => { |
|
281 vscode.postMessage({ command: 'insertSymbol', symbol: convertSymbolWithEffects(sanitizeSymbolForInsert(symbol)) }); |
|
282 }); |
|
283 |
|
284 abbrevsContent.appendChild(btn); |
|
285 }); |
|
286 |
|
287 content.appendChild(abbrevsContent); |
|
288 |
|
289 const searchTab = document.createElement('button'); |
|
290 searchTab.classList.add('tab'); |
|
291 searchTab.textContent = "Search"; |
|
292 searchTab.addEventListener('click', () => { |
|
293 document.querySelectorAll('.tab').forEach(t => t.classList.remove('active')); |
|
294 searchTab.classList.add('active'); |
|
295 document.querySelectorAll('.tab-content').forEach(c => c.classList.add('hidden')); |
|
296 document.getElementById("search-tab-content").classList.remove('hidden'); |
|
297 }); |
|
298 tabs.appendChild(searchTab); |
|
299 |
|
300 const { searchContainer, searchResults } = createSearchField(); |
|
301 const searchContent = document.createElement('div'); |
|
302 searchContent.classList.add('tab-content', 'hidden'); |
|
303 searchContent.id = "search-tab-content"; |
|
304 searchContent.appendChild(searchContainer); |
|
305 |
|
306 content.appendChild(searchContent); |
|
307 container.appendChild(tabs); |
|
308 container.appendChild(content); |
|
309 |
|
310 const tooltip = document.createElement("div"); |
|
311 tooltip.className = "tooltip"; |
|
312 document.body.appendChild(tooltip); |
|
313 |
|
314 document.querySelectorAll(".symbol-button, .arrow-button, .abbrevs-button").forEach(button => { |
|
315 button.addEventListener("mouseenter", (e) => { |
|
316 const rect = button.getBoundingClientRect(); |
|
317 const text = button.getAttribute("data-tooltip"); |
|
318 |
|
319 if (text) { |
|
320 tooltip.textContent = text; |
|
321 tooltip.style.left = `${rect.left + window.scrollX}px`; |
|
322 tooltip.style.top = `${rect.bottom + 6 + window.scrollY}px`; |
|
323 tooltip.classList.add("visible"); |
|
324 } |
|
325 }); |
|
326 |
|
327 button.addEventListener("mouseleave", () => { |
|
328 tooltip.classList.remove("visible"); |
|
329 tooltip.textContent = ""; |
|
330 }); |
|
331 }); |
|
332 |
|
333 } |
|
334 |
|
335 window.addEventListener('message', event => { |
|
336 if (event.data.command === 'update' && event.data.symbols) { |
|
337 renderSymbols(event.data.symbols, event.data.abbrevs_from_Thy); |
|
338 } |
|
339 }); |
|
340 |
|
341 window.onload = function () { |
|
342 const savedState = vscode.getState(); |
|
343 if (savedState && savedState.symbols) { |
|
344 renderSymbols(savedState.symbols, savedState.abbrevs_from_Thy); |
|
345 } |
|
346 }; |
|
347 |
|
348 })(); |