src/Tools/VSCode/extension/media/symbols.js
changeset 83363 486e094b676c
child 83368 5ab0fc66e827
equal deleted inserted replaced
83344:3de18e94ac7c 83363:486e094b676c
       
     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 })();