Upload via GUI (298 Dateien)
This commit is contained in:
241
bundle.js
241
bundle.js
@@ -39,7 +39,7 @@ const aboutBody =
|
||||
'const o=y("p");o.innerHTML=`Git: ${a.link().outerHTML}`,' +
|
||||
'e.appendChild(y("div",[y("div|class:version",[_t.render(),' +
|
||||
'y("div|class:version-details",[y("h1:MyStart|class:version-app-name"),' +
|
||||
'y("p:Version 1.0.1|class:version-number")])]),y("hr"),s,o,y("div|id:mystart-autostart-row,class:mystart-autostart-row")]))';
|
||||
'y("p:Version 1.0.2|class:version-number")])]),y("hr"),s,o,y("div|id:mystart-autostart-row,class:mystart-autostart-row")]))';
|
||||
mainJs = mainJs.slice(0, i) + aboutBody + mainJs.slice(j + aEndMarker.length);
|
||||
|
||||
// --- 2) Rename branding (visible text + storage keys) consistently ---
|
||||
@@ -188,13 +188,13 @@ const windowControlsJs = `(function(){
|
||||
}
|
||||
document.addEventListener('mousemove',function(ev){
|
||||
if(ev.buttons)return; // an active drag (e.g. scrollbar) is in progress -> leave it alone
|
||||
var onCtrls=ev.target.closest&&ev.target.closest('#mystart-winctrls');
|
||||
var onCtrls=ev.target.closest&&ev.target.closest('#mystart-winctrls,#mystart-profiles');
|
||||
var edge=onCtrls?null:edgeAt(ev.clientX,ev.clientY);
|
||||
document.documentElement.style.cursor=edge?CURSOR[edge]:'';
|
||||
},true);
|
||||
document.addEventListener('mousedown',function(ev){
|
||||
if(ev.button!==0)return;
|
||||
if(ev.target.closest&&ev.target.closest('#mystart-winctrls'))return;
|
||||
if(ev.target.closest&&ev.target.closest('#mystart-winctrls,#mystart-profiles'))return;
|
||||
var edge=edgeAt(ev.clientX,ev.clientY);
|
||||
if(edge){ev.preventDefault();send2('window-resize',{edge:edge});}
|
||||
},true);
|
||||
@@ -202,7 +202,240 @@ const windowControlsJs = `(function(){
|
||||
function send2(t,extra){try{window.chrome.webview.postMessage(Object.assign({type:t},extra));}catch(e){}}
|
||||
})();`;
|
||||
|
||||
mainJs = trInfra + autostartJs + windowControlsJs + mainJs;
|
||||
// Optional, self-named data "Reiter" (tabs) in the top-left corner -- e.g.
|
||||
// one for the office, one for a second workplace. Opt-in: with none created,
|
||||
// only a small unobtrusive "+" shows -- nothing changes for anyone who
|
||||
// doesn't want this. nightTab keeps its ENTIRE state (bookmarks, groups,
|
||||
// theme, layout, ...) in a single localStorage key (the app name, here
|
||||
// "MyStart"); switching swaps that key's content with a saved per-tab slot
|
||||
// and reloads. Purely local to this install, no server/sync involved.
|
||||
const profilesJs = `(function(){
|
||||
var KEY='${APPNAME}';
|
||||
var ACTIVE=KEY+'-slot-active';
|
||||
var LIST=KEY+'-profiles';
|
||||
function slotKey(id){return KEY+'-slot-'+id;}
|
||||
function getProfiles(){try{var a=JSON.parse(localStorage.getItem(LIST));return Array.isArray(a)?a:[];}catch(e){return[];}}
|
||||
function saveProfiles(a){localStorage.setItem(LIST,JSON.stringify(a));}
|
||||
function getActive(){return localStorage.getItem(ACTIVE);}
|
||||
function newId(){return 'p'+Date.now().toString(36)+Math.random().toString(36).slice(2,6);}
|
||||
// Users of the earlier fixed "Büro"/"Extern" build had an old-format
|
||||
// marker (ACTIVE='a' or 'b', no profiles list). Turn that into the new
|
||||
// list format once, in place, without moving any data around.
|
||||
function migrateLegacy(){
|
||||
if(localStorage.getItem(LIST)!=null)return;
|
||||
var legacyActive=localStorage.getItem(ACTIVE);
|
||||
if(legacyActive==null)return;
|
||||
var profiles=[];
|
||||
['a','b'].forEach(function(s){
|
||||
if(localStorage.getItem(slotKey(s))!=null||legacyActive===s){
|
||||
var label=localStorage.getItem(KEY+'-slot-'+s+'-label')||(s==='a'?'Büro':'Extern');
|
||||
profiles.push({id:s,label:label});
|
||||
}
|
||||
});
|
||||
if(profiles.length)saveProfiles(profiles);else localStorage.removeItem(ACTIVE);
|
||||
}
|
||||
function loadDataInto(json){
|
||||
if(json!=null){
|
||||
localStorage.setItem(KEY,json);
|
||||
// Pre-set the flash-prevention theme flag so reload doesn't briefly
|
||||
// flash the previous tab's background colour.
|
||||
try{
|
||||
var style=JSON.parse(json).state.theme.style;
|
||||
if(style==='dark'||style==='light')localStorage.setItem(KEY+'Style',style);
|
||||
else localStorage.removeItem(KEY+'Style');
|
||||
}catch(e){}
|
||||
}else{
|
||||
localStorage.removeItem(KEY);
|
||||
localStorage.removeItem(KEY+'Style');
|
||||
}
|
||||
}
|
||||
function switchTo(id){
|
||||
var active=getActive();
|
||||
if(id===active)return;
|
||||
if(active!=null){
|
||||
var live=localStorage.getItem(KEY);
|
||||
if(live!=null)localStorage.setItem(slotKey(active),live);else localStorage.removeItem(slotKey(active));
|
||||
}
|
||||
loadDataInto(localStorage.getItem(slotKey(id)));
|
||||
localStorage.setItem(ACTIVE,id);
|
||||
location.reload();
|
||||
}
|
||||
function addProfile(){
|
||||
var name=window.prompt('Name für den neuen Reiter:','');
|
||||
if(!name||!name.trim())return;
|
||||
name=name.trim();
|
||||
var profiles=getProfiles();
|
||||
var id=newId();
|
||||
if(profiles.length===0&&getActive()==null){
|
||||
// First tab ever: adopt whatever is currently loaded, nothing lost, no reload needed.
|
||||
var current=localStorage.getItem(KEY);
|
||||
if(current!=null)localStorage.setItem(slotKey(id),current);
|
||||
localStorage.setItem(ACTIVE,id);
|
||||
profiles.push({id:id,label:name});
|
||||
saveProfiles(profiles);
|
||||
render();
|
||||
}else{
|
||||
// Additional tab: stash the current one, start this one blank.
|
||||
var active=getActive();
|
||||
if(active!=null){
|
||||
var live=localStorage.getItem(KEY);
|
||||
if(live!=null)localStorage.setItem(slotKey(active),live);else localStorage.removeItem(slotKey(active));
|
||||
}
|
||||
loadDataInto(null);
|
||||
profiles.push({id:id,label:name});
|
||||
saveProfiles(profiles);
|
||||
localStorage.setItem(ACTIVE,id);
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
function renameProfile(id){
|
||||
var profiles=getProfiles();
|
||||
var p=profiles.find(function(x){return x.id===id;});
|
||||
if(!p)return;
|
||||
var name=window.prompt('Name für diesen Reiter:',p.label);
|
||||
if(name&&name.trim()){p.label=name.trim();saveProfiles(profiles);render();}
|
||||
}
|
||||
function deleteProfile(id){
|
||||
var profiles=getProfiles();
|
||||
var p=profiles.find(function(x){return x.id===id;});
|
||||
if(!p)return;
|
||||
if(!window.confirm('"'+p.label+'" wirklich entfernen? Die darin gespeicherten Daten dieses Reiters gehen dabei verloren.'))return;
|
||||
profiles=profiles.filter(function(x){return x.id!==id;});
|
||||
localStorage.removeItem(slotKey(id));
|
||||
var wasActive=getActive()===id;
|
||||
saveProfiles(profiles);
|
||||
if(wasActive){
|
||||
if(profiles.length){
|
||||
var next=profiles[0].id;
|
||||
loadDataInto(localStorage.getItem(slotKey(next)));
|
||||
localStorage.setItem(ACTIVE,next);
|
||||
location.reload();
|
||||
}else{
|
||||
// No tabs left -> back to a single, un-tabbed dataset (whatever was loaded stays, nothing wiped).
|
||||
localStorage.removeItem(ACTIVE);
|
||||
render();
|
||||
}
|
||||
}else{
|
||||
render();
|
||||
}
|
||||
}
|
||||
// --- Cloud sync hooks, called from the native host (Program.cs), which
|
||||
// polls an OneDrive-synced JSON file. The host treats the exported blob as
|
||||
// opaque; only this page understands/produces/consumes its structure. ---
|
||||
window.__mystartSyncExport=function(){
|
||||
var profiles=getProfiles();
|
||||
var active=getActive();
|
||||
var slots={};
|
||||
profiles.forEach(function(p){
|
||||
var raw=(p.id===active)?localStorage.getItem(KEY):localStorage.getItem(slotKey(p.id));
|
||||
if(raw!=null){try{slots[p.id]=JSON.parse(raw);}catch(e){}}
|
||||
});
|
||||
var live=null;
|
||||
if(active==null){
|
||||
var liveRaw=localStorage.getItem(KEY);
|
||||
if(liveRaw!=null){try{live=JSON.parse(liveRaw);}catch(e){}}
|
||||
}
|
||||
var data={profiles:profiles,active:active,slots:slots,live:live};
|
||||
return JSON.stringify({data:data,savedAt:new Date().toISOString()});
|
||||
};
|
||||
window.__mystartSyncImport=function(json){
|
||||
try{
|
||||
var payload=JSON.parse(json);
|
||||
var data=payload.data;
|
||||
if(data.active==null){
|
||||
if(data.live!=null)localStorage.setItem(KEY,JSON.stringify(data.live));else localStorage.removeItem(KEY);
|
||||
localStorage.removeItem(LIST);
|
||||
localStorage.removeItem(ACTIVE);
|
||||
}else{
|
||||
saveProfiles(data.profiles||[]);
|
||||
localStorage.setItem(ACTIVE,data.active);
|
||||
(data.profiles||[]).forEach(function(p){
|
||||
var val=data.slots?data.slots[p.id]:null;
|
||||
if(val!=null)localStorage.setItem(slotKey(p.id),JSON.stringify(val));
|
||||
});
|
||||
var activeVal=data.slots?data.slots[data.active]:null;
|
||||
loadDataInto(activeVal!=null?JSON.stringify(activeVal):null);
|
||||
}
|
||||
location.reload();
|
||||
return true;
|
||||
}catch(e){return false;}
|
||||
};
|
||||
var syncDot;
|
||||
function mkSyncDot(){
|
||||
var d=document.createElement('div');
|
||||
d.style.cssText='width:8px;height:8px;border-radius:50%;background:#565d68;margin-left:6px;flex:0 0 auto;';
|
||||
d.title='OneDrive-Sync: noch kein Status';
|
||||
syncDot=d;
|
||||
return d;
|
||||
}
|
||||
if(window.chrome&&window.chrome.webview){
|
||||
window.chrome.webview.addEventListener('message',function(ev){
|
||||
var d=ev.data;
|
||||
if(!d||d.type!=='sync-status'||!syncDot)return;
|
||||
syncDot.style.background=d.ok?'#2ea043':'#e8b923';
|
||||
syncDot.title=d.ok?('OneDrive-Sync: '+d.message):('OneDrive-Sync-Problem: '+d.message);
|
||||
});
|
||||
}
|
||||
var bar;
|
||||
var TXT_SHADOW='text-shadow:0 1px 3px rgba(0,0,0,.85),0 0 1px rgba(0,0,0,.6);';
|
||||
function mkPlus(){
|
||||
var b=document.createElement('div');
|
||||
b.textContent='+';
|
||||
b.title='Neuen Reiter anlegen';
|
||||
b.style.cssText='width:24px;height:24px;display:flex;align-items:center;justify-content:center;font-family:"Segoe UI",system-ui,sans-serif;font-size:15px;font-weight:600;color:#c7cbd3;cursor:pointer;-webkit-user-select:none;user-select:none;border-radius:6px;transition:background .12s,color .12s;'+TXT_SHADOW;
|
||||
b.onmouseenter=function(){b.style.background='rgba(255,255,255,0.16)';b.style.color='#fff';};
|
||||
b.onmouseleave=function(){b.style.background='transparent';b.style.color='#c7cbd3';};
|
||||
b.addEventListener('click',function(){addProfile();});
|
||||
return b;
|
||||
}
|
||||
function mkTab(p,isActive){
|
||||
var wrap=document.createElement('div');
|
||||
wrap.style.cssText='position:relative;display:flex;align-items:center;';
|
||||
var b=document.createElement('div');
|
||||
b.textContent=p.label;
|
||||
b.title='Zu diesem Reiter wechseln — Doppelklick zum Umbenennen';
|
||||
b.style.cssText='padding:6px 22px 6px 12px;font-family:"Segoe UI",system-ui,sans-serif;font-size:12.5px;cursor:pointer;-webkit-user-select:none;user-select:none;border-radius:6px;transition:background .12s,color .12s;'+TXT_SHADOW+
|
||||
(isActive
|
||||
? 'color:#ffffff;font-weight:700;background:rgba(255,255,255,0.22);box-shadow:inset 0 0 0 1px rgba(255,255,255,0.28);'
|
||||
: 'color:#d7dae0;font-weight:500;background:transparent;');
|
||||
b.addEventListener('click',function(){switchTo(p.id);});
|
||||
b.addEventListener('dblclick',function(ev){ev.preventDefault();renameProfile(p.id);});
|
||||
var x=document.createElement('div');
|
||||
x.textContent='\\u00d7';
|
||||
x.title='Reiter entfernen';
|
||||
x.style.cssText='position:absolute;right:2px;top:50%;transform:translateY(-50%);width:16px;height:16px;display:flex;align-items:center;justify-content:center;font-size:12px;color:#c7cbd3;cursor:pointer;opacity:0;transition:opacity .12s,color .12s,background .12s;border-radius:4px;';
|
||||
x.addEventListener('click',function(ev){ev.stopPropagation();deleteProfile(p.id);});
|
||||
// Toggle on the wrapper (not the label itself) so moving the cursor
|
||||
// from the label onto the x doesn't count as "leaving" and hide it again mid-click.
|
||||
wrap.addEventListener('mouseenter',function(){if(!isActive)b.style.background='rgba(255,255,255,0.14)';x.style.opacity='1';});
|
||||
wrap.addEventListener('mouseleave',function(){if(!isActive)b.style.background='transparent';x.style.opacity='0';});
|
||||
x.onmouseenter=function(){x.style.color='#fff';x.style.background='#e81123';};
|
||||
x.onmouseleave=function(){x.style.color='#c7cbd3';x.style.background='transparent';};
|
||||
wrap.appendChild(b);wrap.appendChild(x);
|
||||
return wrap;
|
||||
}
|
||||
function render(){
|
||||
if(!bar)return;
|
||||
bar.innerHTML='';
|
||||
var active=getActive();
|
||||
getProfiles().forEach(function(p){bar.appendChild(mkTab(p,p.id===active));});
|
||||
bar.appendChild(mkPlus());
|
||||
bar.appendChild(mkSyncDot());
|
||||
}
|
||||
function add(){
|
||||
if(!document.body||document.getElementById('mystart-profiles'))return;
|
||||
migrateLegacy();
|
||||
bar=document.createElement('div');
|
||||
bar.id='mystart-profiles';
|
||||
bar.style.cssText='position:fixed;top:6px;left:6px;display:flex;align-items:center;gap:2px;padding:4px;z-index:2147483647;background:rgba(22,24,29,0.68);border-radius:10px;box-shadow:0 2px 10px rgba(0,0,0,0.4),inset 0 0 0 1px rgba(255,255,255,0.06);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);';
|
||||
document.body.appendChild(bar);
|
||||
render();
|
||||
}
|
||||
if(document.body)add();else document.addEventListener('DOMContentLoaded',add);
|
||||
new MutationObserver(function(){if(!document.getElementById('mystart-profiles'))add();}).observe(document.documentElement,{childList:true,subtree:true});
|
||||
})();`;
|
||||
|
||||
mainJs = trInfra + autostartJs + windowControlsJs + profilesJs + mainJs;
|
||||
|
||||
const trPatches = [
|
||||
// panel header (top-level AND sub, both go through element.header)
|
||||
|
||||
Reference in New Issue
Block a user