Skip to content

WIP: Proof of concept for replacing AngularJS with Alpine.js in webconfig component#9554

Merged
faho merged 3 commits into
fish-shell:masterfrom
septatrix:rewrite/webconfig-to-alpinejs
Aug 29, 2023
Merged

WIP: Proof of concept for replacing AngularJS with Alpine.js in webconfig component#9554
faho merged 3 commits into
fish-shell:masterfrom
septatrix:rewrite/webconfig-to-alpinejs

Conversation

@septatrix

Copy link
Copy Markdown
Contributor

Description

Alpine.js is a very lightweight JS framework bringing many of the comforts from heavier frameworks like Vue or Angular. A huge benefit of Alpine.js is that it does not require any build steps unlike some other more lighter frameworks such as Svelte.

I have wanted to try out Alpine.js for a while now and worked on this during a train ride. It was very pleasant to work with and easy to understand, so I think will also help to reduce the maintenance burden coming forward.

This is for now just a POC, most importantly the color tab is not yet implemented. I also plan to split the HTML and JS into separate files again.

Fixes issue #9461 (at least the part about replacing EOL AngularJS)

TODOs:

  • Changes to fish usage are reflected in user documentation/manpages.
  • Tests have been added for regressions fixed
  • User-visible changes noted in CHANGELOG.rst

@ridiculousfish ridiculousfish self-requested a review February 8, 2023 05:48
@ridiculousfish

Copy link
Copy Markdown
Member

Thank you! This is very cool. I think alpine.js is an excellent choice because of its minimalism. I'll review.

