merge: upgrade to upstream v0.50.95 + keep custom additions

Upstream v0.50.95 features merged (Russian localization, slash commands,
mic toggle fix, gateway sync fix, KaTeX/Prism.js, etc.)

Custom additions preserved:
- Tier-2 agent switching commands in commands.js
- MC panel in index.html + MC CSS
- _resolve_cli_toolsets() in config.py
- Custom routes.py, server.py, boot.js, i18n.js, messages.js, workspace.js

Files with conflict resolution (took upstream, custom code in other files):
- CHANGELOG.md, config.py, commands.js, index.html, panels.js, style.css, ui.js
This commit is contained in:
Rose
2026-04-19 10:06:28 +02:00
parent 067d96bb30
commit 3bdf430413
12 changed files with 1736 additions and 2361 deletions

View File

@@ -200,6 +200,7 @@ async function openFile(path){
$('previewPathText').textContent=path;
$('previewArea').classList.add('visible');
$('fileTree').style.display='none';
const wsSearch=$('wsSearchWrap');if(wsSearch)wsSearch.style.display='none';
_previewCurrentPath = path;
renderFileBreadcrumb(path);
@@ -229,7 +230,27 @@ async function openFile(path){
return;
}
showPreview('code');
$('previewCode').textContent=data.content;
// Apply syntax highlighting based on file extension
const content = data.content || '';
if(['yml','yaml'].includes(ext)){
$('previewCode').className='preview-code hl-yaml';
if(typeof highlightYAML==='function'){
$('previewCode').innerHTML=highlightYAML(content);
}else{
$('previewCode').innerHTML=_highlightWithLineNumbers(content);
}
}else if(ext==='json'){
$('previewCode').className='preview-code hl-json';
$('previewCode').innerHTML=_highlightJSON(content);
}else if(['py','js','ts','sh','bash','zsh','rb','go','rs','java','c','cpp','h','css','scss','html','xml','sql','r','lua','pl','php','swift','kt','dart'].includes(ext)){
$('previewCode').className='preview-code';
$('previewCode').textContent=content;
requestAnimationFrame(()=>{if(typeof highlightCode==='function')highlightCode();});
}else{
// txt, toml, cfg, ini, conf, env, log, etc — readable with line numbers
$('previewCode').className='preview-code hl-text';
$('previewCode').innerHTML=_highlightWithLineNumbers(content);
}
}catch(e){
// If it's a 400/too-large error, offer download instead
downloadFile(path);
@@ -287,3 +308,131 @@ function renderFileBreadcrumb(filePath) {
bar.appendChild(seg);
}
}
// ── Syntax highlighting helpers for file preview ──
function _escHtml(s){return s.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');}
function _highlightWithLineNumbers(text){
return text.split('\n').map(line=>'<span class="code-line">'+_escHtml(line)+'</span>').join('\n');
}
function _highlightJSON(text){
try{
const pretty=JSON.stringify(JSON.parse(text),null,2);
return pretty.split('\n').map(raw=>{
let line=_escHtml(raw);
// Highlight keys
line=line.replace(/^(\s*)(&quot;)([\w\s.\/\-_@:]+)(&quot;)(\s*:)/,'$1<span class="hl-key">$2$3$4</span><span class="hl-value">$5</span>');
// Highlight string values (after colon)
line=line.replace(/(:\s*)(&quot;[^&]*?&quot;)/g,'$1<span class="hl-string">$2</span>');
// Highlight numbers
line=line.replace(/:\s*(\d+\.?\d*)/g,': <span class="hl-number">$1</span>');
// Highlight booleans / null
line=line.replace(/:\s*(true|false|null)/g,': <span class="hl-bool">$1</span>');
return '<span class="code-line">'+line+'</span>';
}).join('\n');
}catch(e){
return _highlightWithLineNumbers(text);
}
}
// ── Workspace file search (server-side recursive) ──
let _wsSearchTimer=null;
function filterWsFiles(){
// Debounce: wait 300ms after last keystroke
clearTimeout(_wsSearchTimer);
_wsSearchTimer=setTimeout(_doWsSearch,300);
}
async function _doWsSearch(){
const input=$('wsSearchInput');
const clearBtn=$('wsSearchClear');
const tree=$('fileTree');
if(!input||!tree)return;
const query=input.value.trim().toLowerCase();
if(clearBtn)clearBtn.classList.toggle('visible',query.length>0);
// Remove any stale "no results" message
const oldNoRes=tree.querySelector('.ws-no-results');
if(oldNoRes)oldNoRes.remove();
// If empty query, restore original file tree
if(!query){
if(typeof renderFileTree==='function')renderFileTree();
return;
}
// Not searchable without a workspace
if(!S.session||!S.session.workspace)return;
// Show loading indicator
tree.innerHTML='<div class="ws-no-results" style="opacity:.5">Suche...</div>';
try{
// Ask server to search recursively
const data=await api(`/api/list?session_id=${encodeURIComponent(S.session.session_id)}&path=.&search=${encodeURIComponent(query)}`);
const results=data.entries||[];
if(!results.length){
tree.innerHTML='<div class="ws-no-results">Keine Dateien gefunden</div>';
return;
}
// Render flat result list with path info
tree.innerHTML='';
for(const item of results){
const el=document.createElement('div');
el.className='file-item';
el.style.paddingLeft='10px';
const iconEl=document.createElement('span');
iconEl.className='file-icon';
iconEl.innerHTML=fileIcon(item.name,item.type);
el.appendChild(iconEl);
const nameEl=document.createElement('span');
nameEl.className='file-name';
nameEl.textContent=item.name;
el.appendChild(nameEl);
// Show relative path as hint
if(item.path){
const pathEl=document.createElement('span');
pathEl.className='file-size';
pathEl.style.opacity='.4';
const dir=item.path.substring(0,item.path.lastIndexOf('/'));
pathEl.textContent=dir||'.';
el.appendChild(pathEl);
}
if(item.type==='dir'){
el.onclick=()=>{clearWsSearch();loadDir(item.path);};
}else{
el.onclick=()=>openFile(item.path);
}
tree.appendChild(el);
}
}catch(e){
// Fallback: client-side filter on currently visible items
tree.innerHTML='';
const allItems=S.entries||[];
const matches=allItems.filter(it=>it.name.toLowerCase().includes(query));
if(!matches.length){
tree.innerHTML='<div class="ws-no-results">Keine Dateien gefunden</div>';
}else{
for(const item of matches){
const el=document.createElement('div');
el.className='file-item';el.style.paddingLeft='10px';
el.innerHTML='<span class="file-icon">'+fileIcon(item.name,item.type)+'</span><span class="file-name">'+item.name+'</span>';
el.onclick=item.type==='dir'?()=>{clearWsSearch();loadDir(item.path);}:()=>openFile(item.path);
tree.appendChild(el);
}
}
}
}
// Toggle workspace search visibility
function toggleWsSearch(){
const wrap=$('wsSearchWrap');
if(!wrap)return;
// Show/hide the search bar
wrap.style.display=wrap.style.display==='none'?'flex':'none';
// Focus input when showing
setTimeout(()=>{if(wrap.style.display!=='none'){const inp=$('wsSearchInput');if(inp)inp.focus();}},50);
}
function clearWsSearch(){
const input=$('wsSearchInput');
if(input)input.value='';
const clearBtn=$('wsSearchClear');
if(clearBtn)clearBtn.classList.remove('visible');
if(typeof renderFileTree==='function')renderFileTree();
}