symbiot 0.63.5 → 0.63.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/package.json +1 -1
- package/ui.mjs +54 -13
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
What each Symbiot release brought, newest first. When Approve bumps the version, it writes that release here from the tasks it approved, worded as release notes by your connected AI (or in the tasks' own words without one). The app shows the releases newer than yours under "What's new" when it offers an update, and once more after you update.
|
|
4
4
|
|
|
5
|
+
## 0.63.6
|
|
6
|
+
|
|
7
|
+
- **Approve opens on what you came for.** Clicking Review and approve on Home showed every project's work waiting for review, each task in full, with the Approve button several screens down. Now you see only that project, with its Approve button straight under its name, and its tasks one line each: the first three, with the rest a click away. Click a task to read it all. The other projects waiting on you are one line, with a button to show them.
|
|
8
|
+
- **Low GPU.** A new switch beside Glass, Ferrofluid and Pearl. With it on, Home's liquid draws at half the resolution and only when something changes, and the blur behind the glass panels is off. Your graphics card stops running at full clock all day. Symbiot remembers it on this computer.
|
|
9
|
+
- **A Symbiot window you aren't using rests.** Whether or not Low GPU is on, the liquid now draws only when something changes while you're in another window, instead of redrawing all the time behind your other apps.
|
|
10
|
+
|
|
5
11
|
## 0.63.5
|
|
6
12
|
|
|
7
13
|
- **Home stops stalling on git.** Every few seconds Home asked git, one project after another, what each of your last 60 agent runs changed, and what's waiting for review. The app couldn't do anything else while it waited: 20 seconds in every 45 on a busy day. It now reuses each answer and refreshes it in the background, so Home answers straight away. Approve still checks git fresh, and Home forgets what it kept as soon as you approve.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "symbiot",
|
|
3
|
-
"version": "0.63.
|
|
3
|
+
"version": "0.63.6",
|
|
4
4
|
"description": "Your week, written from your real work. Reads your local git and writes it up with the AI of your choice \u2014 Claude, OpenAI, Gemini, or a local model.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/ui.mjs
CHANGED
|
@@ -109,6 +109,14 @@ body.lq-look-pearl .lqmsg.me{background:#1B1E23;color:#F4F4F2}
|
|
|
109
109
|
body.lq-liquid header{position:fixed;top:0;left:0;right:0;z-index:4;background:transparent;pointer-events:none}
|
|
110
110
|
body.lq-liquid header .status,body.lq-liquid header .ver,body.lq-liquid #lqlook{pointer-events:auto}
|
|
111
111
|
#lqlook{display:none}
|
|
112
|
+
#lqlow{display:none}
|
|
113
|
+
body.lq-liquid #lqlow{display:inline-flex;align-items:center;pointer-events:auto;margin-left:8px;min-height:34px;padding:0 12px;border-radius:999px;background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.1);color:#8A919B;font:500 12.5px var(--sans);cursor:pointer}
|
|
114
|
+
body.lq-liquid #lqlow[aria-pressed='true']{background:#F3F0EA;color:#0A0B0D;border-color:#F3F0EA}
|
|
115
|
+
#lqlow:focus-visible{outline:2px solid currentColor;outline-offset:2px}
|
|
116
|
+
body.lq-light #lqlow{background:rgba(21,26,33,.05);border-color:rgba(21,26,33,.1);color:#5A6470}
|
|
117
|
+
body.lq-light #lqlow[aria-pressed='true']{background:#151A21;color:#FFFFFF;border-color:#151A21}
|
|
118
|
+
body.lq-touch #lqlow{min-height:40px}
|
|
119
|
+
body.lq-low *,body.lq-low *::before,body.lq-low *::after{backdrop-filter:none!important;-webkit-backdrop-filter:none!important}
|
|
112
120
|
body.lq-liquid #lqlook{display:inline-flex;gap:2px;margin-left:10px;padding:3px;border-radius:999px;background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.1)}
|
|
113
121
|
#lqlook button{min-height:28px;padding:0 12px;border:0;border-radius:999px;background:transparent;color:#8A919B;font:500 12.5px var(--sans);cursor:pointer}
|
|
114
122
|
#lqlook button[aria-checked='true']{background:#F3F0EA;color:#0A0B0D}
|
|
@@ -474,6 +482,9 @@ footer{padding:10px 18px;border-top:1px solid var(--line);display:flex}
|
|
|
474
482
|
.rcard{border:1px solid var(--amber);border-radius:11px;padding:10px 12px;margin-top:8px}
|
|
475
483
|
.rcard .rhead{display:flex;gap:8px;align-items:baseline;flex-wrap:wrap}.rcard .rhead .muted{font-size:12px}
|
|
476
484
|
.rcard .row{margin-top:8px}.rcard .task{margin-top:6px}
|
|
485
|
+
.rcard .rtasks{margin-top:10px}.rcard .rtn{font-size:12px}
|
|
486
|
+
.rcard .rtask .t{display:-webkit-box;-webkit-line-clamp:1;-webkit-box-orient:vertical;overflow:hidden;cursor:pointer}.rcard .rtask.open .t{display:block}
|
|
487
|
+
#reviewlist .rcard{scroll-margin-top:44px}.rcard .rmorebtn{margin-top:8px}.rothers{margin-top:10px;display:flex;gap:10px;align-items:center;flex-wrap:wrap;font-size:13px}
|
|
477
488
|
.pcard .ptext{white-space:pre-wrap;overflow-wrap:anywhere;margin-top:8px;color:var(--bone);font-size:13.5px;line-height:1.55}
|
|
478
489
|
.pcard textarea.pedit{display:block;width:100%;min-height:150px;margin-top:8px;background:var(--ink);border:1px solid var(--line);border-radius:9px;padding:10px 12px;color:var(--bone);font:inherit;font-size:13.5px;line-height:1.55;resize:vertical}
|
|
479
490
|
.pcard textarea.pedit:focus{outline:none;border-color:var(--green)}
|
|
@@ -902,6 +913,7 @@ body.lq-liquid .pshelf .pcard.editing{grid-column:1/-1}
|
|
|
902
913
|
.tnhow > summary::before{content:'?';display:inline-grid;place-items:center;width:16px;height:16px;border-radius:50%;border:1px solid currentColor;font-size:10.5px;font-weight:600}
|
|
903
914
|
.tnhow .note{margin-top:6px;max-width:80ch}
|
|
904
915
|
body.lq-liquid #panel-tasks .task .t{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;cursor:pointer}
|
|
916
|
+
body.lq-liquid #panel-tasks .rcard .rtask .t{-webkit-line-clamp:1}
|
|
905
917
|
body.lq-liquid #panel-tasks .task.open .t{display:block;-webkit-line-clamp:unset;overflow:visible}
|
|
906
918
|
#tasklist.onerepo .task .rp,.rcard .task .rp{display:none}
|
|
907
919
|
body.lq-liquid .rcard{border:1px solid rgba(242,165,65,.3);border-radius:20px;padding:16px 18px 14px;background:rgba(255,255,255,.035);box-shadow:none;margin-top:10px}
|
|
@@ -1094,7 +1106,7 @@ body.lq-light .btip .bta button:not(.quiet){background:#151A21;color:#FFFFFF}
|
|
|
1094
1106
|
<div id="updatebar" class="updatebar"></div>
|
|
1095
1107
|
<div id="appbar" class="updatebar"></div>
|
|
1096
1108
|
<div id="whatsnew" class="whatsnew"></div>
|
|
1097
|
-
<header><span class="dot" role="img" aria-label="Symbiot"></span><span class="brand">Symbiot</span><span class="ver" id="ver"></span><span id="lqlook" role="radiogroup" aria-label="Look"><button type="button" role="radio" data-look="glass" aria-checked="false">Glass</button><button type="button" role="radio" data-look="ferro" aria-checked="true">Ferrofluid</button><button type="button" role="radio" data-look="pearl" aria-checked="false">Pearl</button></span><span class="status" id="status">...</span></header>
|
|
1109
|
+
<header><span class="dot" role="img" aria-label="Symbiot"></span><span class="brand">Symbiot</span><span class="ver" id="ver"></span><span id="lqlook" role="radiogroup" aria-label="Look"><button type="button" role="radio" data-look="glass" aria-checked="false">Glass</button><button type="button" role="radio" data-look="ferro" aria-checked="true">Ferrofluid</button><button type="button" role="radio" data-look="pearl" aria-checked="false">Pearl</button></span><button type="button" id="lqlow" aria-pressed="false" title="Low GPU: Home's liquid drawn at half the resolution and only when something changes, with no blur behind the glass. Easier on your graphics card and battery.">Low GPU</button><span class="status" id="status">...</span></header>
|
|
1098
1110
|
<div class="tabs">
|
|
1099
1111
|
<button class="tab active" data-tab="map">Map</button>
|
|
1100
1112
|
<button class="tab" data-tab="board" id="boardtab">Dashboard</button>
|
|
@@ -1703,13 +1715,26 @@ var pendTimer=null,PENDRUN='';
|
|
|
1703
1715
|
function pendRunning(list){return (list||[]).filter(function(r){return r.running;}).map(function(r){return r.repo;}).join('\\n');}
|
|
1704
1716
|
function pendPoll(){if(pendTimer){clearTimeout(pendTimer);pendTimer=null;}if(!PENDRUN||current!=='tasks')return;
|
|
1705
1717
|
pendTimer=setTimeout(function(){pendTimer=null;if(current!=='tasks')return;api('/api/pending').then(function(list){if(pendRunning(list)===PENDRUN||selIn($('panel-tasks')))pendPoll();else loadTasks();}).catch(pendPoll);},4000);}
|
|
1706
|
-
|
|
1718
|
+
// Approve opens on what you came for: in a project, only its card (the others are a
|
|
1719
|
+
// click away, PENDALL), with its Approve button first and its tasks one line each, three
|
|
1720
|
+
// shown (RTASKS). Clicking Review and approve on Home met the whole list: every project's
|
|
1721
|
+
// card, every task in full, 3600px before the button (2026-10-12).
|
|
1722
|
+
var PENDALL=false,RTASKS=3;
|
|
1723
|
+
function loadPending(){var el0=document.getElementById('reviewlist'),fr=TFILTER.repo;
|
|
1724
|
+
if(el0&&!el0.innerHTML&&fr&&LQ.focus&&LQ.focus.kind==='approve'&&LQ.focus.repo===fr)el0.innerHTML="<div class='tgroup' style='color:var(--amber)'>Waiting for your OK</div><div class='muted rwait'>Getting "+esc(fr)+"'s changes ready…</div>";
|
|
1725
|
+
api('/api/pending').then(function(list){var el=document.getElementById('reviewlist');PENDTASKS=[];PENDRUN=pendRunning(list);pendPoll();
|
|
1707
1726
|
if(!list||!list.length){el.innerHTML='';lqLight();return;}
|
|
1727
|
+
var repo0=TFILTER.repo,mine=repo0&&!PENDALL?list.filter(function(r){return r.repo===repo0;}):[],others=mine.length?list.filter(function(r){return r.repo!==repo0;}):[];
|
|
1728
|
+
if(mine.length)list=mine;else if(repo0&&!PENDALL){el.innerHTML='';lqLight();return;}
|
|
1708
1729
|
var n=0;list.forEach(function(r){n+=r.tasks.length||1;});
|
|
1709
|
-
var h="<div class='tgroup' style='color:var(--amber)'>Awaiting your review <span class='tcount'>"+n+"</span
|
|
1710
|
-
list.forEach(function(r){var ch=r.files.length?(r.files.length+" file"+(r.files.length>1?"s":"")+" changed"+(r.stat?" · "+esc(r.stat):"")):"no uncommitted changes";
|
|
1730
|
+
var h="<div class='tgroup' style='color:var(--amber)'>"+(mine.length?"Waiting for your OK":"Awaiting your review <span class='tcount'>"+n+"</span>")+"</div>";
|
|
1731
|
+
list.forEach(function(r){var top='';var ch=r.files.length?(r.files.length+" file"+(r.files.length>1?"s":"")+" changed"+(r.stat?" · "+esc(r.stat):"")):"no uncommitted changes";
|
|
1711
1732
|
h+="<div class='rcard' data-repo='"+esc(r.repo)+"'"+(r.publishesOnMerge?" data-pom='1'":"")+"><div class='rhead'><b>"+esc(r.repo)+"</b><span class='muted'>"+(r.path?"on "+esc(r.branch||'?')+" · "+ch:"repo not found on disk")+"</span></div>";
|
|
1712
|
-
|
|
1733
|
+
top=h;h='';var nt=r.tasks.length;
|
|
1734
|
+
if(nt)h+="<div class='rtasks'><div class='muted rtn'>"+nt+" finished task"+(nt>1?"s":"")+" in it"+(nt>RTASKS?", the first "+RTASKS+" below":"")+". Click one to read it all.</div>";
|
|
1735
|
+
r.tasks.forEach(function(t,i){PENDTASKS.push(t);if(i===RTASKS)h+="<div class='rmore hidden'>";h+="<div class='task rtask' data-id='"+esc(t.id)+"'><span class='t' title='click to read it all'>"+esc(t.text)+"</span>"+askBtn(t)+"<button class='rm sendback' title='not right - send back to the agent (unticks it)'>↶</button></div>";});
|
|
1736
|
+
if(nt>RTASKS)h+="</div><button type='button' class='ghost rmorebtn'>Show the other "+(nt-RTASKS)+"</button>";
|
|
1737
|
+
if(nt)h+="</div>";
|
|
1713
1738
|
// a run that stopped on questions or a handover: partly done, and what it waits on, in plain words
|
|
1714
1739
|
var pd=r.partly,pq=pd&&pd.questions||0,ph=(pd&&pd.handed)||[];
|
|
1715
1740
|
if(pd)h+="<div class='partly'><b>Partly done:</b> "+esc([pq?pq+" question"+(pq>1?"s":"")+" for you":"",ph.length?ph.map(function(x){return x.lane+" is on “"+x.text+"”";}).join(', '):""].filter(Boolean).join(', and '))+". "+(r.untasked?"The changes so far can be approved now, or once it's finished.":"")+"</div>";
|
|
@@ -1725,9 +1750,15 @@ else if(ur)h+="<div class='muted' style='margin:6px 0'><span class='err'>Unrelea
|
|
|
1725
1750
|
// The version is already released (its v* tag exists, or npm has it) and these changes keep it:
|
|
1726
1751
|
// Approve bumps it in the PR too, a patch unless you pick otherwise.
|
|
1727
1752
|
var bo=r.bumpOffer;if(bo&&r.files.length)h+="<div class='row' style='margin:6px 0;font-size:12px'><span class='muted'>Version</span><select class='bumpsel' title='bump the version in package.json (and the lockfile) in this PR' style='flex:0 0 auto;width:auto'><option value='patch'>bump to "+esc(bo.patch)+" (patch)</option><option value='minor'>bump to "+esc(bo.minor)+" (minor)</option><option value=''>keep "+esc(bo.version)+"</option></select></div>";
|
|
1728
|
-
h+="<div class='row ract'>"+(pq&&r.path?"<button class='act answerq' data-path='"+esc(r.path).replace(/'/g,''')+"'>Answer its question"+(pq>1?"s":"")+"</button>":"")+"<button class='"+(pd?'ghost':'act')+" approve'"+(pd?" data-partly='1'":"")+(r.path&&!r.running?"":" disabled")+(r.running?" title='the agent is still editing this repo'>"+(r.untasked?"Approve changes without a task":"Approve")+" · agent still working":r.untasked?" data-untasked='1' title='commit on a branch, push and open a PR, without a task'>Approve changes without a task → PR":" title='commit on a branch, push, open a PR, then archive'>Approve → "+(r.files.length?"PR":"archive"))+"</button>"+(r.files.length?"<button class='ghost showdiff'>Show diff</button>":"")+"<label title='Queue GitHub auto-merge so this PR lands once its CI checks pass. Needs Allow auto-merge on the repo.' style='margin-left:auto;font-size:12px;color:var(--faint);display:flex;align-items:center;gap:6px'><input type='checkbox' class='amtoggle' style='width:auto'"+(r.autoMerge?" checked":"")+"> auto-merge on green CI</label></div><div class='rdiff hidden'></div
|
|
1753
|
+
h+="<div class='row ract'>"+(pq&&r.path?"<button class='act answerq' data-path='"+esc(r.path).replace(/'/g,''')+"'>Answer its question"+(pq>1?"s":"")+"</button>":"")+"<button class='"+(pd?'ghost':'act')+" approve'"+(pd?" data-partly='1'":"")+(r.path&&!r.running?"":" disabled")+(r.running?" title='the agent is still editing this repo'>"+(r.untasked?"Approve changes without a task":"Approve")+" · agent still working":r.untasked?" data-untasked='1' title='commit on a branch, push and open a PR, without a task'>Approve changes without a task → PR":" title='commit on a branch, push, open a PR, then archive'>Approve → "+(r.files.length?"PR":"archive"))+"</button>"+(r.files.length?"<button class='ghost showdiff'>Show diff</button>":"")+"<label title='Queue GitHub auto-merge so this PR lands once its CI checks pass. Needs Allow auto-merge on the repo.' style='margin-left:auto;font-size:12px;color:var(--faint);display:flex;align-items:center;gap:6px'><input type='checkbox' class='amtoggle' style='width:auto'"+(r.autoMerge?" checked":"")+"> auto-merge on green CI</label></div><div class='rdiff hidden'></div>";
|
|
1754
|
+
var act=h.slice(h.lastIndexOf("<div class='row ract'>")),bump=h.lastIndexOf("<div class='row' style='margin:6px 0;font-size:12px'><span class='muted'>Version</span>"),body=h.slice(0,h.lastIndexOf("<div class='row ract'>"));
|
|
1755
|
+
var bsel=bump>=0?body.slice(bump):'';if(bump>=0)body=body.slice(0,bump);
|
|
1756
|
+
h=top+bsel+act+body+"</div>";});
|
|
1757
|
+
if(others.length)h+="<div class='muted rothers'>Also waiting for your OK: "+others.map(function(r){return esc(r.repo);}).join(', ')+" <button type='button' class='ghost rallbtn'>Show them</button></div>";
|
|
1729
1758
|
el.innerHTML=h;
|
|
1759
|
+
var ra=el.querySelector('.rallbtn');if(ra)ra.addEventListener('click',function(){PENDALL=true;loadPending();});
|
|
1730
1760
|
el.querySelectorAll('.rcard').forEach(function(card){var repo=card.getAttribute('data-repo');
|
|
1761
|
+
var rmb=card.querySelector('.rmorebtn'),rm=card.querySelector('.rmore');if(rmb&&rm)rmb.addEventListener('click',function(){var shut=rm.classList.toggle('hidden');rmb.textContent=shut?'Show the other '+rm.children.length:'Show fewer';});
|
|
1731
1762
|
card.querySelectorAll('.task').forEach(function(row){wireAsk(row,row.getAttribute('data-id'));});
|
|
1732
1763
|
card.querySelectorAll('.sendback').forEach(function(b){b.addEventListener('click',function(){api('/api/pending/sendback',{id:b.closest('.task').getAttribute('data-id')}).then(loadTasks);});});
|
|
1733
1764
|
var amt=card.querySelector('.amtoggle');if(amt)amt.addEventListener('change',function(){api('/api/automerge',{repo:repo,on:amt.checked});});
|
|
@@ -3243,7 +3274,7 @@ function lqSaid(t){LQ.talk.push({me:false,text:t});LQ.talk=LQ.talk.slice(-8);lqT
|
|
|
3243
3274
|
// repo's questions and its Approve, under "Needs you here" at the top of its
|
|
3244
3275
|
// tasks, with its held handovers; an ops run's question on its block in Agents;
|
|
3245
3276
|
// a first step on its part of Settings.
|
|
3246
|
-
function lqFocus(y){lqAct();y=y||{};LQ.focus={kind:y.kind||'',repo:y.repo||'',path:y.path||'',focus:y.focus||'',shown:false};
|
|
3277
|
+
function lqFocus(y){lqAct();y=y||{};PENDALL=false;LQ.focus={kind:y.kind||'',repo:y.repo||'',path:y.path||'',focus:y.focus||'',shown:false};
|
|
3247
3278
|
document.querySelectorAll('main .lit').forEach(function(x){x.classList.remove('lit');}); // only what this orb lit stays lit
|
|
3248
3279
|
if(y.kind==='setup'){lqPool('settings');setTimeout(lqLight,300);return;}
|
|
3249
3280
|
if(!y.repo||y.repo==='ops'){lqPool('agents');return;}
|
|
@@ -3481,9 +3512,18 @@ var LQ_NB=32; // metaballs home can draw: the core, the droplets, the lines to t
|
|
|
3481
3512
|
// a third of the resolution, without the breathing, and only when something moved (Home), at most
|
|
3482
3513
|
// 20 a second while you use it and 5 at rest. glDpr: the pixel ratio a canvas draws at;
|
|
3483
3514
|
// glEvery: draw 1 frame in n.
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3515
|
+
// Low GPU (GLLOW, the header's switch, kept on this computer): the same, at half the
|
|
3516
|
+
// resolution, and no blur behind the glass (body.lq-low). The full liquid kept a graphics
|
|
3517
|
+
// card at full clock all day, the window open behind others (2026-10-12). And whatever you
|
|
3518
|
+
// pick, a window you aren't using (glFocused) draws the same way, only when something moved.
|
|
3519
|
+
var GLSOFT=false,GLSTILL=false,GLLOW=false;try{GLSTILL=!!(window.matchMedia&&window.matchMedia('(prefers-reduced-motion: reduce)').matches);}catch(e){}
|
|
3520
|
+
try{GLLOW=!!(window.localStorage&&window.localStorage.getItem('symbiot-lowgpu')==='1');}catch(e){}
|
|
3521
|
+
function glFocused(){try{return !document.hasFocus||document.hasFocus();}catch(e){return true;}}
|
|
3522
|
+
function glLight(){return GLSOFT||GLSTILL||GLLOW||!glFocused();}
|
|
3523
|
+
function glEvery(busy){return glLight()?(busy?3:12):1;}
|
|
3524
|
+
function glDpr(max){return GLSOFT?0.34:GLLOW?0.5:Math.min(window.devicePixelRatio||1,max);}
|
|
3525
|
+
function glLow(on){GLLOW=!!on;try{if(window.localStorage)window.localStorage.setItem('symbiot-lowgpu',GLLOW?'1':'0');}catch(e){}
|
|
3526
|
+
if(document.body&&document.body.classList)document.body.classList.toggle('lq-low',GLLOW);var b=$('lqlow');if(b)b.setAttribute('aria-pressed',GLLOW?'true':'false');}
|
|
3487
3527
|
function lqGL(c,n){c=c||$('lq');n=n||LQ_NB;if(!c||!c.getContext)return null;var gl=null;try{gl=c.getContext('webgl',{antialias:false,alpha:false});}catch(e){}if(!gl)return null;
|
|
3488
3528
|
try{var dbg=gl.getExtension('WEBGL_debug_renderer_info'),rn=String(dbg?gl.getParameter(dbg.UNMASKED_RENDERER_WEBGL):gl.getParameter(gl.RENDERER)).toLowerCase();if(['swiftshader','llvmpipe','softpipe','software'].some(function(k){return rn.indexOf(k)>=0;}))GLSOFT=true;}catch(e){}
|
|
3489
3529
|
function sh(t,src){var o=gl.createShader(t);gl.shaderSource(o,src);gl.compileShader(o);return o;}
|
|
@@ -3495,7 +3535,7 @@ var U={};['uRes','uT','uDpr','uB','uRip','uStyle','uContrast','uExposure'].forEa
|
|
|
3495
3535
|
return {gl:gl,c:c,U:U,out:new Float32Array(n*4),t0:(window.performance&&performance.now)?performance.now():Date.now()};}
|
|
3496
3536
|
function lqDraw(G){var gl=G.gl,c=G.c,S=lqSize(),d=glDpr(3),W=Math.max(1,Math.floor(S.w*d)),H=Math.max(1,Math.floor(S.h*d));
|
|
3497
3537
|
if(c.width!==W||c.height!==H){c.width=W;c.height=H;gl.viewport(0,0,W,H);}
|
|
3498
|
-
var now=(window.performance&&performance.now)?performance.now():Date.now(),t=(now-G.t0)/1000,th=LQ.theme,slow=
|
|
3538
|
+
var now=(window.performance&&performance.now)?performance.now():Date.now(),t=(now-G.t0)/1000,th=LQ.theme,slow=glLight(),still=th.still||slow,o=G.out;LQ.t=t;
|
|
3499
3539
|
for(var i=0;i<o.length;i++)o[i]=0;var NB=o.length/4;
|
|
3500
3540
|
var put=function(k,x,y,r,a,lv){if(k>NB-1)return;var w=still?0:(k===0?1:lv!=null?lv:0.3);o[k*4]=x/S.w+Math.sin(t*1.3+k*1.7)*0.004*w;o[k*4+1]=y/S.h+Math.cos(t*1.1+k*2.3)*0.005*w;o[k*4+2]=Math.max(0,r)*(1+0.035*Math.sin(t*2.0+k)*w);o[k*4+3]=a||0;};
|
|
3501
3541
|
put(0,LQ.core.x,LQ.core.y,LQ.core.cr);var k=1,tend=[];
|
|
@@ -3510,7 +3550,7 @@ var P=LQ.pointer;if(P&&LQ.mode==='aware'&&!LQ.talking){var best=null,bd=1e9;[LQ.
|
|
|
3510
3550
|
// a bulge toward the pointer, its tip always on the orb's edge: reaching further, it hung in the gap as an orb of its own with no label (2026-10-08)
|
|
3511
3551
|
if(best&&bd<230){var dx=P[0]-best.x,dy=P[1]-best.y,dl=Math.hypot(dx,dy)||1,tr=8+20*(1-Math.max(0,bd)/230),reach=Math.min(dl,best.cr+tr*0.6);put(NB-1,best.x+dx/dl*reach,best.y+dy/dl*reach,tr);}}
|
|
3512
3552
|
// drawn seldom (no GPU, or less motion asked for): still, and only when something moved
|
|
3513
|
-
if(slow){var sig=W+'|'+H+'|'+th.look+th.night+th.contrast;for(var q=0;q<o.length;q++)sig+=','+Math.round(o[q]*400);if(sig===G.sig)return;G.sig=sig;}
|
|
3553
|
+
if(slow){var sig=W+'|'+H+'|'+th.look+th.night+th.contrast+GLLOW;for(var q=0;q<o.length;q++)sig+=','+Math.round(o[q]*400);if(sig===G.sig)return;G.sig=sig;}
|
|
3514
3554
|
var U=G.U;gl.uniform2f(U.uRes,W,H);gl.uniform1f(U.uT,slow?0:t);gl.uniform1f(U.uDpr,d);gl.uniform4fv(U.uB,o);gl.uniform3f(U.uRip,LQ.ripple[0],LQ.ripple[1],still?-10:LQ.ripple[2]);
|
|
3515
3555
|
gl.uniform1f(U.uStyle,th.look==='glass'?0:th.look==='pearl'?2:1);gl.uniform1f(U.uContrast,th.contrast?1:0);gl.uniform1f(U.uExposure,th.night?0.82:1);
|
|
3516
3556
|
gl.drawArrays(gl.TRIANGLES,0,3);}
|
|
@@ -3673,6 +3713,7 @@ document.addEventListener('pointerdown',function(ev){var t=ev&&ev.target;if(!LQ.
|
|
|
3673
3713
|
var co=$('lqcore');if(co&&co.addEventListener)co.addEventListener('click',function(){var i=$('lqask');if(i&&i.focus)i.focus();});
|
|
3674
3714
|
var sk=$('lqsink');if(sk&&sk.addEventListener)sk.addEventListener('click',lqSink);
|
|
3675
3715
|
var bk=$('lqback');if(bk&&bk.addEventListener)bk.addEventListener('click',lqHome);
|
|
3716
|
+
glLow(GLLOW);var lw=$('lqlow');if(lw&&lw.addEventListener)lw.addEventListener('click',function(){glLow(!GLLOW);});
|
|
3676
3717
|
var lk=$('lqlook');if(lk&&lk.querySelectorAll){var cur=lqLookGet();lk.querySelectorAll('button').forEach(function(b){b.setAttribute('aria-checked',b.getAttribute('data-look')===cur?'true':'false');b.addEventListener('click',function(){lqLook(b.getAttribute('data-look'));});});}
|
|
3677
3718
|
var gb=$('lqgo');if(gb&&gb.addEventListener)gb.addEventListener('click',lqGoWork);
|
|
3678
3719
|
var so=$('lqsort');if(so&&so.querySelectorAll)so.querySelectorAll('button').forEach(function(b){b.setAttribute('aria-checked',b.getAttribute('data-sort')===LANESORT?'true':'false');b.addEventListener('click',function(){lqSort(b.getAttribute('data-sort'));});});
|
|
@@ -3681,7 +3722,7 @@ lqLoad(true);
|
|
|
3681
3722
|
// the only clocks: rest when left alone, fresh data now and then, the hour (night)
|
|
3682
3723
|
setInterval(function(){if(LQ.mode==='aware'&&!LQ.talking&&Date.now()-LQ.lastAct>LQ_REST)LQ.mode='rest';if(LQ.mode!=='pool')lqLoad(false);var h=new Date().getHours(),n=h>=22||h<6;if(n!==LQ.theme.night)lqTheme();},30000);
|
|
3683
3724
|
var G=lqGL();if(G){try{lqStep();lqDraw(G);}catch(e){}}if(typeof window.requestAnimationFrame!=='function')return;var raf=function(f){return window.requestAnimationFrame(f);};
|
|
3684
|
-
function frame(){if(!document.hidden){LQ.frame++;if(LQ.mode!=='pool'||LQ.frame%3===0)lqStep();var every=(
|
|
3725
|
+
function frame(){if(!document.hidden){LQ.frame++;if(LQ.mode!=='pool'||LQ.frame%3===0)lqStep();var every=glLight()?glEvery(LQ.mode==='aware'):LQ.mode==='pool'?6:LQ.mode==='rest'?2:1;if(G&&LQ.frame%every===0)lqDraw(G);}raf(frame);} // moves every frame; drawn less at rest, behind a panel, without a GPU
|
|
3685
3726
|
raf(frame);}
|
|
3686
3727
|
|
|
3687
3728
|
initGraphEvents();syncP();refresh();firstTab();lqInit();onbLoad();var sbo=$('sbopen');if(sbo)sbo.addEventListener('click',function(){sbOpen('');});var rr=$('rerunsetup');if(rr)rr.addEventListener('click',function(){api('/api/onboarding/restart',{}).then(function(){lqSink();onbLoad();});});awayInit();lqDeep();loadWhatsNew();loadAgentCfg();loadScanRoots();loadKnowledge();loadPhone();loadLinks();loadMind();loadTrusted();loadMail();loadScreensUI();loadMonitorsUI();loadDesktop();loadWatchUI();setInterval(function(){whenFree(document.querySelector('main'),'watch',loadWatchUI);},60000);loadPhoneLink();
|