@@ -0,0 +1,5 @@
(()=>{var Ye=!1,Ze=!1,V=[];function Ft(e){mn(e)}function mn(e){V.includes(e)||V.push(e),hn()}function xe(e){let t=V.indexOf(e);t!==-1&&V.splice(t,1)}function hn(){!Ze&&!Ye&&(Ye=!0,queueMicrotask(_n))}function _n(){Ye=!1,Ze=!0;for(let e=0;e<V.length;e++)V[e]();V.length=0,Ze=!1}var T,I,$,Qe,Xe=!0;function Bt(e){Xe=!1,e(),Xe=!0}function Kt(e){T=e.reactive,$=e.release,I=t=>e.effect(t,{scheduler:r=>{Xe?Ft(r):r()}}),Qe=e.raw}function et(e){I=e}function zt(e){let t=()=>{};return[n=>{let i=I(n);return e._x_effects||(e._x_effects=new Set,e._x_runEffects=()=>{e._x_effects.forEach(o=>o())}),e._x_effects.add(i),t=()=>{i!==void 0&&(e._x_effects.delete(i),$(i))},i},()=>{t()}]}var Vt=[],Ht=[],qt=[];function Ut(e){qt.push(e)}function ye(e,t){typeof t=="function"?(e._x_cleanups||(e._x_cleanups=[]),e._x_cleanups.push(t)):(t=e,Ht.push(t))}function Wt(e){Vt.push(e)}function Gt(e,t,r){e._x_attributeCleanups||(e._x_attributeCleanups={}),e._x_attributeCleanups[t]||(e._x_attributeCleanups[t]=[]),e._x_attributeCleanups[t].push(r)}function tt(e,t){!e._x_attributeCleanups||Object.entries(e._x_attributeCleanups).forEach(([r,n])=>{(t===void 0||t.includes(r))&&(n.forEach(i=>i()),delete e._x_attributeCleanups[r])})}var nt=new MutationObserver(rt),it=!1;function ie(){nt.observe(document,{subtree:!0,childList:!0,attributes:!0,attributeOldValue:!0}),it=!0}function ot(){gn(),nt.disconnect(),it=!1}var oe=[],st=!1;function gn(){oe=oe.concat(nt.takeRecords()),oe.length&&!st&&(st=!0,queueMicrotask(()=>{xn(),st=!1}))}function xn(){rt(oe),oe.length=0}function h(e){if(!it)return e();ot();let t=e();return ie(),t}var at=!1,be=[];function Jt(){at=!0}function Yt(){at=!1,rt(be),be=[]}function rt(e){if(at){be=be.concat(e);return}let t=[],r=[],n=new Map,i=new Map;for(let o=0;o<e.length;o++)if(!e[o].target._x_ignoreMutationObserver&&(e[o].type==="childList"&&(e[o].addedNodes.forEach(s=>s.nodeType===1&&t.push(s)),e[o].removedNodes.forEach(s=>s.nodeType===1&&r.push(s))),e[o].type==="attributes")){let s=e[o].target,a=e[o].attributeName,c=e[o].oldValue,l=()=>{n.has(s)||n.set(s,[]),n.get(s).push({name:a,value:s.getAttribute(a)})},u=()=>{i.has(s)||i.set(s,[]),i.get(s).push(a)};s.hasAttribute(a)&&c===null?l():s.hasAttribute(a)?(u(),l()):u()}i.forEach((o,s)=>{tt(s,o)}),n.forEach((o,s)=>{Vt.forEach(a=>a(s,o))});for(let o of r)if(!t.includes(o)&&(Ht.forEach(s=>s(o)),o._x_cleanups))for(;o._x_cleanups.length;)o._x_cleanups.pop()();t.forEach(o=>{o._x_ignoreSelf=!0,o._x_ignore=!0});for(let o of t)r.includes(o)||!o.isConnected||(delete o._x_ignoreSelf,delete o._x_ignore,qt.forEach(s=>s(o)),o._x_ignore=!0,o._x_ignoreSelf=!0);t.forEach(o=>{delete o._x_ignoreSelf,delete o._x_ignore}),t=null,r=null,n=null,i=null}function ve(e){return j(L(e))}function M(e,t,r){return e._x_dataStack=[t,...L(r||e)],()=>{e._x_dataStack=e._x_dataStack.filter(n=>n!==t)}}function ct(e,t){let r=e._x_dataStack[0];Object.entries(t).forEach(([n,i])=>{r[n]=i})}function L(e){return e._x_dataStack?e._x_dataStack:typeof ShadowRoot=="function"&&e instanceof ShadowRoot?L(e.host):e.parentNode?L(e.parentNode):[]}function j(e){let t=new Proxy({},{ownKeys:()=>Array.from(new Set(e.flatMap(r=>Object.keys(r)))),has:(r,n)=>e.some(i=>i.hasOwnProperty(n)),get:(r,n)=>(e.find(i=>{if(i.hasOwnProperty(n)){let o=Object.getOwnPropertyDescriptor(i,n);if(o.get&&o.get._x_alreadyBound||o.set&&o.set._x_alreadyBound)return!0;if((o.get||o.set)&&o.enumerable){let s=o.get,a=o.set,c=o;s=s&&s.bind(t),a=a&&a.bind(t),s&&(s._x_alreadyBound=!0),a&&(a._x_alreadyBound=!0),Object.defineProperty(i,n,{...c,get:s,set:a})}return!0}return!1})||{})[n],set:(r,n,i)=>{let o=e.find(s=>s.hasOwnProperty(n));return o?o[n]=i:e[e.length-1][n]=i,!0}});return t}function we(e){let t=n=>typeof n=="object"&&!Array.isArray(n)&&n!==null,r=(n,i="")=>{Object.entries(Object.getOwnPropertyDescriptors(n)).forEach(([o,{value:s,enumerable:a}])=>{if(a===!1||s===void 0)return;let c=i===""?o:`${i}.${o}`;typeof s=="object"&&s!==null&&s._x_interceptor?n[o]=s.initialize(e,c,o):t(s)&&s!==n&&!(s instanceof Element)&&r(s,c)})};return r(e)}function Ee(e,t=()=>{}){let r={initialValue:void 0,_x_interceptor:!0,initialize(n,i,o){return e(this.initialValue,()=>yn(n,i),s=>lt(n,i,s),i,o)}};return t(r),n=>{if(typeof n=="object"&&n!==null&&n._x_interceptor){let i=r.initialize.bind(r);r.initialize=(o,s,a)=>{let c=n.initialize(o,s,a);return r.initialValue=c,i(o,s,a)}}else r.initialValue=n;return r}}function yn(e,t){return t.split(".").reduce((r,n)=>r[n],e)}function lt(e,t,r){if(typeof t=="string"&&(t=t.split(".")),t.length===1)e[t[0]]=r;else{if(t.length===0)throw error;return e[t[0]]||(e[t[0]]={}),lt(e[t[0]],t.slice(1),r)}}var Zt={};function y(e,t){Zt[e]=t}function se(e,t){return Object.entries(Zt).forEach(([r,n])=>{Object.defineProperty(e,`$${r}`,{get(){let[i,o]=ut(t);return i={interceptor:Ee,...i},ye(t,o),n(t,i)},enumerable:!1})}),e}function Qt(e,t,r,...n){try{return r(...n)}catch(i){Z(i,e,t)}}function Z(e,t,r=void 0){Object.assign(e,{el:t,expression:r}),console.warn(`Alpine Expression Error: ${e.message}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm reasonably sure distributions would prefer we ship the unminified source, and size isn't really a big deal here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was using a minified version because the angular files were also minified. The full version is about double the size¹ but I agree that unminified is probably preferred by Distros.

¹ https://cdn.jsdelivr.net/npm/alpinejs@3.11.1/dist/

@septatrix

Copy link
Copy Markdown
Contributor Author

I also hooked up the color tab now. What remains to be done now is cleaning up the code by splitting it up in files again and test some edge cases.

One thing to note: Alpine.js does not support IE11 due to usage of @vue/reactivity under the hood. As IE is officially unsupported (finally) and only those using WSL and IE11 would be affected I do not think this should be considered a problem. I suspect most fish users are more tech-adapt and use evergreen browsers anyhow.

Due to the above, we can also reasonably replace a lot of old code with more modern equivalents. Another thing I would like to work on is make the webconfig a bit more accessible: E.g. use proper elements like inputs and buttons instead of divs with onclick handlers everywhere, improve keyboard navigation etc.

@mqudsi mqudsi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a nice change. Dropping IE11 support is probably wholly inconsequential for our purposes.

@septatrix septatrix marked this pull request as draft February 8, 2023 19:24
@faho faho added this to the fish next-3.x milestone Aug 29, 2023
@faho

faho commented Aug 29, 2023

Copy link
Copy Markdown
Member

Alright, I've checked this out, made the prompt tab work.

I can't find a bunch missing, so I'm going to merge this now so we can then collaborate on it together, instead of letting this PR languish in draft mode.

Thanks for all that work!

@faho faho marked this pull request as ready for review August 29, 2023 12:28
@faho faho merged commit 4b4cd4c into fish-shell:master Aug 29, 2023
@github-actions github-actions Bot locked as resolved and limited conversation to collaborators Sep 2, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants