Sparkles#14780
Conversation
rtibblesbot
left a comment
There was a problem hiding this comment.
Summary
This PR adds a celebratory burst animation to the PicturePasswordGrid submit button. The core idea is good and the accessibility fundamentals are correct (aria-hidden="true" on the SVG, prefers-reduced-motion respected for the CSS animations). However, there are two blocking issues: a confirmed test regression from the DURATION change, and an incomplete resource cleanup in onBeforeUnmount.
Blocking
Test regression from DURATION change (__tests__/PicturePasswordGrid.spec.js:359-364)
The DURATION constant was changed from 380 to 1480, but the playSuccessAnimation test still advances timers by only 380ms at the final step and has a stale comment // After the final 380ms. With a 3-element sequence and STAGGER=150, the last icon starts bouncing at t=300ms and its cleanup timer fires at t=300+1480=1780ms. The test reads state at t=680ms total and expects bouncingId to be null, arrowBouncing to be false, and resolved to be true — none of which can be true at that point. The final jest.advanceTimersByTime call must be updated to 1480 and the comment corrected. (The spec file is not in this diff, so the inline comment appears in the review body.)
onBeforeUnmount calls player.stop() instead of player.destruct() — see inline comment on SubmitBurstAnimation.vue:195.
Suggestions
burstVisible remains true for 1100ms under prefers-reduced-motion — see inline comment on PicturePasswordGrid.vue:314.
SubmitBurstAnimation not stubbed in test mountComponent()
The test helper at __tests__/PicturePasswordGrid.spec.js:303-305 stubs PicturePasswordOption and KIcon but not SubmitBurstAnimation. When burstVisible becomes true during the animation test, the real SVGator component mounts and its embedded IIFE attempts to call document.getElementById, window.requestAnimationFrame, and register a resize listener. In a jsdom environment this may produce console errors caught by jest-fail-on-console. Adding 'SubmitBurstAnimation' to the stubs array would isolate the test correctly.
No unit test for SubmitBurstAnimation.vue
The AGENTS.md requirement states "Testing is Required." This new component has no test file. A minimal test verifying it renders without errors and that player.destruct is called on unmount would satisfy the requirement.
burstVisible toggle behaviour is untested
The new burstVisible ref — set to true when the last icon bounces, and auto-cleared after 1100ms — has no test coverage. A test checking burstVisible at the right time steps would close this gap.
Nitpicks
.submit-burst missing explicit horizontal centering — see inline comment on PicturePasswordGrid.vue:488.
Blanket <!-- eslint-disable --> at line 1 of SubmitBurstAnimation.vue — see inline comment.
submitBurst.svg is a design source artifact committed unnecessarily
This file is not imported anywhere in the codebase — the animation is fully embedded in SubmitBurstAnimation.vue. Committing SVG design source files to the component tree adds noise without benefit.
WIP/debug commit messages in branch history
The branch includes commits titled "WIP: Adding a container parent..." and "Testing burst animation display". These should be squashed or reworded before merge.
Praise
The ESLint flat config override in eslint.config.mjs is exactly the right mechanism — scoped to **/animations/**/*.vue, setting only languageOptions.ecmaVersion: 2022 without touching rules or globals, with a clear explanatory comment. This is a minimal and correct intervention that avoids disabling any actual lint rules.
The accessibility fundamentals are solid: aria-hidden="true" on the SVG correctly marks the animation as decorative, and the v-if-driven mount/unmount pattern for the SVGator player is the right approach — mounting equals play-start, and the onBeforeUnmount hook ensures cleanup on every hide cycle.
@rtibblesbot's comments are generated by an LLM, and should be evaluated accordingly
How was this generated?
Reviewed the pull request diff checking for:
- Correctness: bugs, edge cases, undocumented behavior, resource leaks, hardcoded values
- Design: unnecessary complexity, naming, readability, comment accuracy, redundant state
- Architecture: duplicated concerns, minimal interfaces, composition over inheritance
- Testing: behavior-based assertions, mocks only at hard boundaries, accurate coverage
- Completeness: missing dependencies, unupdated usages, i18n, accessibility, security
- Principles: DRY (same reason to change), SRP, Rule of Three (no premature abstraction)
- Checked CI status and linked issue acceptance criteria
- For UI changes: inspected screenshots for layout, visual completeness, and consistency
| player.ready(player => player.play()); | ||
| }); | ||
|
|
||
| onBeforeUnmount(() => { |
There was a problem hiding this comment.
blocking: player.stop() only resets the playhead and cancels the pending requestAnimationFrame — it does not remove the global resize listener registered by the SVGator player IIFE. The SVGator player exposes player.destruct() specifically for full teardown, which removes all listeners and nulls internal state. Per the project convention "whoever allocates a resource releases it", this should call player.destruct() instead:
onBeforeUnmount(() => {
if (player && typeof player.destruct === 'function') {
player.destruct();
}
player = null;
});| @@ -0,0 +1,204 @@ | |||
| <!-- eslint-disable --> | |||
There was a problem hiding this comment.
nitpick: The file-level <!-- eslint-disable --> disables all ESLint rules for the entire file. The actual rule violation (import-x/no-import-module-exports from the UMD wrapper) is already handled by the targeted // eslint-disable-next-line comment on line 169. This blanket disable is redundant and will silently swallow any future lint issues introduced into the non-generated parts of this file. Consider removing it.
| @@ -305,7 +313,13 @@ | |||
| const isLast = i === sequence.value.length - 1; | |||
| window.setTimeout(() => { | |||
There was a problem hiding this comment.
suggestion: When prefers-reduced-motion: reduce is active, dur and stagger are zeroed (correct), but burstVisible is still set to true and the 1100ms teardown timer still runs. The SVGator player in SubmitBurstAnimation has no reduced-motion guard of its own, so the animation plays even for users who have opted into reduced motion.
Consider skipping the burst entirely when reduce is true:
if (isLast) {
arrowBouncing.value = true;
if (!reduce) {
burstVisible.value = true;
window.setTimeout(() => {
burstVisible.value = false;
}, 1100);
}
}| justify-content: center; | ||
| } | ||
|
|
||
| .submit-button { |
There was a problem hiding this comment.
nitpick: .submit-burst has position: absolute; top: 50%; z-index: 100 but no left coordinate. Without an explicit left: 50%; transform: translate(-50%, -50%), the horizontal position of the burst SVG depends on the flex container's current layout rather than being explicitly centered. This may work visually now but will be fragile if the container layout changes.
Build Artifacts
Smoke test screenshot |
…se as playground.
- Move flexbox centering from submit-button to submit-container - Remove redundant top positioning from submit-burst (centered via flex parent)
Align PicturePasswordGrid success animation timing with actual visual behavior so the promise resolves after the burst window, not just icon bounce. Keep reduced-motion users out of burst playback and make burst overlay positioning explicit. Update grid unit tests to validate burst visibility timing, reduced-motion behavior, and isolate animation tests by stubbing the burst component. Resolves #15097 AI-assisted-by: gpt-5
Use full SVGator teardown by calling player.destruct() on unmount, add focused unit coverage for burst render and teardown behavior, and remove the unused submitBurst.svg design-source artifact. Keep lint suppressions scoped to generated SVG constraints and the UMD wrapper import context. Resolves #15097 AI-assisted-by: gpt-5
rtibblesbot
left a comment
There was a problem hiding this comment.
Prior findings
Resolved:
player.stop()instead ofplayer.destruct()(blocking) —onBeforeUnmountnow callsplayer.destruct()with a defensive type-check- Blanket
<!-- eslint-disable -->at file level (nitpick) — replaced with targetedmax-len, vue/max-lenand scoped/* eslint-disable */around the minified block - No reduced-motion guard for the burst (suggestion) — burst is skipped entirely when
reduceistrue .submit-burstmissingleftcoordinate (nitpick) — now hasleft: 50%; transform: translate(-50%, -50%)
4/4 prior findings resolved.
CI still running (Python/frontend jobs pending at review time). Static analysis (CodeQL, Analyze) passes.
Video shows the grid, error feedback, and confirmation dialog working correctly. The burst animation is too brief to appear in extracted frames, but code and lifecycle are sound.
@rtibblesbot's comments are generated by an LLM, and should be evaluated accordingly
How was this generated?
Compared the current PR state against findings from a prior review:
- Retrieved prior bot reviews deterministically via the GitHub API
- Classified each prior finding as RESOLVED, UNADDRESSED, ACKNOWLEDGED, or CONTESTED
- Only raised NEW findings for newly introduced code
- Reviewed the pull request diff checking for correctness, design, architecture, testing, completeness, and adherence to DRY/SRP principles
- Checked CI status and linked issue acceptance criteria
| }); | ||
|
|
||
| onBeforeUnmount(() => { | ||
| if (player && typeof player.destruct === 'function') { |
There was a problem hiding this comment.
praise: player.destruct() is the correct full teardown, and the typeof player.destruct === 'function' guard handles the case where the SVGator runtime wasn't successfully installed. Good resource cleanup discipline — allocates in onMounted, releases in onBeforeUnmount.
|
|
||
| window.setTimeout(() => resolve(), (sequence.value.length - 1) * stagger + dur); | ||
| const lastStart = (iconCount - 1) * stagger; | ||
| const animationDuration = Math.max(lastStart + iconDuration, lastStart + burstDuration); |
There was a problem hiding this comment.
praise: Using Math.max(lastStart + iconDuration, lastStart + burstDuration) correctly gates the Promise on whichever animation takes longer — the burst outlasts the icon bounce, so the parent caller doesn't proceed while sparkles are still on screen.
| wrapper.destroy(); | ||
| }); | ||
|
|
||
| it('calls player.destruct on unmount when available', () => { |
There was a problem hiding this comment.
praise: Explicitly testing the teardown path (not just mount) closes the loop on the prior blocking finding — this test would have caught the original player.stop() bug.
| { | ||
| files: ['**/animations/**/*.vue'], | ||
| languageOptions: { | ||
| ecmaVersion: 2022, |
There was a problem hiding this comment.
I am not sure there's a good reason to pin the ecmaVersion - we can set it as 'latest' in the base kolibri-format configuration.
There was a problem hiding this comment.
Ah, yeah, this was one of the AI-Assisted changes from the test-writing phase. I will double check, but I think it was having issues with some of the lint-ignore comments for the svgator-vue-component file
There was a problem hiding this comment.
AI's defense:
We only bumped ecmaVersion for the animation files because the SVGator-generated player code embedded inside those .vue files uses newer syntax (class fields + ??=) that Kolibri’s base ESLint config didn’t understand. Without the override, every lint run failed before we could even touch the file. That’s why the block at the bottom of eslint.config.mjs pins languageOptions.ecmaVersion = 2022 for /animations//*.vue only — it was a surgical workaround so we didn’t change the language target for the entire repo.
I'll look into alternatives!
| const svg = document.getElementById('e9E2fA0WrSF1'); | ||
| player = svg && svg.svgatorPlayer; | ||
| if (!player) { | ||
| return; | ||
| } | ||
| player.ready(player => player.play()); |
There was a problem hiding this comment.
Are these lines actually needed? In theory, the comment above says the previous blob code already plays the animation, right? Asked Claude to interpret the code above, and it says it does, but I'm not completely sure 😅. Just double-checking :). In any case, I'd say the comment above should explain a bit more where all this bundled code is coming from and what it does.
|
|
||
| onBeforeUnmount(() => { | ||
| if (player && typeof player.destruct === 'function') { | ||
| player.destruct(); |
There was a problem hiding this comment.
We don't really need a player global variable right? we can just query the element and extract the player directly here.
| /* eslint-disable */ | ||
| // prettier-ignore | ||
| // SVGator embedded player + animation data; initializes the player engine | ||
| !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof __SVGATOR_DEFINE__&&__SVGATOR_DEFINE__.amd?__SVGATOR_DEFINE__(e):((t="undefined"!=typeof globalThis?globalThis:t||self).__SVGATOR_PLAYER__=t.__SVGATOR_PLAYER__||{},t.__SVGATOR_PLAYER__["91c80d77"]=e())}(this,function(){"use strict";const t=n(Math.pow(10,-6)),e=n(Math.pow(10,-2));function n(t,e=6){return function(t,e,n){if(Number.isInteger(t))return t;const r=Math.pow(10,e);return Math[n]((+t+Number.EPSILON)*r)/r}(t,e,"round")}function r(e,n,r=t){return Math.abs(e-n)<r}const i=Math.PI/180;function s(t){return t*i}function o(t){return t/i}function u(t){return"number"!=typeof t&&(t=Number(t)),isNaN(t)||!isFinite(t)?0:t}let l={iD:!1};function a(t){return`${t}`.replace(/['"]/g,"")}function c(t,e="px"){return t.endsWith(e)?t:`${t}${e}`}function f(t=""){return l.iD?t:void 0}function h(t){const e=["^","\\s*","(matrix\\()","(?<a>-?[0-9]*\\.?[0-9]+)","\\s*",",?","\\s*","(?<b>-?[0-9]*\\.?[0-9]+)","\\s*",",?","\\s*","(?<c>-?[0-9]*\\.?[0-9]+)","\\s*",",?","\\s*","(?<d>-?[0-9]*\\.?[0-9]+)","\\s*",",?","\\s*","(?<e>-?[0-9]*\\.?[0-9]+)","\\s*",",?","\\s*","(?<f>-?[0-9]*\\.?[0-9]+)","\\)"].join(""),n=t.match(new RegExp(e,"i"));if(n)return[u(n.groups.a),u(n.groups.b),u(n.groups.c),u(n.groups.d),u(n.groups.e),u(n.groups.f)]}function d(t,e="inline"){return t&&t!==e?(t=a(t))===e?f(e):t:f(e)}function g(t,e="1px"){if(!t||t===e)return f(e);if(t.endsWith("px")||t.endsWith("%"))return t;const r=c(t=`${n(u(t),2)}`);return r===e?f(e):r}function p(t,e="none"){if(!t||t===e)return f(e);if((t=a(t))===e)return f(e);const n=t.match(/url\(#.+?\)/);return n?n[0]:function(t){const e=["^","\\s*","url\\(","#","(?<maskId>[a-zA-Z0-9\\-_]+)","\\)"].join(""),n=t.match(new RegExp(e,"i"));if(n)return`url(#${n.groups.maskId})`}(t)}function y(t,e="rgb(0, 0, 0)"){if(!t||t===e)return f(e);if((t=a(t))===e)return f(e);if(t.startsWith("rgb"))return t;const n=function(t){if("string"!=typeof t||"#"!==t[0])return t;const e=t.slice(1),n=e.length;if(3!==n&&6!==n)return t;for(let r=0;r<n;r++){const n=e.charCodeAt(r);if(!(n>=48&&n<=57||n>=65&&n<=70||n>=97&&n<=102))return t}if(3===n)return`rgb(${parseInt(e[0]+e[0],16)}, ${parseInt(e[1]+e[1],16)}, ${parseInt(e[2]+e[2],16)})`;return`rgb(${parseInt(e.slice(0,2),16)}, ${parseInt(e.slice(2,4),16)}, ${parseInt(e.slice(4,6),16)})`}(t);return n===e?f(e):n}function m(t,e="1"){return t&&t!==e?(t=`${n(u(t),3)}`)===e?f(e):t:f(e)}let v={};const b={fill:"none",stroke:"none",opacity:"0.01",transform:"matrix(0.001 0 0 0.001 -10000 -10000)"};function w({element:t,tagType:e="path",property:n="d",attributeValue:r}){return v[r]||(v[r]=function({element:t,tagType:e="path",property:n="d",attributeValue:r}){const i=t.ownerSVGElement,s=document.createElementNS(i.namespaceURI,e);s.setAttributeNS(null,n,r);for(const t in b)s.setAttributeNS(null,t,b[t]);i.appendChild(s);const o=getComputedStyle(s)[n];return s.remove(),o}({element:t,tagType:e,property:n,attributeValue:r})),v[r]}const x=()=>{v={}};"object"==typeof window&&(window.removeEventListener("resize",x),window.addEventListener("resize",x));const A={include:(t,e)=>["path"].includes(e),formatter:t=>function(t,e=""){return t&&t!==e?(t=a(t))===e?f(e):t.startsWith("path(")?t:`path('${t}')`:f(e)}(t)},_={include:(t,e)=>["rect","mask"].includes(e),formatter:(t,e,n)=>g(t,function(t,e){var n;return(null===(n={mask:{x:"-150%",y:"-150%",width:"400%",height:"400%"}}[e])||void 0===n?void 0:n[t])||(["x","y"].includes(t)?"0px":"100%")}(e,n))},k=Object.freeze({d:A,display:t=>d(t),height:_,fill:t=>y(t),"fill-opacity":t=>m(t),filter:t=>d(t,"none"),mask:t=>p(t),opacity:t=>m(t),stroke:t=>y(t,"none"),"stroke-opacity":t=>m(t),"stroke-width":t=>g(t),transform:t=>function(t,e="none"){if(!t||t===e)return f(e);const n=h(t);return n?`matrix(${n.join(", ")})`:t}(t),"transform-origin":t=>function(t,e="0px 0px"){if(!t||t===e)return f(e);const n=["^","\\s*","(?<x>[0-9]+)","(px)?","\\s*",",","\\s*","(?<y>[0-9]+)","(px)?"].join(""),r=t.match(new RegExp(n,"i"));if(!r)return t;let i=`${u(r.groups.x)}`;i=c(i);let s=`${u(r.groups.y)}`;s=c(s);const o=`${i} ${s}`;return o===e?f(e):o}(t),visibility:t=>d(t,"visible"),width:_,x:_,y:_}),S=Object.keys(k),E=e;function I(t,e){return r((e-t)/(e||1)*100,0,E)}function O(t,e,n,r,i=window){var s,o;const u=t.getAttribute(e),l="transform"===e?"none":"",a=(i.safari||function(t){var e,n;const r=null==t?void 0:t.userAgent;return(null==t||null===(e=t.vendor)||void 0===e||null===(n=e.indexOf)||void 0===n?void 0:n.call(e,"Apple"))>-1&&r&&-1===r.indexOf("CriOS")&&-1===r.indexOf("FxiOS")}(null==i?void 0:i.navigator)||i.webkit)&&!i.chrome&&(null===(s=r.getPropertyValue)||void 0===s?void 0:s.call(r,e))===l||"mask"===n?u:null===(o=r.getPropertyValue)||void 0===o?void 0:o.call(r,e);if(u&&a){if(u===a)return u;switch(e){case"transform":return function(t,e){const n=h(t),r=h(e);if((null==n?void 0:n.length)===(null==r?void 0:r.length)){for(let t=0,e=n.length;t<e;t++)if(n[t]!==r[t]&&!I(r[t],n[t]))return;return t}}(u,a);case"d":return function(t,e,n){return w({element:t,attributeValue:e})===n?e:void 0}(t,u,a);default:return a}}}function M(t,e,n,r){var i,s,o;const u="transform"===e||["mask","path"].includes(n)&&["x","y","width","height","d"].includes(e);return r&&u?O(t,e,n,r):(null===(i=r.getPropertyValue)||void 0===i?void 0:i.call(r,e))??(null===(s=t.attrs)||void 0===s||null===(s=s.style)||void 0===s?void 0:s[e])??(null===(o=t.attrs)||void 0===o?void 0:o[e])}function B(t,e,n=!1){l.iD=n;const r="undefined"!=typeof window&&getComputedStyle(t),i={};for(let l=0,a=S.length;l<a;l++){var s,o,u;const a=S[l],c=t.type||t.nodeName;if(!1===(null===(s=(o=k[a]).include)||void 0===s?void 0:s.call(o,a,c)))continue;const f=k[a].formatter||k[a];if(null!=e&&null!==(u=e[t.id])&&void 0!==u&&u[a])continue;const h=M(t,a,c,r);if(null==h&&!n)continue;const d=f.call(this,h,a,c);d&&(i[a]=d)}return i}function N(t){var e,n;if(null==t||null===(e=t.wD)||void 0===e||!e.length)return;this.h=t.wD.shift();const r=null===(n=t.rootId)||void 0===n?void 0:n.slice(0,-1);this.wIs=t.wD.map(t=>`${r}${t}`)}function j(t){const e=new N(t),n=t.svg,r=e.wIs,i=t.originalAnimations[0].elements,s=e.h;function o(t,e,s){var u;if(e[t])return;const l=n.querySelector("#"+t),a=null==l||null===(u=l.parentElement)||void 0===u?void 0:u.id;if(l&&a){if(r.includes(a))return e[a]||o(a,e,s),e[a].children??=[],e[t]=B(l,i),void e[a].children.push(e[t]);e[t]=B(l,i),s.push(e[t])}}async function u(){const t=function(){let t=[],e={};for(let n=0,i=r.length;n<i;n++)o(r[n],e,t);return t}();return await async function(t){var e;const n=JSON.stringify(t),r=(new TextEncoder).encode(n),i=await(null===(e=window.crypto)||void 0===e||null===(e=e.subtle)||void 0===e?void 0:e.digest("SHA-256",r));return i&&Array.from(new Uint8Array(i)).map(t=>t.toString(16).padStart(2,"0")).join("")||s}(t)}this.vH=async function(){await u()!==s&&requestAnimationFrame(()=>t.stop())}}function T(t){let e=0,n=0;const r=new j(t);this.cF=function(i,s){return t.wD?(e++,function(t){return!(t-n<300)&&(t-n>=500||e>=3)}(i)?(e=0,n=i,window.requestAnimationFrame(()=>r.vH()),s()):s()):s()}}function P(t){return t}function F(t,e,n){const r=1-n;return 3*n*r*(t*r+e*n)+n*n*n}function R(t=0,e=0,n=1,i=1){return t<0||t>1||n<0||n>1?null:r(t,e)&&r(n,i)?P:s=>{if(s<=0)return t>0?s*e/t:0===e&&n>0?s*i/n:0;if(s>=1)return n<1?1+(s-1)*(i-1)/(n-1):1===n&&t<1?1+(s-1)*(e-1)/(t-1):1;let o,u=0,l=1;for(;u<l;){o=(u+l)/2;const e=F(t,n,o);if(r(s,e))break;e<s?u=o:l=o}return F(e,i,o)}}function C(){return 1}function V(t){return 1===t?1:0}function D(t=1,e=0){if(1===t){if(0===e)return V;if(1===e)return C}const n=1/t;return t=>t>=1?1:(t+=e*n)-t%n}Number.isInteger||(Number.isInteger=function(t){return"number"==typeof t&&isFinite(t)&&Math.floor(t)===t}),Number.EPSILON||(Number.EPSILON=2220446049250313e-31);const $=Math.sin,q=Math.cos,L=Math.acos,z=Math.asin,G=Math.tan,W=Math.atan2,Y=Math.sqrt;function U(t,e){return{a:t[0]*e[0]+t[2]*e[1],b:t[1]*e[0]+t[3]*e[1],c:t[0]*e[2]+t[2]*e[3],d:t[1]*e[2]+t[3]*e[3],tx:t[0]*e[4]+t[2]*e[5]+t[4],ty:t[1]*e[4]+t[3]*e[5]+t[5]}}function H(t,e,n){return t>=.5?n:e}function J(t,e,n){return 0===t||e===n?e:t*(n-e)+e}function X(t,e,n){const r=J(t,e,n);return r<=0?0:r}function K(t,e,n){const r=J(t,e,n);return r<=0?0:r>=1?1:r}function Q(t,e,n){return 0===t?e:1===t?n:{x:J(t,e.x,n.x),y:J(t,e.y,n.y)}}function Z(t,e,n){return 0===t?e:1===t?n:{x:X(t,e.x,n.x),y:X(t,e.y,n.y)}}function tt(t,e,n){const r=function(t,e,n){return Math.round(J(t,e,n))}(t,e,n);return r<=0?0:r>=255?255:r}function et(t,e,n){return 0===t?e:1===t?n:{r:tt(t,e.r,n.r),g:tt(t,e.g,n.g),b:tt(t,e.b,n.b),a:J(t,null==e.a?1:e.a,null==n.a?1:n.a)}}function nt(t,e,n){let r=e.length;if(r!==n.length)return H(t,e,n);let i=new Array(r);for(let s=0;s<r;s++)i[s]=J(t,e[s],n[s]);return i}function rt(t,e){const n=[];for(let r=0;r<t;r++)n.push(e);return n}function it(t,e){if(--e<=0)return t;const n=(t=Object.assign([],t)).length;do{for(let e=0;e<n;e++)t.push(t[e])}while(--e>0);return t}class st{constructor(t){this.list=t,this.length=t.length}setAttribute(t,e){const n=this.list;for(let r=0;r<this.length;r++)n[r].setAttribute(t,e)}removeAttribute(t){const e=this.list;for(let n=0;n<this.length;n++)e[n].removeAttribute(t)}style(t,e){const n=this.list;for(let r=0;r<this.length;r++)n[r].style[t]=e}}const ot=/-./g,ut=(t,e)=>e.toUpperCase();let lt;function at(t){return"function"==typeof t?t:H}function ct(t){return t?"function"==typeof t?t:Array.isArray(t)?function(t,e=P){if(!Array.isArray(t))return e;switch(t.length){case 1:return D(t[0])||e;case 2:return D(t[0],t[1])||e;case 4:return R(t[0],t[1],t[2],t[3])||e}return e}(t,null):function(t,e,n=P){switch(t){case"linear":return P;case"steps":return D(e.steps||1,e.jump||0)||n;case"bezier":case"cubic-bezier":return R(e.x1||0,e.y1||0,e.x2||0,e.y2||0)||n}return n}(t.type,t.value,null):null}function ft(t,e,n,r=!1){const i=e.length-1;if(t<=e[0].t)return r?[0,0,e[0].v]:e[0].v;if(t>=e[i].t)return r?[i,1,e[i].v]:e[i].v;let s,o=e[0],u=null;for(s=1;s<=i;s++){if(!(t>e[s].t)){u=e[s];break}o=e[s]}return null==u?r?[i,1,e[i].v]:e[i].v:o.t===u.t?r?[s,1,u.v]:u.v:(t=(t-o.t)/(u.t-o.t),o.e&&(t=o.e(t)),r?[s,t,n(t,o.v,u.v)]:n(t,o.v,u.v))}function ht(t,e,n=null){return t&&t.length?"function"!=typeof e?null:("function"!=typeof n&&(n=null),r=>{let i=ft(r,t,e);return null!=i&&n&&(i=n(i)),i}):null}function dt(t,e){return t.t-e.t}function gt(t,e,n,r,i){const s="@"===n[0],o="#"===n[0];let u=lt[n],l=H;var a;switch(s?(a=n.substr(1),n=a.replace(ot,ut)):o&&(n=n.substr(1)),typeof u){case"function":if(l=u(r,i,ft,ct,n,s,e,t),o)return l;break;case"string":l=ht(r,at(u));break;case"object":if(l=ht(r,at(u.i),u.f),l&&"function"==typeof u.u)return u.u(e,l,n,s,t)}return l?function(t,e,n,r=!1){if(r)return t instanceof st?r=>t.style(e,n(r)):r=>t.style[e]=n(r);if(Array.isArray(e)){const r=e.length;return i=>{const s=n(i);if(null==s)for(let n=0;n<r;n++)t[n].removeAttribute(e);else for(let n=0;n<r;n++)t[n].setAttribute(e,s)}}return r=>{const i=n(r);null==i?t.removeAttribute(e):t.setAttribute(e,i)}}(e,n,l,s):null}function pt(t,e,n,r){if(!r||"object"!=typeof r)return null;let i=null,s=null;return Array.isArray(r)?s=function(t){if(!t||!t.length)return null;for(let e=0;e<t.length;e++)t[e].e&&(t[e].e=ct(t[e].e));return t.sort(dt)}(r):(s=r.keys,i=r.data||null),s?gt(t,e,n,s,i):null}function yt(t,e,n){if(!n)return null;const r=[];for(const i in n)if(n.hasOwnProperty(i)){const s=pt(t,e,i,n[i]);s&&r.push(s)}return r.length?r:null}function mt(t,e){if(!e.settings.duration||e.settings.duration<0)return null;const n=function(t,e){if(!e)return null;let n=[];if(Array.isArray(e)){const r=e.length;for(let i=0;i<r;i++){const r=e[i];if(2!==r.length)continue;let s=null;if("string"==typeof r[0])s=t.getElementById(r[0]);else if(Array.isArray(r[0])){s=[];for(let e=0;e<r[0].length;e++)if("string"==typeof r[0][e]){const n=t.getElementById(r[0][e]);n&&s.push(n)}s=s.length?1===s.length?s[0]:new st(s):null}if(!s)continue;const o=yt(t,s,r[1]);o&&(n=n.concat(o))}}else for(const r in e){if(!e.hasOwnProperty(r))continue;const i=t.getElementById(r);if(!i)continue;const s=yt(t,i,e[r]);s&&(n=n.concat(s))}return n.length?n:null}(t,e.elements);return n?function(t,e){const n=e.duration,r=t.length;let i=null;return(s,o)=>{const u=e.iterations||1/0,l=(e.alternate&&u%2==0)^e.direction>0?n:0;let a=s%n,c=1+(s-a)/n;o*=e.direction,e.alternate&&c%2==0&&(o=-o);let f=!1;if(c>u)a=l,f=!0,-1===e.fill&&(a=e.direction>0?0:n);else if(o<0&&(a=n-a),a===i)return!1;i=a;for(let e=0;e<r;e++)t[e](a);return f}}(n,e.settings):null}function vt(t,e=document,n=0){const r=function(t,e){const n=e.querySelectorAll("svg");for(let e=0;e<n.length;e++)if(n[e].id===t.root&&!n[e].svgatorAnimation)return n[e].svgatorAnimation=!0,n[e];return null}(t,e);if(r)return r;if(n>=20)return null;const i=function(t){const e=t=>t.shadowRoot;return document?Array.from(t.querySelectorAll(":not("+["a","area","audio","br","canvas","circle","datalist","embed","g","head","hr","iframe","img","input","link","object","path","polygon","rect","script","source","style","svg","title","track","video"].join()+")")).filter(e).map(e):[]}(e);for(let e=0;e<i.length;e++){const r=vt(t,i[e],n+1);if(r)return r}return null}function bt(t,e=null,n=Number,r="undefined"!=typeof BigInt&&BigInt){const i="0x"+(t.replace(/[^0-9a-fA-F]+/g,"")||27);return e&&r&&n.isSafeInteger&&!n.isSafeInteger(+i)?n(r(i))%e+e:+i}function wt(t,e=27){return!t||t%e?t%e:[0,1].includes(e)?e:wt(t/e,e)}function xt(t,e,n){if(!t||!t.length)return;const r=bt(n),i=wt(r)+5;let s=function(t,e,n){let r="";for(;t&&n&&e<=t.length;)r+=t.substring(0,e),t=t.substring(e+1),e=n;return r+t}(t,wt(r,5),i);return s=s.replace(/\x7c$/g,"==").replace(/\x2f$/g,"="),s=atob(s),s=s.replace(/[\x41-\x5A]/g,""),s=function(t,e,n){const r=+("0x"+t.substring(0,4));t=t.substring(4);const i=bt(e,r)%r+n%27,s=[];for(let e=0;e<t.length;e+=2){if("|"===t[e]){const n=+("0x"+t.substring(e+1,e+1+4))-i;e+=3,s.push(n);continue}const n=+("0x"+t[e]+t[e+1])-i;s.push(n)}return String.fromCharCode(...s)}(s,e,r),s=JSON.parse(s),s}const At=[{key:"alternate",def:!1},{key:"fill",def:1},{key:"iterations",def:0},{key:"direction",def:1},{key:"speed",def:1},{key:"fps",def:100}],_t="91c80d77";let kt=class{_svg;_rootId;constructor(t){this._id=0,this._running=!1,this._rollingBack=!1,this._animations=t.animations,this._settings=t.animationSettings,t.version<"2022-05-02"&&delete this._settings.speed,At.forEach(t=>{this._settings[t.key]=this._settings[t.key]||t.def}),this.duration=t.animationSettings.duration,this.offset=t.animationSettings.offset||0,this.rollbackStartOffset=0,this._rootId=t.root,this._svg=t.svg,this._originalAnimations=t.originalAnimations,this._fTC=new T(this)}get svg(){return this._svg}get rootId(){return this._rootId}get alternate(){return this._settings.alternate}get fill(){return this._settings.fill}get iterations(){return this._settings.iterations}get direction(){return this._settings.direction}get speed(){return this._settings.speed}get fps(){return this._settings.fps}get wD(){return this._settings.w}get originalAnimations(){return this._originalAnimations}get maxFiniteDuration(){return this.iterations>0?this.iterations*this.duration:this.duration}static build(t,e){if(delete t.animationSettings,t.options=xt(t.options,t.root,_t),t.animations.map(e=>{e.settings=xt(e.s,t.root,_t),delete e.s,t.animationSettings||(t.animationSettings=e.settings)}),Object.assign(t,{originalAnimations:t.animations},function(t,e){if(lt=e,!t||!t.root||!Array.isArray(t.animations))return null;const n=t.svg||vt(t);if(!n)return null;const r=t.animations.map(t=>mt(n,t)).filter(t=>!!t);return r.length?{svg:n,animations:r}:null}(t,e)),null==t||!t.svg)return null;const n=t.options||{},r=new this(t);return{el:t.svg,options:n,player:r}}static push(t){return this.build(t)}static init(){const t=window.__SVGATOR_PLAYER__&&window.__SVGATOR_PLAYER__[_t];Array.isArray(t)&&t.splice(0).forEach(t=>this.build(t))}_apply(t,e={}){const n=this._animations,r=n.length;let i=0;for(let s=0;s<r;s++)e[s]?i++:(e[s]=n[s](t,1),e[s]&&i++);return i}_rollback(t){let e=1/0,n=null;this.rollbackStartOffset=t,this._rollingBack=!0,this._running=!0;const r=i=>{if(!this._rollingBack)return;null==n&&(n=i);let s=Math.round(t-(i-n)*this.speed);if(s>this.duration&&e!==1/0){const t=!!this.alternate&&s/this.duration%2>1;let e=s%this.duration;e+=t?this.duration:0,s=e||this.duration}const o=(this.fps?1e3/this.fps:0)*this.speed,u=Math.max(0,s);u<=e-o&&(this.offset=u,e=u,this._apply(u));const l=this.iterations>0&&-1===this.fill&&s>=this.maxFiniteDuration;(s<=0||this.offset<s||l)&&this.stop(),this._id=window.requestAnimationFrame(r)};this._id=window.requestAnimationFrame(r)}_start(t=0){let e,n=-1/0;const r={};this._running=!0;const i=s=>{e??=s;const o=Math.round((s-e)*this.speed+t),u=(this.fps?1e3/this.fps:0)*this.speed;o>=n+u&&!this._rollingBack&&this._fTC.cF(s,()=>{this.offset=o,n=o;if(this._apply(o,r)===this._animations.length)return this.pause(!0),!0})||(this._id=window.requestAnimationFrame(i))};this._id=window.requestAnimationFrame(i)}_pause(){this._id&&window.cancelAnimationFrame(this._id),this._running=!1}play(){if(!this._running)return this._rollingBack?this._rollback(this.offset):this._start(this.offset)}stop(){this._pause(),this.offset=0,this.rollbackStartOffset=0,this._rollingBack=!1,this._apply(0)}reachedToEnd(){return this.iterations>0&&this.offset>=this.iterations*this.duration}restart(t=!1){this.stop(t),this.play(t)}pause(){this._pause()}toggle(){return this._running?this.pause():this.reachedToEnd()?this.restart():this.play()}trigger(t,e){}_adjustOffset(t=!1){const e=this.alternate?2*this.duration:this.duration;if(t){if(!this._rollingBack&&0===this.offset)return void(this.offset=e);this._rollingBack&&(this.offset,this.maxFiniteDuration)}!this._rollingBack||this.rollbackStartOffset<=this.duration?0!==this.iterations&&(this.offset=Math.min(this.offset,this.maxFiniteDuration)):(this.offset=this.rollbackStartOffset-(this.rollbackStartOffset-this.offset)%e,this.rollbackStartOffset=0)}reverse(t=!1){if(!this._running)return this._adjustOffset(t),this._rollingBack=!this._rollingBack,t&&this.play(!1),void this.trigger("reverse",this.offset);this.pause(!1,!1),this._adjustOffset(),this._rollingBack=!this._rollingBack,this.play(!1),this.trigger("reverse",this.offset)}};function St(t){return n(t)+""}function Et(t,e=" "){return t&&t.length?t.map(St).join(e):""}function It(t){return St(t.x)+","+St(t.y)}function Ot(t){return t?null==t.a||t.a>=1?function(t){if(!t)return"transparent";const e=t=>parseInt(t).toString(16).padStart(2,"0");return function(t){const e=[];let n="#"===t[0]?e.push("#"):0;for(;n<t.length;n+=2){if(t[n]!==t[n+1])return t;e.push(t[n])}return e.join("")}("#"+e(t.r)+e(t.g)+e(t.b)+(null==t.a||t.a>=1?"":e(255*t.a)))}(t):"rgba("+t.r+","+t.g+","+t.b+","+t.a+")":"transparent"}function Mt(t){return t?"url(#"+t+")":"none"}!function(){for(var t=0,e=["ms","moz","webkit","o"],n=0;n<e.length&&!window.requestAnimationFrame;++n)window.requestAnimationFrame=window[e[n]+"RequestAnimationFrame"],window.cancelAnimationFrame=window[e[n]+"CancelAnimationFrame"]||window[e[n]+"CancelRequestAnimationFrame"];window.requestAnimationFrame||(window.requestAnimationFrame=function(e){var n=Date.now(),r=Math.max(0,16-(n-t)),i=window.setTimeout(function(){e(n+r)},r);return t=n+r,i},window.cancelAnimationFrame=window.clearTimeout)}();var Bt={f:null,i:Z,u:(t,e)=>n=>{const r=e(n);t.setAttribute("rx",St(r.x)),t.setAttribute("ry",St(r.y))}},Nt={f:null,i:function(t,e,n){return 0===t?e:1===t?n:{width:X(t,e.width,n.width),height:X(t,e.height,n.height)}},u:(t,e)=>n=>{const r=e(n);t.setAttribute("width",St(r.width)),t.setAttribute("height",St(r.height))}};let jt={},Tt=null;function Pt(t){let e=function(){if(Tt)return Tt;if("object"!=typeof document||!document.createElementNS)return{};let t=document.createElementNS("http://www.w3.org/2000/svg","svg");return t&&t.style?(t.style.position="absolute",t.style.opacity="0.01",t.style.zIndex="-9999",t.style.left="-9999px",t.style.width="1px",t.style.height="1px",Tt={svg:t},Tt):{}}().svg;if(!e)return function(t){return null};let n=document.createElementNS(e.namespaceURI,"path");n.setAttributeNS(null,"d",t),n.setAttributeNS(null,"fill","none"),n.setAttributeNS(null,"stroke","none"),e.appendChild(n);let r=n.getTotalLength();return function(t){let e=n.getPointAtLength(r*t);return{x:e.x,y:e.y}}}function Ft(t,e,n,r,i=1){let s=function(t){return jt[t]?jt[t]:jt[t]=Pt(t)}(function(t,e,n,r){if(!t||!r)return!1;let i=["M",t.x,t.y];if(e&&n&&(i.push("C"),i.push(e.x),i.push(e.y),i.push(n.x),i.push(n.y)),e?!n:n){let t=e||n;i.push("Q"),i.push(t.x),i.push(t.y)}return e||n||i.push("L"),i.push(r.x),i.push(r.y),i.join(" ")}(t,e,n,r));try{return s(i)}catch(t){return null}}function Rt(t,e,n){return t+(e-t)*n}function Ct(t,e,n,r=!1){const i={x:Rt(t.x,e.x,n),y:Rt(t.y,e.y,n)};return r&&(i.a=function(t,e){return Math.atan2(e.y-t.y,e.x-t.x)}(t,e)),i}function Vt(t,e,n,r){const i=1-r;return i*i*t+2*i*r*e+r*r*n}function Dt(t,e,n,r){return 2*(1-r)*(e-t)+2*r*(n-e)}function $t(t,e,n,r,i=!1){let s=Ft(t,e,null,n,r);return s||(s={x:Vt(t.x,e.x,n.x,r),y:Vt(t.y,e.y,n.y,r)}),i&&(s.a=function(t,e,n,r){return Math.atan2(Dt(t.y,e.y,n.y,r),Dt(t.x,e.x,n.x,r))}(t,e,n,r)),s}function qt(t,e,n,r,i){const s=i*i;return i*s*(r-t+3*(e-n))+3*s*(t+n-2*e)+3*i*(e-t)+t}function Lt(t,e,n,r,i){const s=1-i;return 3*(s*s*(e-t)+2*s*i*(n-e)+i*i*(r-n))}function zt(t,e,n,r,i,s=!1){let o=Ft(t,e,n,r,i);return o||(o={x:qt(t.x,e.x,n.x,r.x,i),y:qt(t.y,e.y,n.y,r.y,i)}),s&&(o.a=function(t,e,n,r,i){return Math.atan2(Lt(t.y,e.y,n.y,r.y,i),Lt(t.x,e.x,n.x,r.x,i))}(t,e,n,r,i)),o}function Gt(t,e,n,r=!1){if(Yt(e)){if(Ut(n))return $t(e,n.start,n,t,r)}else if(Yt(n)){if(Ht(e))return $t(e,e.end,n,t,r)}else{if(Ht(e))return Ut(n)?zt(e,e.end,n.start,n,t,r):$t(e,e.end,n,t,r);if(Ut(n))return $t(e,n.start,n,t,r)}return Ct(e,n,t,r)}function Wt(t,e,n){const r=Gt(t,e,n,!0);return r.a=o(function(t,e=!1){return e?t+Math.PI:t}(r.a)),r}function Yt(t){return!t.type||"corner"===t.type}function Ut(t){return null!=t.start&&!Yt(t)}function Ht(t){return null!=t.end&&!Yt(t)}const Jt=new class{constructor(t=1,e=0,n=0,r=1,i=0,s=0){this.m=[t,e,n,r,i,s],this.i=null,this.w=null,this.s=null}get determinant(){const t=this.m;return t[0]*t[3]-t[1]*t[2]}get isIdentity(){if(null===this.i){const t=this.m;this.i=1===t[0]&&0===t[1]&&0===t[2]&&1===t[3]&&0===t[4]&&0===t[5]}return this.i}point(t,e){const n=this.m;return{x:n[0]*t+n[2]*e+n[4],y:n[1]*t+n[3]*e+n[5]}}translateSelf(t=0,e=0){if(!t&&!e)return this;const n=this.m;return n[4]+=n[0]*t+n[2]*e,n[5]+=n[1]*t+n[3]*e,this.w=this.s=this.i=null,this}rotateSelf(t=0){if(t%=360){t=s(t);const e=$(t),n=q(t),r=this.m,i=r[0],o=r[1];r[0]=i*n+r[2]*e,r[1]=o*n+r[3]*e,r[2]=r[2]*n-i*e,r[3]=r[3]*n-o*e,this.w=this.s=this.i=null}return this}scaleSelf(t=1,e=1){if(1!==t||1!==e){const n=this.m;n[0]*=t,n[1]*=t,n[2]*=e,n[3]*=e,this.w=this.s=this.i=null}return this}skewSelf(t,e){if(e%=360,(t%=360)||e){const n=this.m,r=n[0],i=n[1],o=n[2],u=n[3];t&&(t=G(s(t)),n[2]+=r*t,n[3]+=i*t),e&&(e=G(s(e)),n[0]+=o*e,n[1]+=u*e),this.w=this.s=this.i=null}return this}resetSelf(t=1,e=0,n=0,r=1,i=0,s=0){const o=this.m;return o[0]=t,o[1]=e,o[2]=n,o[3]=r,o[4]=i,o[5]=s,this.w=this.s=this.i=null,this}recomposeSelf(t=null,e=null,n=null,r=null,i=null){return this.isIdentity||this.resetSelf(),t&&(t.x||t.y)&&this.translateSelf(t.x,t.y),e&&this.rotateSelf(e),n&&(n.x&&this.skewSelf(n.x,0),n.y&&this.skewSelf(0,n.y)),!r||1===r.x&&1===r.y||this.scaleSelf(r.x,r.y),i&&(i.x||i.y)&&this.translateSelf(i.x,i.y),this}decompose(t=0,e=0){const r=this.m,i=r[0]*r[0]+r[1]*r[1],s=[[r[0],r[1]],[r[2],r[3]]];let u=Y(i);if(0===u)return{origin:{x:n(r[4]),y:n(r[5])},translate:{x:n(t),y:n(e)},scale:{x:0,y:0},skew:{x:0,y:0},rotate:0};s[0][0]/=u,s[0][1]/=u;const l=r[0]*r[3]-r[1]*r[2]<0;l&&(u=-u);let a=s[0][0]*s[1][0]+s[0][1]*s[1][1];s[1][0]-=s[0][0]*a,s[1][1]-=s[0][1]*a;let c,f=Y(s[1][0]*s[1][0]+s[1][1]*s[1][1]);return 0===f?{origin:{x:n(r[4]),y:n(r[5])},translate:{x:n(t),y:n(e)},scale:{x:n(u),y:0},skew:{x:0,y:0},rotate:0}:(s[1][0]/=f,s[1][1]/=f,a/=f,s[1][1]<0?(c=o(L(s[1][1])),s[0][1]<0&&(c=360-c)):c=o(z(s[0][1])),l&&(c=-c),a=o(W(a,Y(s[0][0]*s[0][0]+s[0][1]*s[0][1]))),l&&(a=-a),{origin:{x:n(r[4]),y:n(r[5])},translate:{x:n(t),y:n(e)},scale:{x:n(u),y:n(f)},skew:{x:n(a),y:0},rotate:n(c)})}multiply(t){return this.clone().multiplySelf(t)}preMultiply(t){return t.multiply(this)}multiplySelf(t){const{a:e,b:n,c:r,d:i,tx:s,ty:o}=U(this.m,t.m);return this.resetSelf(e,n,r,i,s,o),this}preMultiplySelf(t){const{a:e,b:n,c:r,d:i,tx:s,ty:o}=U(t.m,this.m);return this.resetSelf(e,n,r,i,s,o),this}clone(){const t=this.m;return new this.constructor(t[0],t[1],t[2],t[3],t[4],t[5])}static create(t){return t?Array.isArray(t)?new this(...t):t instanceof this?t.clone():(new this).recomposeSelf(t.origin,t.rotate,t.skew,t.scale,t.translate):new this}toString(t=" ",e=!1){if(null===this.s){let r=this.m.map(t=>n(t));e||1!==r[0]||0!==r[1]||0!==r[2]||1!==r[3]?this.s="matrix("+r.join(t)+")":this.s="translate("+r[4]+t+r[5]+")"}return this.s}};var Xt={f:function(t){return t?t.join(" "):""},i:function(t,e,n){if(0===t)return e;if(1===t)return n;let r=e.length;if(r!==n.length)return H(t,e,n);let i,s=new Array(r);for(let o=0;o<r;o++){if(i=typeof e[o],i!==typeof n[o])return H(t,e,n);if("number"===i)s[o]=J(t,e[o],n[o]);else{if(e[o]!==n[o])return H(t,e,n);s[o]=e[o]}}return s}},Kt={f:Et,i:nt},Qt={f:null,i:nt,u:(t,e)=>n=>{const r=e(n);t.setAttribute("x1",St(r[0])),t.setAttribute("y1",St(r[1])),t.setAttribute("x2",St(r[2])),t.setAttribute("y2",St(r[3]))}},Zt={f:St,i:J},te={f:St,i:K},ee={f:function(t,e=" "){return t&&t.length>0&&(t=t.map(t=>n(t,4))),Et(t,e)},i:function(t,e,r){let i=e.length,s=r.length;if(i!==s)if(0===i)i=s,e=rt(i,0);else if(0===s)s=i,r=rt(i,0);else{const t=function(t,e){const n=t*e/function(t,e){let n;for(;e;)n=e,e=t%e,t=n;return t||1}(t,e);return n<0?-n:n}(i,s);e=it(e,Math.floor(t/i)),r=it(r,Math.floor(t/s)),i=s=t}let o=[];for(let s=0;s<i;s++)o.push(n(X(t,e[s],r[s])));return o}},ne={f:St,i:J};function re(t,e,r){return t.map(t=>function(t,e,r){let i=t.v;if(!i||"g"!==i.t||i.s||!i.v||!i.r)return t;const s=r.getElementById(i.r),o=s&&s.querySelectorAll("stop")||[];return i.s=i.v.map((t,e)=>{let r=o[e]&&o[e].getAttribute("offset");return r=n(parseInt(r)/100),{c:t,o:r}}),delete i.v,t}(t,0,r))}const ie={gt:"gradientTransform",c:{x:"cx",y:"cy"},rd:"r",f:{x:"x1",y:"y1"},to:{x:"x2",y:"y2"}};function se(t,e,n,r,i,s,o,u){return re(t,0,u),e=function(t,e,n){let r,i,s;const o=t.length-1,u={};for(let l=0;l<=o;l++)r=t[l],r.e&&(r.e=e(r.e)),r.v&&(i=r.v,"g"===i.t&&i.r&&(s=n.getElementById(i.r),s&&(u[i.r]={e:s,s:s.querySelectorAll("stop")})));return u}(t,r,u),r=>{const i=n(r,t,oe);if(!i)return"none";if("c"===i.t)return Ot(i.v);if("g"===i.t){if(!e[i.r])return Mt(i.r);const t=e[i.r];return function(t,e){let n=t.s;for(let r=n.length;r<e.length;r++){const e=n[n.length-1].cloneNode();e.id=ae(e.id),t.e.appendChild(e),n=t.s=t.e.querySelectorAll("stop")}for(let t=0,r=n.length,i=e.length-1;t<r;t++)n[t].setAttribute("stop-color",Ot(e[Math.min(t,i)].c)),n[t].setAttribute("offset",e[Math.min(t,i)].o)}(t,i.s),Object.keys(ie).forEach(e=>{if(void 0===i[e])return;if("object"==typeof ie[e])return void Object.keys(ie[e]).forEach(n=>{if(void 0===i[e][n])return;const r=i[e][n],s=ie[e][n];t.e.setAttribute(s,r)});const n="gt"===e?(r=i[e],Array.isArray(r)?"matrix("+r.join(" ")+")":""):i[e];var r;const s=ie[e];t.e.setAttribute(s,n)}),Mt(i.r)}return"none"}}function oe(t,e,n){if(0===t)return e;if(1===t)return n;if(e&&n){const r=e.t;if(r===n.t)switch(e.t){case"c":return{t:r,v:et(t,e.v,n.v)};case"g":if(e.r===n.r){const i={t:r,s:ue(t,e.s,n.s),r:e.r};return e.gt&&n.gt&&(i.gt=nt(t,e.gt,n.gt)),e.c?(i.c=Q(t,e.c,n.c),i.rd=X(t,e.rd,n.rd)):e.f&&(i.f=Q(t,e.f,n.f),i.to=Q(t,e.to,n.to)),i}}if("c"===e.t&&"g"===n.t||"c"===n.t&&"g"===e.t){const r="c"===e.t?e:n,i="g"===e.t?{...e}:{...n},s=i.s.map(t=>({c:r.v,o:t.o}));return i.s="c"===e.t?ue(t,s,i.s):ue(t,i.s,s),i}}return H(t,e,n)}function ue(t,e,n){if(e.length===n.length)return e.map((e,r)=>le(t,e,n[r]));const r=Math.max(e.length,n.length),i=[];for(let s=0;s<r;s++){const r=le(t,e[Math.min(s,e.length-1)],n[Math.min(s,n.length-1)]);i.push(r)}return i}function le(t,e,n){return{o:K(t,e.o,n.o||0),c:et(t,e.c,n.c||{})}}function ae(t){return t.replace(/-fill-([0-9]+)$/,(t,e)=>"-fill-"+(+e+1))}function ce(t,e,n){return 0===t?e:1===t?n:{blur:Z(t,e.blur,n.blur),offset:Q(t,e.offset,n.offset),color:et(t,e.color,n.color)}}const fe={blur:Z,brightness:X,contrast:X,"drop-shadow":ce,"inner-shadow":ce,grayscale:X,"hue-rotate":J,invert:X,opacity:X,saturate:X,sepia:X};function he(t,e,n){if(0===t)return e;if(1===t)return n;const r=e.length;if(r!==n.length)return H(t,e,n);const i=[];let s;for(let o=0;o<r;o++){if(e[o].type!==n[o].type)return e;if(s=fe[e[o].type],!s)return H(t,e,n);i.push({type:e.type,value:s(t,e[o].value,n[o].value)})}return i}const de={blur:t=>t?e=>{t.setAttribute("stdDeviation",It(e))}:null,brightness:(t,e,n)=>(t=pe(n,e))?e=>{e=St(e),t.map(t=>t.setAttribute("slope",e))}:null,contrast:(t,e,n)=>(t=pe(n,e))?e=>{const n=St((1-e)/2);e=St(e),t.map(t=>{t.setAttribute("slope",e),t.setAttribute("intercept",n)})}:null,"drop-shadow"(t,e,n){const r=n.getElementById(e+"-blur");if(!r)return null;const i=n.getElementById(e+"-offset");if(!i)return null;const s=n.getElementById(e+"-flood");return s?t=>{r.setAttribute("stdDeviation",It(t.blur)),i.setAttribute("dx",St(t.offset.x)),i.setAttribute("dy",St(t.offset.y)),s.setAttribute("flood-color",Ot(t.color))}:null},"inner-shadow"(t,e,n){const r=n.getElementById(e+"-blur");if(!r)return null;const i=n.getElementById(e+"-offset");if(!i)return null;const s=n.getElementById(e+"-color-matrix");return s?t=>{r.setAttribute("stdDeviation",It(t.blur)),i.setAttribute("dx",St(t.offset.x)),i.setAttribute("dy",St(t.offset.y));const e=[0,0,0,0,t.color.r/255,0,0,0,0,t.color.g/255,0,0,0,0,t.color.b/255,0,0,0,t.color.a,0];s.setAttribute("values",Et(e))}:null},grayscale:t=>t?e=>{t.setAttribute("values",Et(function(t){return[.2126+.7874*(t=1-t),.7152-.7152*t,.0722-.0722*t,0,0,.2126-.2126*t,.7152+.2848*t,.0722-.0722*t,0,0,.2126-.2126*t,.7152-.7152*t,.0722+.9278*t,0,0,0,0,0,1,0]}(e)))}:null,"hue-rotate":t=>t?e=>t.setAttribute("values",St(e)):null,invert:(t,e,n)=>(t=pe(n,e))?e=>{e=St(e)+" "+St(1-e),t.map(t=>t.setAttribute("tableValues",e))}:null,opacity:(t,e,n)=>(t=n.getElementById(e+"-A"))?e=>t.setAttribute("tableValues","0 "+St(e)):null,saturate:t=>t?e=>t.setAttribute("values",St(e)):null,sepia:t=>t?e=>t.setAttribute("values",Et(function(t){return[.393+.607*(t=1-t),.769-.769*t,.189-.189*t,0,0,.349-.349*t,.686+.314*t,.168-.168*t,0,0,.272-.272*t,.534-.534*t,.131+.869*t,0,0,0,0,0,1,0]}(e))):null};const ge=["R","G","B"];function pe(t,e){const n=ge.map(n=>t.getElementById(e+"-"+n)||null);return-1!==n.indexOf(null)?null:n}var ye={fill:se,"fill-opacity":te,stroke:se,"stroke-opacity":te,"stroke-width":Zt,"stroke-dashoffset":ne,"stroke-dasharray":ee,opacity:te,transform:function(t,e,n,r){if(!(t=function(t,e){if(!t||"object"!=typeof t)return null;let n=!1;for(const r in t)t.hasOwnProperty(r)&&(t[r]&&t[r].length?(t[r].forEach(t=>{t.e&&(t.e=e(t.e))}),n=!0):delete t[r]);return n?t:null}(t,r)))return null;const i=(r,i,s,o=null)=>t[r]?n(i,t[r],s):e&&e[r]?e[r]:o;return e&&e.a&&t.o?e=>{const r=n(e,t.o,Wt);return Jt.recomposeSelf(r,i("r",e,J,0)+r.a,i("k",e,Q),i("s",e,Q),i("t",e,Q)).toString()}:t=>Jt.recomposeSelf(i("o",t,Gt,null),i("r",t,J,0),i("k",t,Q),i("s",t,Q),i("t",t,Q)).toString()},"#filter":function(t,e,n,r,i,s,o,u){if(!e.items||!t||!t.length)return null;const l=function(t,e){t=t.map(t=>t&&de[t[0]]?(e.getElementById(t[1]),de[t[0]](e.getElementById(t[1]),t[1],e)):null);const n=t.length;return e=>{for(let r=0;r<n;r++)t[r]&&t[r](e[r].value)}}(e.items,u);return l?(t=function(t,e){return t.map(t=>(t.e=e(t.e),t))}(t,r),e=>{l(n(e,t,he))}):null},"#line":Qt,points:Kt,d:Xt,r:Zt,"#size":Nt,"#radius":Bt,_(t,e){if(Array.isArray(t))for(let n=0;n<t.length;n++)this[t[n]]=e;else this[t]=e}};const me={currentTime:"offset",duration:"duration",hasEnded:function(){return this.reachedToEnd()},isAlternate:"alternate",isPlaying:"_running",isRollingBack:"_rollingBack",state:function(t,e){return e.isPlaying?e.isRollingBack?"rollback":"playing":e.hasEnded?"ended":"paused"},totalTime:"maxFiniteDuration",iterations:"iterations",direction:"direction",fill:"fill",isReversed:function(t,e){return-1===e.direction},isBackwards:function(t,e){return-1===e.fill},isInfinite:function(t,e){return 0===e.iterations},speed:"speed",fps:"fps"},ve={destruct:"destruct",pause:"pause",play:function(t,e){return be(t,e.hasEnded?"restart":"play",e)},restart:"restart",reverse:function(t,e){return be(t,"reverse",e,[!0])},seek:"seek",seekBy:"seekBy",seekTo:"seekTo",stop:"stop",toggle:"toggle",togglePlay:"toggle",set:"set"};function be(t,e,n,r=[]){return function(){const i=[...arguments];return i.unshift(...r),t[e].call(t,...i),n}}class we{constructor(t){const e={},n=["on","off"],r={get:function(t,r,i){return me[r]?"function"==typeof me[r]?me[r].call(t,t,i):t[me[r]]:ve[r]?"function"==typeof ve[r]?ve[r].call(t,t,i):be(t,ve[r],i):-1!==n.indexOf(r)?e[r]:"ready"===r?t=>(t&&t.call(i,i),i):void 0},set:function(t,r,i){return-1!==n.indexOf(r)&&(e[r]=i)},ownKeys:function(t){return Object.keys(me)},has:function(t,e){return void 0!==me[e]}};if("function"==typeof Proxy)return new Proxy(t,r);const i=Object.keys(me).concat(Object.keys(ve)).concat(n),s={};return i.forEach(e=>{const i={enumerable:!1,configurable:!1,get:()=>r.get(t,e,s)};-1!==n.indexOf(e)&&(i.set=n=>r.set(t,e,n)),Object.defineProperty(s,e,i)}),s}}function xe(t){t||(t=this);let e={};this.on=function(t,n,r=!1){return"function"==typeof n&&(t.split(/[, ]+/g).forEach(t=>(e[t]=e[t]||[],r?e[t].unshift(n):e[t].push(n))),!0)},this.off=function(t,n){for(let r in e)if(e.hasOwnProperty(r)&&r.substr(0,t.length)===t)if(n)for(let t=0;t<e[r].length;t++)e[r][t]===n&&(e[r][t]=null);else e[r]=null},this.trigger=function(){let n,[r,...i]=[...arguments];t:for(let s in e)if(e.hasOwnProperty(s)&&e[s]&&(s===r||s.substr(0,r.length+1)===r+"."))for(let r=0;r<(e[s]||[]).length;r++)if(e[s][r]&&(n=e[s][r].apply(t,i),!1===n))break t;return n}}let Ae=function(t,e,n=n){let r=!1,i=null;return function(s){r&&clearTimeout(r),r=setTimeout(()=>function(){let s=0,o=n.innerHeight,u=0,l=n.innerWidth,a=t.parentNode;for(;a instanceof Element;){let t=n.getComputedStyle(a);if("visible"!==t.overflowY||"visible"!==t.overflowX){let e=a.getBoundingClientRect();"visible"!==t.overflowY&&(s=Math.max(s,e.top),o=Math.min(o,e.bottom)),"visible"!==t.overflowX&&(u=Math.max(u,e.left),l=Math.min(l,e.right))}if(a===a.parentNode)break;a=a.parentNode}r=!1;let c=t.getBoundingClientRect(),f=Math.min(c.height,Math.max(0,s-c.top)),h=Math.min(c.height,Math.max(0,c.bottom-o)),d=Math.min(c.width,Math.max(0,u-c.left)),g=Math.min(c.width,Math.max(0,c.right-l)),p=(c.height-f-h)/c.height,y=(c.width-d-g)/c.width,m=Math.round(p*y*100);null!==i&&i===m||(i=m,e(m))}(),100)}};class _e{constructor(t,e,n){const r=function(t){var e;const n=t&&1===(null===(e=t.ownerDocument)||void 0===e||null===(e=e.childNodes)||void 0===e?void 0:e.length)&&window.parent!==window,r={el:t,window:window};if(!n)return r;let i;try{i=window.parent.document}catch(t){return r}return r.window=window.parent,r.el=Array.from(i.querySelectorAll("iframe,object")).filter(t=>t.contentWindow===window)[0]||r.el,r}(t);e=Math.max(1,e||1),e=Math.min(e,100),this.el=r.el,this._handlers=[],this.onThresholdChange=n&&n.call?n:()=>{},this.thresholdPercent=e||1,this.currentVisibility=null,this.visibilityCalculator=Ae(this.el,this.onVisibilityUpdate.bind(this),r.window),this.bindScrollWatchers(),this.visibilityCalculator()}bindScrollWatchers(){let t=this.el.parentNode;for(;t&&(this._handlers.push({element:t,event:"scroll",handler:this.visibilityCalculator}),t.addEventListener("scroll",this.visibilityCalculator),t!==t.parentNode&&t!==document);)t=t.parentNode}onVisibilityUpdate(t){let e=this.currentVisibility>=this.thresholdPercent,n=t>=this.thresholdPercent;if(null===this.currentVisibility||e!==n)return this.currentVisibility=t,void this.onThresholdChange(n);this.currentVisibility=t}destruct(){this._handlers.forEach(t=>{t.element.removeEventListener(t.event,t.handler)})}}class ke{static adjustLink(t){var e;const n=t&&1===(null===(e=t.ownerDocument)||void 0===e||null===(e=e.childNodes)||void 0===e?void 0:e.length)&&window.parent!==window,r=null==t?void 0:t.firstElementChild;n&&r&&"a"===r.tagName&&!r.getAttribute("target")&&r.setAttributeNS(null,"target","_parent")}static autoPlay(t,e,n,r=[]){if("click"===n.start){const i=()=>{switch(n.click){case"freeze":return!t._running&&t.reachedToEnd()?t.restart():t.toggle();case"restart":return t.offset>0?t.restart():t.play();case"reverse":return t._running?t.reverse():t.reachedToEnd()?1===t.fill?t.reverse(!0):t.restart():t.play();default:if(t._running)return;return t.reachedToEnd()?t.restart():t.play()}};return r.push({element:e,event:"click",handler:i}),void e.addEventListener("click",i)}if("hover"===n.start){const i=()=>t.reachedToEnd()?t.restart():t._rollingBack?t.reverse():t.play();r.push({element:e,event:"mouseenter",handler:i}),e.addEventListener("mouseenter",i);const s=()=>{switch(n.hover){case"freeze":return t.pause();case"reset":return t.stop();case"reverse":if(t.reverse(),t._running)return;return t.play();default:return}};return r.push({element:e,event:"mouseleave",handler:s}),void e.addEventListener("mouseleave",s)}if("scroll"===n.start){const i=new _e(e,n.scroll||25,function(e){e?t.reachedToEnd()?t.restart():t.play():t.pause()});return void r.push({callback:()=>i.destruct()})}"programmatic"!==n.start&&t.play()}}const Se=!0,Ee=["iterations","speed","fps","direction","fill","alternate"];class Ie extends kt{constructor(t){super(t),this._handlers=[]}static build(t){var e,n;let r=super.build(t,ye);if(!r)return null;let{el:i,options:s,player:o}=r;const u=new we(o),l=new xe(u);u.on=l.on,u.off=l.off,o.trigger=l.trigger;const a=null==i||null===(e=i.svgatorPlayer)||void 0===e||null===(e=e.ready)||void 0===e||null===(n=e.call)||void 0===n?void 0:n.call(e);return i.svgatorPlayer=u,ke.adjustLink(i),ke.autoPlay(o,i,s,o._handlers),function(t,e,n){let r;"function"==typeof Event?r=new Event("ready"):(r=document.createEvent("Event"),r.initEvent("ready",!0,!0));if(t.dispatchEvent(r),!n||!n.length)return;n.forEach(t=>e.ready(t))}(i,i.svgatorPlayer,a),u}play(t=true){const e=super.play();return t===Se&&this.trigger("play",this.offset),e}pause(t=!1,e=true){const n=super.pause();return e===Se&&this.trigger(t?"end":"pause",this.offset),n}restart(){const t=super.restart(!1);return this.trigger("restart",this.offset),t}stop(t=true){const e=super.stop();return t===Se&&this.trigger("stop",this.offset),e}_apply(t,e={},n=true){const r=super._apply(t);if(n===Se){const e=()=>this.trigger("keyframe",t);window.requestAnimationFrame(e)}return r}seekTo(t){const e=this._running;var n,r,i;e&&this.pause(!1,!1),this.offset=this.iterations>0?(n=t,r=0,i=this.maxFiniteDuration,n<r?r:n>i?i:n):Math.max(t,0),this._apply(this.offset),e&&this.play(!1)}seek(t){return this.seekTo(Math.round(t/100*this.maxFiniteDuration))}seekBy(t){return this.seekTo(this.offset+t)}set(t,e){if(!Ee.includes(t))return;const n=this._running;n&&this.pause(!1,!1),this._settings[t]=e,n?this.play(!1):this._apply(this.offset,{},!1)}destruct(){this.stop(),this._handlers.forEach(t=>{t.element?t.element.removeEventListener(t.event,t.handler):t.callback&&t.callback.call&&t.callback.call()});const t=()=>{},e=Object.getOwnPropertyNames(Object.getPrototypeOf(this));e.push(...Object.getOwnPropertyNames(this)),e.forEach(e=>{"function"==typeof this[e]?this[e]=t:delete this[e]})}}return Ie.init(),Ie}); |
There was a problem hiding this comment.
Wonder if it'd be better to move this to its own file just to keep the code a bit more readable, as in theory is the "general" tool (in contrast to the code for the specific animation below).
There was a problem hiding this comment.
I'd considered this, but could not find a precedent, and figured "if it ain't broke" after the mess I made prior to getting @LianaHarris360's code in 😅
There was a problem hiding this comment.
Yeah, there isn't really any practical motivation other than abstracting this piece of code because we won't be mounting this component more than once at any given time, and we don't use this in other places (and if we do in the future, we can then figure it out 😅)
| for (let i = 0; i < iconCount; i++) { | ||
| const id = sequence.value[i]; | ||
| const isLast = i === sequence.value.length - 1; | ||
| const isLast = i === iconCount - 1; | ||
| window.setTimeout(() => { | ||
| bouncingId.value = id; | ||
| if (isLast) arrowBouncing.value = true; | ||
| if (isLast) { | ||
| arrowBouncing.value = true; | ||
| if (!reduce) { | ||
| burstVisible.value = true; | ||
| window.setTimeout(() => { | ||
| burstVisible.value = false; | ||
| }, burstDuration); | ||
| } | ||
| } | ||
| window.setTimeout(() => { | ||
| if (bouncingId.value === id) bouncingId.value = null; | ||
| if (isLast) arrowBouncing.value = false; | ||
| }, dur); | ||
| }, iconDuration); | ||
| }, i * stagger); | ||
| } | ||
|
|
||
| window.setTimeout(() => resolve(), (sequence.value.length - 1) * stagger + dur); | ||
| const lastStart = (iconCount - 1) * stagger; | ||
| const animationDuration = Math.max(lastStart + iconDuration, lastStart + burstDuration); | ||
| window.setTimeout(() => resolve(), animationDuration); |
There was a problem hiding this comment.
Could we define all of this in a more imperative way? I think defining that many setTimeOuts having to manually compute how much time will it take and having to check an isLast to know if the animation burst needs to be triggered is too complex.
I think we can just create a wait(time)=> new Promise(resolve => setTimeout(resolve, time)) helper, and then just call await wait(STAGGER) after every sequence item but the last one would make all of this much more readable.
something like
for (i in ..) {
playAnimationFor(i);
if (!isLast) {
await wait(STAGGER)
}
}
playBurstAnimation();
await wait(BURST_DURATION);
resolve();
Then the resolve call is called idiomatically after all animations succeeded instead of computing what the wait time should be and modify it every time we add something new here.
Relatedly, if reduce is true, should we just call the resolve() without playing any animation and avoid the reduce ? 0 : ... computations?
There was a problem hiding this comment.
I have not ignored this, and am investigating! The general complication with the timings has to do with the orchestration of the various animations throughout picture login, so I'm trying to see what's compatible and how we might be able to streamline the JS/CSS pieces in a more legible format :)
That being said, I admittedly just sort of let the AI go wild here; there's a lot here 😬
|
Ah, my other high-level comment: would it be easier if we just played a |
This was not really my call, and therefore more a question for @LianaHarris360, I think? |
|
I'm not against giving it a try, especially if there's a decrease in the build size with the |
Summary
Screen.Recording.2026-06-01.at.12.50.02.PM.mov
References
…
Reviewer guidance
…
AI usage
Actually, honestly, like... none 🤯 (up to 0e17493, the implementation)
Although it helped with some of the bullet points in the 0e17493 commit message.