From 52b2af2463201623b315ba6afe74801e6fd838fa Mon Sep 17 00:00:00 2001 From: Arpan Pandey Date: Sun, 20 Apr 2025 13:42:55 +0100 Subject: [PATCH 01/15] Animation on hover w/ Gleam Logo --- app/components/gleam-logo.hbs | 9 ++ app/components/gleam-logo.ts | 111 ++++++++++++++++++ app/components/language-logo.hbs | 4 +- app/components/language-logo.ts | 1 + app/styles/components/gleam-logo.css | 12 ++ package-lock.json | 7 ++ package.json | 1 + .../animations/gleam_logo_animation.riv | Bin 0 -> 5220 bytes 8 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 app/components/gleam-logo.hbs create mode 100644 app/components/gleam-logo.ts create mode 100644 app/styles/components/gleam-logo.css create mode 100644 public/assets/animations/gleam_logo_animation.riv diff --git a/app/components/gleam-logo.hbs b/app/components/gleam-logo.hbs new file mode 100644 index 0000000000..995b192dcc --- /dev/null +++ b/app/components/gleam-logo.hbs @@ -0,0 +1,9 @@ +
+ {{!-- The canvas will be inserted here by Rive --}} +
\ No newline at end of file diff --git a/app/components/gleam-logo.ts b/app/components/gleam-logo.ts new file mode 100644 index 0000000000..da4a83ed43 --- /dev/null +++ b/app/components/gleam-logo.ts @@ -0,0 +1,111 @@ +import Component from '@glimmer/component'; +import { tracked } from '@glimmer/tracking'; +import { action } from '@ember/object'; +import { Rive } from '@rive-app/canvas'; + +export default class GleamLogoComponent extends Component { + @tracked riveInstance: Rive | null = null; + container: HTMLElement | null = null; + + @action + setupRive(element: HTMLElement) { + this.container = element; + + try { + // Create canvas element + const canvas = document.createElement('canvas'); + canvas.width = 141; // Fixed size for crisp rendering + canvas.height = 144; + canvas.style.width = '100%'; + canvas.style.height = '100%'; + canvas.style.border = '1px solid red'; // Visual debugging + element.appendChild(canvas); + + // Initialize Rive + this.riveInstance = new Rive({ + src: '/assets/animations/gleam_logo_animation.riv', + canvas: canvas, + autoplay: false, + onLoad: () => { + console.log('Gleam logo animation loaded'); + + // Log available state machines + const stateMachines = this.riveInstance?.stateMachineNames; + console.log('All State Machines:', stateMachines); + + if (stateMachines && stateMachines.length > 0) { + // Log details about each state machine + stateMachines.forEach((name, index) => { + console.log(`State Machine ${index + 1}:`, { + name, + inputs: this.riveInstance?.stateMachineInputs(name) + }); + }); + } + + // Play initial animation directly with longer delay + setTimeout(() => { + if (this.riveInstance) { + const stateMachines = this.riveInstance.stateMachineNames; + if (stateMachines && stateMachines.length > 0) { + // Try each state machine + stateMachines.forEach((stateMachineName) => { + console.log('Attempting to play with state machine:', stateMachineName); + + // Reset and play + this.riveInstance?.reset(); + this.riveInstance?.play(stateMachineName); + + // Log animation state after a short delay + setTimeout(() => { + if (this.riveInstance) { + console.log('Animation state after play:', { + stateMachine: stateMachineName, + isPlaying: this.riveInstance.isPlaying, + isPaused: this.riveInstance.isPaused, + isStopped: this.riveInstance.isStopped, + allStateMachines: this.riveInstance.stateMachineNames + }); + } + }, 500); + }); + } + } + }, 2000); + } + }); + } catch (error: unknown) { + console.error('Error setting up Rive:', error); + } + } + + @action + handleMouseEnter() { + if (this.riveInstance) { + const stateMachines = this.riveInstance.stateMachineNames; + if (stateMachines && stateMachines.length > 0) { + const stateMachineName = stateMachines[0]; + console.log('Playing hover animation with state machine:', stateMachineName); + this.riveInstance.reset(); + this.riveInstance.play(stateMachineName); + } + } + } + + @action + handleMouseLeave() { + if (this.riveInstance) { + // Stop the animation and reset to initial state + this.riveInstance.stop(); + this.riveInstance.reset(); + } + } + + @action + cleanupRive() { + if (this.riveInstance) { + this.riveInstance.stop(); + this.riveInstance = null; + } + } +} \ No newline at end of file diff --git a/app/components/language-logo.hbs b/app/components/language-logo.hbs index 308a8ae38c..51a7117b72 100644 --- a/app/components/language-logo.hbs +++ b/app/components/language-logo.hbs @@ -1,4 +1,6 @@ -{{#if (eq @variant "color")}} +{{#if (and (eq @language.slug "gleam") (eq @variant "color"))}} + +{{else if (eq @variant "color")}} {{@language.name}} {{else if (eq @variant "gray")}} {{@language.name}} diff --git a/app/components/language-logo.ts b/app/components/language-logo.ts index 0137618894..6e4d5beb0f 100644 --- a/app/components/language-logo.ts +++ b/app/components/language-logo.ts @@ -15,5 +15,6 @@ export default class LanguageLogoComponent extends Component {} declare module '@glint/environment-ember-loose/registry' { export default interface Registry { LanguageLogo: typeof LanguageLogoComponent; + GleamLogo: typeof import('./gleam-logo').default; } } diff --git a/app/styles/components/gleam-logo.css b/app/styles/components/gleam-logo.css new file mode 100644 index 0000000000..e098a8e774 --- /dev/null +++ b/app/styles/components/gleam-logo.css @@ -0,0 +1,12 @@ +.gleam-logo-container { + display: block; + width: 100%; + height: 100%; +} + +.gleam-logo-container canvas { + display: block; + width: 100%; + height: 100%; + object-fit: contain; +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index cbf9d0f7b4..1ebdeca1e6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "license": "MIT", "dependencies": { "@rails/actioncable": "^8.0.200", + "@rive-app/canvas": "^2.27.0", "@stripe/stripe-js": "^5.5.0", "@tailwindcss/container-queries": "^0.1.1", "@tailwindcss/typography": "^0.5.16", @@ -8394,6 +8395,12 @@ "integrity": "sha512-EDqWyxck22BHmv1e+mD8Kl6GmtNkhEPdRfGFT7kvsv1yoXd9iYrqHDVAaR8bKmU/syC5eEZ2I5aWWxtB73ukMw==", "license": "MIT" }, + "node_modules/@rive-app/canvas": { + "version": "2.27.0", + "resolved": "https://registry.npmjs.org/@rive-app/canvas/-/canvas-2.27.0.tgz", + "integrity": "sha512-wXGYCjXO+UpqesRPVSy7YmTnKSelhJVEu6kCWNWzwFD5IfwaM7rF9oG6WsubZ/D2Aacl9snaJUWjos7Em8mUSg==", + "license": "MIT" + }, "node_modules/@ro0gr/ceibo": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@ro0gr/ceibo/-/ceibo-2.2.0.tgz", diff --git a/package.json b/package.json index 346717c72d..23590ab088 100644 --- a/package.json +++ b/package.json @@ -202,6 +202,7 @@ }, "dependencies": { "@rails/actioncable": "^8.0.200", + "@rive-app/canvas": "^2.27.0", "@stripe/stripe-js": "^5.5.0", "@tailwindcss/container-queries": "^0.1.1", "@tailwindcss/typography": "^0.5.16", diff --git a/public/assets/animations/gleam_logo_animation.riv b/public/assets/animations/gleam_logo_animation.riv new file mode 100644 index 0000000000000000000000000000000000000000..372b25d4ba3fac8247b6580f84cd9637098dc94c GIT binary patch literal 5220 zcmZu#30M@zw(h~1L6JqdAX9=10>LDb1eL1-(mmbd1}-F4cPVb*3Mir|Dn<=H0;m|3 zrx6!IxW)^(#3e>i(Aj!ixGr%=MO;xtkvx&;RioU~Jw4te-}C!gy3YCQKj+l>tGcJZ z7$yvIrt0pFvZ)@co7MH|I#r{pQGHj{puXkwO#M*Z)aJhGiTaM}k-FCDftvdN{`pWU zH;U?Hu&F3#s<_h7mddO&G_^4U6Qy<;o<1cbWX7y%2`N+?4dp2jxKYIs26rkm!azA| zR9?~?Z|OhEQ)5+8P8td>wTJa^Z=+l9C4hXl29Rwu8aHZJsM)<(Q>mvqXtbFsr|v3B zt8vy<`{sz~aM5C`kU5tzlLVjrjBNMuUn8QhiF2%YD5*E{Y~wyg_}_qD4C(uFpB)kF zy+K4DjyG8GKYC}Icy0+G8ZxvDIXEKOjwrnOt%z3YeXaPn(sC0&`NL#JSh?T^O^OC6 zh^4uJ81VCL3mz5t*uOZyMGx5P@-nj|v8mMgYscaD?ADwTs*V!IW+XnU23qc9N3@MIzYUhuTGRSDC z(PO8Vr6xXLMk6ES2S28XdupQ{F;}XyY5~jQO>?wney9l$y@Ru9qT8q-s@CL*XuQV) z7XMVz-^?FLf655P5m`~>Oo|d^Vsoa567!Q;ynJRb!`FVS*mpM$646iJ8TG;~v(`k~ zOf0m=OtnUcXzsu`JwDq`%kV>PY-NO={y8QR;G-ZWy7aeKhc|SpqWP41KooSUG7;}R zTkRYn635^}2^Rd=DTn5VRs*7i(wa&84hl!I6g_UL)q;2J(9(Pr@8FiQty(b4DwItP zHj}dtl$Cz36}-^RC5nI6e%6~rZpBRCWm(5i-}t@{-a@!fK0lHaT#Uzqp^3x4y%;QjEM`&`~_Wlg7?+ZBbWYu&9A?NtTVc4{gnUdqoi=`$~JWN5zb;=tbs)G!mIsqy+ z5vgK1y1Z8M;ZZ|p)BNBVKy=OkaVBQU$BUmXXxSP`9dH1x{-i#NclX=R2&hXhGZ}wT zLCAVDe!mzFpmptx{M*5Bba`q-Rgky0H)TKbu2n?2&w0?91DW=9oOxlov#vv}D0^3! zRd6{S$q?f?S&(76cN^wd(e}@RtoZ&WFB3m#&`d^fxe&mR8879u!*u@VbFJvZLM=$S zGQ-5rdX>ZoWqVy1LN+J}mRV;-UY{JZ;N?U1n)vzIfarVe9!;b>C_PN)I|>l^{%{Na zveP{i|1=*E(e-<2;v25h%Lp8hZiNQ&dc0 z5V&h-6dAigv7d|?uSfYIp)5XnB9h_9{-D_BC4zm^C!wrRpKmad_x621b!Sob$66Ls zrvn&1g$29@YOOf}_PkzNR<7%A1&rc@w5)Ka#mhvNg(|FU@mv;8>alu!0M?R!x)SOg zF+9UW{&8NZH}A|E7PY(`V!_=@@6r6RF@T`c_nOGF<4^3XYWymPl~%-xBla(*dEY$P zeA!>Rn8^~jlcW1=9>gNU>AqI{qGD(i|6)KBBP>ddG?N3zl>Q6n5Q~oA0mOH41|z@2 z2>l=XBPir#i-O4Acu$XVg+MF58trZ3KaK#z(o3KaAB}>DUcFt9YK}l2%bGzUn-W0S z>x)1kZ?%d-ra$~2J;GBeEqKogP)Ow(=>Nc4P{`DmuJ%mp-@qyx+UHqtVK8t+Ww}Wk zI81lwIoO+iUuBW&xqc2tadoRK!ru-8e^v{iBz=uv+fk3w*T-1!_eqdra;7^Y+>~LHcy=9+5@47xzD(RLDb}>EABHSDgoh%=wL~B{P!h%lwAGP3%zwf2_ zdF`RXaNRu_{bf)@JR-6WQ|w4&`?qj9Cx9U73JPE-%fx| z&P|7`)^}7o>B{<9kxN0C6*t91NAV42K>S%R@6QqZudu;J{y-hf^wiko1E^I#fEr8V zIrlrqU0FDQCOHnEc-_(Y>m!h>bpk6~HJxL)u4+5=2MZGaP9r>ZHu%v~i>~@xl3Kz3|HL%VF~F zaaV#jyR)L>(fC7qsh2T~0P(aIDIe4P(F9kGT1ddp#0O?NV$h|sFE}#NS zGwmGGauA6P5CO zY0|%Fww?b~>P>h-6QVf0ee5$6pOQaYFPuA8Op{GZ6vTWLUr#r&>}Qj{0*{`G)GcBQ|V$hsu_28S%s$(-<2 zZMBK?U#jq2Pr1pV>my-(eu#fY^U<(w!pMqZ6LHQ`5KUX_IW+Ji9f$oGFNXi;F0f1W z%J!do>Nt6{b!1Ni+A_AlD%bmR1dIa46 zD~X+K_(;0O%(w2?qBuw|mP5JP5>dDd;mIa(fD{I1G`)d$%-E+(MXZ5*`RY1{Gzxrf}hVjHXouP^NUp6VipUN5U-g}6Rm3`xJ}pmIwv za_Gw8IUGLyuWA$TvD@KD`s_T1eA=}$2rFVMXu=0NsQx}-4~PEEc^j~`S4|YZca|e~ zZg4h>F7$ZJ36-TGW)k|yLDk4)TM-I;%;D%UHAa3^lf%(x>K+jl8oUj{iINb8oK163 zsr#K5(L|Sa27Ks3HO=pPAN<`kqlj9L&EW(?bcKm@IOw4AYP~3;vYa{=Z(BZ^=F6`+ z99vqu*idoj`|zt6wlaCT!BI!~N*^2Axt|en9f21k_Zg1hyWegU(Thh*Md9t#7$Z5* z?4VlKixp8fO^Jy6!Rf-+p8|(QYLx7V2Zz0HfdhiA=s0v*NcelMiS#aUblRu<^9VE# zw1KOoQ&-l8r{?6JPQkX7?$iz+>DO3^O8#MAs?rsW`r3-ji+yZD^hh9_G*)TG=3Nzjnh?fP>Z zy1b3%aQEBACO#4z!VUfzOS_8ssO+`C;uDP-ZseaP;v# z12do^p*9wj+4{L-VWYiZ#`9ng4uAe5D6{>0hwg0;o4l|>$3Rf#sy+^?$u^x$ZUw%D zL77v_9gfD$n?<>KLi}z}=DyL68156|M7iDg;S5lw+-^_JDOJ$(y{)bAptY!iLPJ9- zYCC*;dZ1Q4RIBRM@GT2IZ^8F2_^<`vx>V29s%Eu{@`CS9UK;7I7OuWme3Y5pCR>Moem@2jXfm Date: Sun, 20 Apr 2025 13:44:14 +0100 Subject: [PATCH 02/15] linting stuff --- app/components/gleam-logo.hbs | 10 +++++----- app/components/gleam-logo.ts | 22 +++++++++++----------- app/styles/components/gleam-logo.css | 2 +- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/app/components/gleam-logo.hbs b/app/components/gleam-logo.hbs index 995b192dcc..35c221e981 100644 --- a/app/components/gleam-logo.hbs +++ b/app/components/gleam-logo.hbs @@ -1,9 +1,9 @@ -
- {{!-- The canvas will be inserted here by Rive --}} -
\ No newline at end of file + {{! The canvas will be inserted here by Rive }} + \ No newline at end of file diff --git a/app/components/gleam-logo.ts b/app/components/gleam-logo.ts index da4a83ed43..d0c5cf86a8 100644 --- a/app/components/gleam-logo.ts +++ b/app/components/gleam-logo.ts @@ -10,11 +10,11 @@ export default class GleamLogoComponent extends Component { @action setupRive(element: HTMLElement) { this.container = element; - + try { // Create canvas element const canvas = document.createElement('canvas'); - canvas.width = 141; // Fixed size for crisp rendering + canvas.width = 141; // Fixed size for crisp rendering canvas.height = 144; canvas.style.width = '100%'; canvas.style.height = '100%'; @@ -28,21 +28,21 @@ export default class GleamLogoComponent extends Component { autoplay: false, onLoad: () => { console.log('Gleam logo animation loaded'); - + // Log available state machines const stateMachines = this.riveInstance?.stateMachineNames; console.log('All State Machines:', stateMachines); - + if (stateMachines && stateMachines.length > 0) { // Log details about each state machine stateMachines.forEach((name, index) => { console.log(`State Machine ${index + 1}:`, { name, - inputs: this.riveInstance?.stateMachineInputs(name) + inputs: this.riveInstance?.stateMachineInputs(name), }); }); } - + // Play initial animation directly with longer delay setTimeout(() => { if (this.riveInstance) { @@ -51,11 +51,11 @@ export default class GleamLogoComponent extends Component { // Try each state machine stateMachines.forEach((stateMachineName) => { console.log('Attempting to play with state machine:', stateMachineName); - + // Reset and play this.riveInstance?.reset(); this.riveInstance?.play(stateMachineName); - + // Log animation state after a short delay setTimeout(() => { if (this.riveInstance) { @@ -64,7 +64,7 @@ export default class GleamLogoComponent extends Component { isPlaying: this.riveInstance.isPlaying, isPaused: this.riveInstance.isPaused, isStopped: this.riveInstance.isStopped, - allStateMachines: this.riveInstance.stateMachineNames + allStateMachines: this.riveInstance.stateMachineNames, }); } }, 500); @@ -72,7 +72,7 @@ export default class GleamLogoComponent extends Component { } } }, 2000); - } + }, }); } catch (error: unknown) { console.error('Error setting up Rive:', error); @@ -108,4 +108,4 @@ export default class GleamLogoComponent extends Component { this.riveInstance = null; } } -} \ No newline at end of file +} diff --git a/app/styles/components/gleam-logo.css b/app/styles/components/gleam-logo.css index e098a8e774..a99adc4da6 100644 --- a/app/styles/components/gleam-logo.css +++ b/app/styles/components/gleam-logo.css @@ -9,4 +9,4 @@ width: 100%; height: 100%; object-fit: contain; -} \ No newline at end of file +} From 175ee69efe653d6146413d41509dfda6e6d655cd Mon Sep 17 00:00:00 2001 From: Arpan Pandey Date: Sun, 20 Apr 2025 13:49:30 +0100 Subject: [PATCH 03/15] manually making the linter happy --- app/components/gleam-logo.ts | 76 +++++++++++++++++++++--------------- 1 file changed, 44 insertions(+), 32 deletions(-) diff --git a/app/components/gleam-logo.ts b/app/components/gleam-logo.ts index d0c5cf86a8..01109107c6 100644 --- a/app/components/gleam-logo.ts +++ b/app/components/gleam-logo.ts @@ -3,12 +3,53 @@ import { tracked } from '@glimmer/tracking'; import { action } from '@ember/object'; import { Rive } from '@rive-app/canvas'; -export default class GleamLogoComponent extends Component { +interface GleamLogoSignature { + Element: HTMLDivElement; + Args: { + class?: string; + style?: string; + [key: string]: unknown; + }; + Blocks: Record; +} + +export default class GleamLogoComponent extends Component { @tracked riveInstance: Rive | null = null; container: HTMLElement | null = null; @action - setupRive(element: HTMLElement) { + cleanupRive() { + if (this.riveInstance) { + this.riveInstance.stop(); + this.riveInstance = null; + } + } + + @action + handleMouseEnter() { + if (this.riveInstance) { + const stateMachines = this.riveInstance.stateMachineNames; + + if (stateMachines && stateMachines.length > 0) { + const stateMachineName = stateMachines[0]; + console.log('Playing hover animation with state machine:', stateMachineName); + this.riveInstance.reset(); + this.riveInstance.play(stateMachineName); + } + } + } + + @action + handleMouseLeave() { + if (this.riveInstance) { + // Stop the animation and reset to initial state + this.riveInstance.stop(); + this.riveInstance.reset(); + } + } + + @action + setupRive(element: HTMLDivElement) { this.container = element; try { @@ -47,6 +88,7 @@ export default class GleamLogoComponent extends Component { setTimeout(() => { if (this.riveInstance) { const stateMachines = this.riveInstance.stateMachineNames; + if (stateMachines && stateMachines.length > 0) { // Try each state machine stateMachines.forEach((stateMachineName) => { @@ -78,34 +120,4 @@ export default class GleamLogoComponent extends Component { console.error('Error setting up Rive:', error); } } - - @action - handleMouseEnter() { - if (this.riveInstance) { - const stateMachines = this.riveInstance.stateMachineNames; - if (stateMachines && stateMachines.length > 0) { - const stateMachineName = stateMachines[0]; - console.log('Playing hover animation with state machine:', stateMachineName); - this.riveInstance.reset(); - this.riveInstance.play(stateMachineName); - } - } - } - - @action - handleMouseLeave() { - if (this.riveInstance) { - // Stop the animation and reset to initial state - this.riveInstance.stop(); - this.riveInstance.reset(); - } - } - - @action - cleanupRive() { - if (this.riveInstance) { - this.riveInstance.stop(); - this.riveInstance = null; - } - } } From c8648599f9c363a745e759fadcffc0cb1ea05265 Mon Sep 17 00:00:00 2001 From: Arpan Pandey Date: Sun, 20 Apr 2025 13:59:46 +0100 Subject: [PATCH 04/15] Removing debugging stuff --- app/components/gleam-logo.ts | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/app/components/gleam-logo.ts b/app/components/gleam-logo.ts index 01109107c6..5a4ae37d6b 100644 --- a/app/components/gleam-logo.ts +++ b/app/components/gleam-logo.ts @@ -32,7 +32,6 @@ export default class GleamLogoComponent extends Component { if (stateMachines && stateMachines.length > 0) { const stateMachineName = stateMachines[0]; - console.log('Playing hover animation with state machine:', stateMachineName); this.riveInstance.reset(); this.riveInstance.play(stateMachineName); } @@ -59,7 +58,6 @@ export default class GleamLogoComponent extends Component { canvas.height = 144; canvas.style.width = '100%'; canvas.style.height = '100%'; - canvas.style.border = '1px solid red'; // Visual debugging element.appendChild(canvas); // Initialize Rive @@ -68,22 +66,6 @@ export default class GleamLogoComponent extends Component { canvas: canvas, autoplay: false, onLoad: () => { - console.log('Gleam logo animation loaded'); - - // Log available state machines - const stateMachines = this.riveInstance?.stateMachineNames; - console.log('All State Machines:', stateMachines); - - if (stateMachines && stateMachines.length > 0) { - // Log details about each state machine - stateMachines.forEach((name, index) => { - console.log(`State Machine ${index + 1}:`, { - name, - inputs: this.riveInstance?.stateMachineInputs(name), - }); - }); - } - // Play initial animation directly with longer delay setTimeout(() => { if (this.riveInstance) { @@ -92,24 +74,9 @@ export default class GleamLogoComponent extends Component { if (stateMachines && stateMachines.length > 0) { // Try each state machine stateMachines.forEach((stateMachineName) => { - console.log('Attempting to play with state machine:', stateMachineName); - // Reset and play this.riveInstance?.reset(); this.riveInstance?.play(stateMachineName); - - // Log animation state after a short delay - setTimeout(() => { - if (this.riveInstance) { - console.log('Animation state after play:', { - stateMachine: stateMachineName, - isPlaying: this.riveInstance.isPlaying, - isPaused: this.riveInstance.isPaused, - isStopped: this.riveInstance.isStopped, - allStateMachines: this.riveInstance.stateMachineNames, - }); - } - }, 500); }); } } From c4ab10f557dbc14721d0046696dfb5eb2132dcfd Mon Sep 17 00:00:00 2001 From: Arpan Pandey Date: Sun, 20 Apr 2025 18:19:10 +0100 Subject: [PATCH 05/15] Working on hover too --- app/components/gleam-logo.ts | 89 ++++++++++++++---- .../animations/gleam_logo_animation.riv | Bin 5220 -> 5273 bytes 2 files changed, 70 insertions(+), 19 deletions(-) diff --git a/app/components/gleam-logo.ts b/app/components/gleam-logo.ts index 5a4ae37d6b..2b026a9b26 100644 --- a/app/components/gleam-logo.ts +++ b/app/components/gleam-logo.ts @@ -16,9 +16,14 @@ interface GleamLogoSignature { export default class GleamLogoComponent extends Component { @tracked riveInstance: Rive | null = null; container: HTMLElement | null = null; + animationInterval: number | null = null; @action cleanupRive() { + if (this.animationInterval) { + clearInterval(this.animationInterval); + this.animationInterval = null; + } if (this.riveInstance) { this.riveInstance.stop(); this.riveInstance = null; @@ -27,21 +32,31 @@ export default class GleamLogoComponent extends Component { @action handleMouseEnter() { + console.log('Hover animation starting'); if (this.riveInstance) { + console.log('Hover - Rive instance:', this.riveInstance); const stateMachines = this.riveInstance.stateMachineNames; - + console.log('Hover - Available state machines:', stateMachines); if (stateMachines && stateMachines.length > 0) { - const stateMachineName = stateMachines[0]; - this.riveInstance.reset(); - this.riveInstance.play(stateMachineName); + const stateMachineName = 'State Machine 1'; + if (stateMachines.includes(stateMachineName)) { + console.log('Hover - Playing state machine:', stateMachineName); + const inputs = this.riveInstance.stateMachineInputs(stateMachineName); + console.log('Hover - State machine inputs:', inputs); + this.riveInstance.reset(); + this.riveInstance.play(stateMachineName); + console.log('Hover - Play called on state machine'); + } else { + console.log('State Machine 1 not found, available machines:', stateMachines); + } } } } @action handleMouseLeave() { + console.log('Hover animation stopping'); if (this.riveInstance) { - // Stop the animation and reset to initial state this.riveInstance.stop(); this.riveInstance.reset(); } @@ -50,37 +65,73 @@ export default class GleamLogoComponent extends Component { @action setupRive(element: HTMLDivElement) { this.container = element; - + try { - // Create canvas element const canvas = document.createElement('canvas'); - canvas.width = 141; // Fixed size for crisp rendering + canvas.width = 141; canvas.height = 144; canvas.style.width = '100%'; canvas.style.height = '100%'; element.appendChild(canvas); - // Initialize Rive this.riveInstance = new Rive({ src: '/assets/animations/gleam_logo_animation.riv', canvas: canvas, - autoplay: false, + autoplay: true, + automaticallyHandleEvents: true, onLoad: () => { - // Play initial animation directly with longer delay + console.log('Rive file loaded'); + // Set up interval to play animation every 15 seconds + this.animationInterval = window.setInterval(() => { + console.log('Interval - Playing animation'); + if (this.riveInstance) { + console.log('Interval - Rive instance:', this.riveInstance); + const stateMachines = this.riveInstance.stateMachineNames; + console.log('Interval - Available state machines:', stateMachines); + if (stateMachines && stateMachines.length > 0) { + const stateMachineName = 'State Machine 2'; + if (stateMachines.includes(stateMachineName)) { + console.log('Interval - Playing state machine:', stateMachineName); + const inputs = this.riveInstance.stateMachineInputs(stateMachineName); + console.log('Interval - State machine inputs:', inputs); + this.riveInstance.reset(); + this.riveInstance.play(stateMachineName); + console.log('Interval - Play called on state machine'); + } else { + console.log('State Machine 2 not found, available machines:', stateMachines); + } + } + } + // Simulate mouse leave after a short delay + setTimeout(() => { + this.handleMouseLeave(); + }, 2000); + }, 15000); + // Play initial animation with State Machine 2 setTimeout(() => { if (this.riveInstance) { const stateMachines = this.riveInstance.stateMachineNames; - if (stateMachines && stateMachines.length > 0) { - // Try each state machine - stateMachines.forEach((stateMachineName) => { - // Reset and play - this.riveInstance?.reset(); - this.riveInstance?.play(stateMachineName); - }); + // First play State Machine 2 + const stateMachine2 = 'State Machine 2'; + if (stateMachines.includes(stateMachine2)) { + console.log('Initial - Playing state machine:', stateMachine2); + this.riveInstance.reset(); + this.riveInstance.play(stateMachine2); + + // Then after 800ms, reset State Machine 2 + setTimeout(() => { + if (this.riveInstance) { + console.log('Initial - Resetting state machine:', stateMachine2); + this.riveInstance.reset(); + } + }, 800); + } else { + console.log('State Machine 2 not found, available machines:', stateMachines); + } } } - }, 2000); + }, 500); }, }); } catch (error: unknown) { diff --git a/public/assets/animations/gleam_logo_animation.riv b/public/assets/animations/gleam_logo_animation.riv index 372b25d4ba3fac8247b6580f84cd9637098dc94c..d6f2739a710515db5658721027663e971fe6ced8 100644 GIT binary patch delta 261 zcmaE&F;mk#$TQ59ong|};3JF=7#}h|W_-fH9}NWlz~CgI1R!5 zKiOPZQA6IG)g!+wwaAhmD2t@{KdNGe$qmAaLZ;^Y!6k_$sS3V{$r+h>sR~Au?+J_Z z&0|bquwBA*_Kc6+R7S?h`-F8Z>=_)VGqNz)G1vk{z-*wRyG%eYoMdEhpUucH2Po_S Sl4StNGJ%W$2~XA%u>t@oSVj8) delta 256 zcmbQK`9#As$TQ59o#Dwt|09gASU#~lXL-W-hVc!{L&ldZ_n1DiJY#vs{Fw1I%LB$2 zEO(invM_+bL~|`hhK)&WLW~TPUka&D782H(93w23CTX0;AZ4n`pc)}6D*B&6(%2Zm zWnhpuXZ6T0OD(eG2g>9k%Salt0=fJQaxRQ+hjtj*0p*blVn8+M|Kwl7y7qPq4h;4T uj?)>L8EmIAGJset^B7a^GBMp_I?2f3J{u?olwbraL6exn$T&G(#0mi2RX_&- From ea04fe0f20256558dacbd29c64aac3c15d7736b2 Mon Sep 17 00:00:00 2001 From: Arpan Pandey Date: Sun, 20 Apr 2025 18:19:51 +0100 Subject: [PATCH 06/15] linter wasn't feeling too happy ig --- app/components/gleam-logo.ts | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/app/components/gleam-logo.ts b/app/components/gleam-logo.ts index 2b026a9b26..0dbb61a7f9 100644 --- a/app/components/gleam-logo.ts +++ b/app/components/gleam-logo.ts @@ -24,6 +24,7 @@ export default class GleamLogoComponent extends Component { clearInterval(this.animationInterval); this.animationInterval = null; } + if (this.riveInstance) { this.riveInstance.stop(); this.riveInstance = null; @@ -33,12 +34,15 @@ export default class GleamLogoComponent extends Component { @action handleMouseEnter() { console.log('Hover animation starting'); + if (this.riveInstance) { console.log('Hover - Rive instance:', this.riveInstance); const stateMachines = this.riveInstance.stateMachineNames; console.log('Hover - Available state machines:', stateMachines); + if (stateMachines && stateMachines.length > 0) { const stateMachineName = 'State Machine 1'; + if (stateMachines.includes(stateMachineName)) { console.log('Hover - Playing state machine:', stateMachineName); const inputs = this.riveInstance.stateMachineInputs(stateMachineName); @@ -56,6 +60,7 @@ export default class GleamLogoComponent extends Component { @action handleMouseLeave() { console.log('Hover animation stopping'); + if (this.riveInstance) { this.riveInstance.stop(); this.riveInstance.reset(); @@ -65,7 +70,7 @@ export default class GleamLogoComponent extends Component { @action setupRive(element: HTMLDivElement) { this.container = element; - + try { const canvas = document.createElement('canvas'); canvas.width = 141; @@ -84,12 +89,15 @@ export default class GleamLogoComponent extends Component { // Set up interval to play animation every 15 seconds this.animationInterval = window.setInterval(() => { console.log('Interval - Playing animation'); + if (this.riveInstance) { console.log('Interval - Rive instance:', this.riveInstance); const stateMachines = this.riveInstance.stateMachineNames; console.log('Interval - Available state machines:', stateMachines); + if (stateMachines && stateMachines.length > 0) { const stateMachineName = 'State Machine 2'; + if (stateMachines.includes(stateMachineName)) { console.log('Interval - Playing state machine:', stateMachineName); const inputs = this.riveInstance.stateMachineInputs(stateMachineName); @@ -102,6 +110,7 @@ export default class GleamLogoComponent extends Component { } } } + // Simulate mouse leave after a short delay setTimeout(() => { this.handleMouseLeave(); @@ -111,14 +120,16 @@ export default class GleamLogoComponent extends Component { setTimeout(() => { if (this.riveInstance) { const stateMachines = this.riveInstance.stateMachineNames; + if (stateMachines && stateMachines.length > 0) { // First play State Machine 2 const stateMachine2 = 'State Machine 2'; + if (stateMachines.includes(stateMachine2)) { console.log('Initial - Playing state machine:', stateMachine2); this.riveInstance.reset(); this.riveInstance.play(stateMachine2); - + // Then after 800ms, reset State Machine 2 setTimeout(() => { if (this.riveInstance) { From a20cf9f64d48c6e3fe85833d043d0e3b62e122cb Mon Sep 17 00:00:00 2001 From: Arpan Pandey Date: Sun, 20 Apr 2025 18:24:10 +0100 Subject: [PATCH 07/15] remove logs and stuff --- app/components/gleam-logo.ts | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/app/components/gleam-logo.ts b/app/components/gleam-logo.ts index 0dbb61a7f9..e42a43b85a 100644 --- a/app/components/gleam-logo.ts +++ b/app/components/gleam-logo.ts @@ -33,25 +33,15 @@ export default class GleamLogoComponent extends Component { @action handleMouseEnter() { - console.log('Hover animation starting'); - if (this.riveInstance) { - console.log('Hover - Rive instance:', this.riveInstance); const stateMachines = this.riveInstance.stateMachineNames; - console.log('Hover - Available state machines:', stateMachines); if (stateMachines && stateMachines.length > 0) { const stateMachineName = 'State Machine 1'; if (stateMachines.includes(stateMachineName)) { - console.log('Hover - Playing state machine:', stateMachineName); - const inputs = this.riveInstance.stateMachineInputs(stateMachineName); - console.log('Hover - State machine inputs:', inputs); this.riveInstance.reset(); this.riveInstance.play(stateMachineName); - console.log('Hover - Play called on state machine'); - } else { - console.log('State Machine 1 not found, available machines:', stateMachines); } } } @@ -59,8 +49,6 @@ export default class GleamLogoComponent extends Component { @action handleMouseLeave() { - console.log('Hover animation stopping'); - if (this.riveInstance) { this.riveInstance.stop(); this.riveInstance.reset(); @@ -85,28 +73,17 @@ export default class GleamLogoComponent extends Component { autoplay: true, automaticallyHandleEvents: true, onLoad: () => { - console.log('Rive file loaded'); // Set up interval to play animation every 15 seconds this.animationInterval = window.setInterval(() => { - console.log('Interval - Playing animation'); - if (this.riveInstance) { - console.log('Interval - Rive instance:', this.riveInstance); const stateMachines = this.riveInstance.stateMachineNames; - console.log('Interval - Available state machines:', stateMachines); if (stateMachines && stateMachines.length > 0) { const stateMachineName = 'State Machine 2'; if (stateMachines.includes(stateMachineName)) { - console.log('Interval - Playing state machine:', stateMachineName); - const inputs = this.riveInstance.stateMachineInputs(stateMachineName); - console.log('Interval - State machine inputs:', inputs); this.riveInstance.reset(); this.riveInstance.play(stateMachineName); - console.log('Interval - Play called on state machine'); - } else { - console.log('State Machine 2 not found, available machines:', stateMachines); } } } @@ -122,23 +99,18 @@ export default class GleamLogoComponent extends Component { const stateMachines = this.riveInstance.stateMachineNames; if (stateMachines && stateMachines.length > 0) { - // First play State Machine 2 const stateMachine2 = 'State Machine 2'; if (stateMachines.includes(stateMachine2)) { - console.log('Initial - Playing state machine:', stateMachine2); this.riveInstance.reset(); this.riveInstance.play(stateMachine2); // Then after 800ms, reset State Machine 2 setTimeout(() => { if (this.riveInstance) { - console.log('Initial - Resetting state machine:', stateMachine2); this.riveInstance.reset(); } }, 800); - } else { - console.log('State Machine 2 not found, available machines:', stateMachines); } } } From 6cc3d5bedc823d52167f6d1010a93bb007e97bff Mon Sep 17 00:00:00 2001 From: Arpan Pandey Date: Sun, 20 Apr 2025 18:41:49 +0100 Subject: [PATCH 08/15] Add integration test --- .../integration/components/gleam-logo-test.ts | 228 ++++++++++++++++++ 1 file changed, 228 insertions(+) create mode 100644 tests/integration/components/gleam-logo-test.ts diff --git a/tests/integration/components/gleam-logo-test.ts b/tests/integration/components/gleam-logo-test.ts new file mode 100644 index 0000000000..04dfc27a46 --- /dev/null +++ b/tests/integration/components/gleam-logo-test.ts @@ -0,0 +1,228 @@ +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'ember-qunit'; +import { render, settled, triggerEvent } from '@ember/test-helpers'; +import { hbs } from 'ember-cli-htmlbars'; +import { tracked } from '@glimmer/tracking'; + +// Declare Rive type for window +declare global { + interface Window { + Rive: typeof MockRive; + } +} + +// Mock Rive class for testing +class MockRive { + @tracked canvas: HTMLCanvasElement; + @tracked stateMachineNames: string[] = ['State Machine 1', 'State Machine 2']; + @tracked isLoaded = false; + private _listeners: Map = new Map(); + private _interval: number | null = null; + + get interval(): number | null { + return this._interval; + } + + constructor(options: { canvas: HTMLCanvasElement }) { + this.canvas = options.canvas; + } + + reset() { + // Mock reset implementation + } + + play(stateMachineName: string) { + // Mock play implementation + } + + stop() { + // Mock stop implementation + } + + stateMachineInputs(stateMachineName: string) { + return []; + } + + on(event: string, callback: Function) { + if (!this._listeners.has(event)) { + this._listeners.set(event, []); + } + this._listeners.get(event)?.push(callback); + } + + off(event: string, callback: Function) { + const listeners = this._listeners.get(event); + if (listeners) { + const index = listeners.indexOf(callback); + if (index > -1) { + listeners.splice(index, 1); + } + } + } + + // Helper method to trigger events + triggerEvent(event: string, ...args: any[]) { + const listeners = this._listeners.get(event); + if (listeners) { + listeners.forEach(callback => callback(...args)); + } + } + + // Helper method to simulate load + simulateLoad() { + this.isLoaded = true; + this.triggerEvent('load'); + } +} + +interface TestContext { + originalRive: typeof MockRive; + owner: { + lookup: (name: string) => any; + }; +} + +module('Integration | Component | gleam-logo', function (hooks) { + setupRenderingTest(hooks); + + hooks.beforeEach(function (this: TestContext) { + // Store original Rive constructor + this.originalRive = window.Rive; + // Replace with mock + window.Rive = MockRive; + }); + + hooks.afterEach(function (this: TestContext) { + // Restore original Rive constructor + window.Rive = this.originalRive; + }); + + test('it renders and initializes Rive', async function (assert) { + await render(hbs``); + + // Verify canvas is in the DOM + assert.dom('canvas').exists('Canvas element is rendered'); + + // Get the container and create mock Rive instance + const container = document.querySelector('.gleam-logo-container') as HTMLElement; + const canvas = container?.querySelector('canvas'); + assert.ok(canvas, 'Canvas element exists'); + + // Create and attach mock Rive instance + const mockRive = new MockRive({ canvas: canvas as HTMLCanvasElement }); + (container as any).__riveInstance = mockRive; + + // Simulate Rive load + mockRive.simulateLoad(); + await settled(); + + // Verify state machines are available + assert.deepEqual( + mockRive.stateMachineNames, + ['State Machine 1', 'State Machine 2'], + 'State machines are available' + ); + }); + + test('it handles hover events', async function (assert) { + await render(hbs``); + + // Get the container and create mock Rive instance + const container = document.querySelector('.gleam-logo-container') as HTMLElement; + const canvas = container?.querySelector('canvas'); + assert.ok(canvas, 'Canvas element exists'); + + // Create and attach mock Rive instance + const mockRive = new MockRive({ canvas: canvas as HTMLCanvasElement }); + (container as any).__riveInstance = mockRive; + + // Track calls to play and reset + let playCalls = 0; + let resetCalls = 0; + + // Override the mock methods before simulating load + mockRive.play = (stateMachineName: string) => { + playCalls++; + assert.equal(stateMachineName, 'State Machine 1', 'Hover plays correct state machine'); + }; + + mockRive.reset = () => { + resetCalls++; + }; + + // Simulate Rive load + mockRive.simulateLoad(); + await settled(); + + // Add event handler directly to the container + const handleMouseEnter = () => { + if (mockRive) { + const stateMachines = mockRive.stateMachineNames; + if (stateMachines && stateMachines.length > 0) { + const stateMachineName = 'State Machine 1'; + if (stateMachines.includes(stateMachineName)) { + mockRive.reset(); + mockRive.play(stateMachineName); + } + } + } + }; + + container.addEventListener('mouseenter', handleMouseEnter); + + // Trigger the event + container.dispatchEvent(new MouseEvent('mouseenter')); + await settled(); + + assert.equal(playCalls, 1, 'Play was called once on hover'); + assert.equal(resetCalls, 1, 'Reset was called once on hover'); + + // Clean up + container.removeEventListener('mouseenter', handleMouseEnter); + }); + + test('it cleans up resources on destroy', async function (this: TestContext, assert) { + await render(hbs``); + + // Get the container and create mock Rive instance + const container = document.querySelector('.gleam-logo-container') as HTMLElement; + const canvas = container?.querySelector('canvas'); + assert.ok(canvas, 'Canvas element exists'); + + // Create and attach mock Rive instance + const mockRive = new MockRive({ canvas: canvas as HTMLCanvasElement }); + (container as any).__riveInstance = mockRive; + + // Track cleanup calls + let stopCalls = 0; + let resetCalls = 0; + + mockRive.stop = () => { + stopCalls++; + }; + + mockRive.reset = () => { + resetCalls++; + }; + + // Simulate Rive load + mockRive.simulateLoad(); + await settled(); + + // Add cleanup handler directly + const cleanupRive = () => { + if (mockRive) { + mockRive.stop(); + mockRive.reset(); + } + }; + + // Trigger cleanup + cleanupRive(); + await settled(); + + assert.equal(stopCalls, 1, 'Stop was called on destroy'); + assert.equal(resetCalls, 1, 'Reset was called on destroy'); + assert.equal(mockRive.interval, null, 'Animation interval was cleared'); + }); +}); \ No newline at end of file From 0335cf1508630920370c2b4b0689c40126e20e5f Mon Sep 17 00:00:00 2001 From: Arpan Pandey Date: Sun, 20 Apr 2025 18:43:45 +0100 Subject: [PATCH 09/15] linters will be the end of me --- .../integration/components/gleam-logo-test.ts | 60 +++++++++---------- 1 file changed, 28 insertions(+), 32 deletions(-) diff --git a/tests/integration/components/gleam-logo-test.ts b/tests/integration/components/gleam-logo-test.ts index 04dfc27a46..3658e0e62f 100644 --- a/tests/integration/components/gleam-logo-test.ts +++ b/tests/integration/components/gleam-logo-test.ts @@ -64,7 +64,7 @@ class MockRive { triggerEvent(event: string, ...args: any[]) { const listeners = this._listeners.get(event); if (listeners) { - listeners.forEach(callback => callback(...args)); + listeners.forEach((callback) => callback(...args)); } } @@ -99,61 +99,57 @@ module('Integration | Component | gleam-logo', function (hooks) { test('it renders and initializes Rive', async function (assert) { await render(hbs``); - + // Verify canvas is in the DOM assert.dom('canvas').exists('Canvas element is rendered'); - + // Get the container and create mock Rive instance const container = document.querySelector('.gleam-logo-container') as HTMLElement; const canvas = container?.querySelector('canvas'); assert.ok(canvas, 'Canvas element exists'); - + // Create and attach mock Rive instance const mockRive = new MockRive({ canvas: canvas as HTMLCanvasElement }); (container as any).__riveInstance = mockRive; - + // Simulate Rive load mockRive.simulateLoad(); await settled(); - + // Verify state machines are available - assert.deepEqual( - mockRive.stateMachineNames, - ['State Machine 1', 'State Machine 2'], - 'State machines are available' - ); + assert.deepEqual(mockRive.stateMachineNames, ['State Machine 1', 'State Machine 2'], 'State machines are available'); }); test('it handles hover events', async function (assert) { await render(hbs``); - + // Get the container and create mock Rive instance const container = document.querySelector('.gleam-logo-container') as HTMLElement; const canvas = container?.querySelector('canvas'); assert.ok(canvas, 'Canvas element exists'); - + // Create and attach mock Rive instance const mockRive = new MockRive({ canvas: canvas as HTMLCanvasElement }); (container as any).__riveInstance = mockRive; - + // Track calls to play and reset let playCalls = 0; let resetCalls = 0; - + // Override the mock methods before simulating load mockRive.play = (stateMachineName: string) => { playCalls++; assert.equal(stateMachineName, 'State Machine 1', 'Hover plays correct state machine'); }; - + mockRive.reset = () => { resetCalls++; }; - + // Simulate Rive load mockRive.simulateLoad(); await settled(); - + // Add event handler directly to the container const handleMouseEnter = () => { if (mockRive) { @@ -167,48 +163,48 @@ module('Integration | Component | gleam-logo', function (hooks) { } } }; - + container.addEventListener('mouseenter', handleMouseEnter); - + // Trigger the event container.dispatchEvent(new MouseEvent('mouseenter')); await settled(); - + assert.equal(playCalls, 1, 'Play was called once on hover'); assert.equal(resetCalls, 1, 'Reset was called once on hover'); - + // Clean up container.removeEventListener('mouseenter', handleMouseEnter); }); test('it cleans up resources on destroy', async function (this: TestContext, assert) { await render(hbs``); - + // Get the container and create mock Rive instance const container = document.querySelector('.gleam-logo-container') as HTMLElement; const canvas = container?.querySelector('canvas'); assert.ok(canvas, 'Canvas element exists'); - + // Create and attach mock Rive instance const mockRive = new MockRive({ canvas: canvas as HTMLCanvasElement }); (container as any).__riveInstance = mockRive; - + // Track cleanup calls let stopCalls = 0; let resetCalls = 0; - + mockRive.stop = () => { stopCalls++; }; - + mockRive.reset = () => { resetCalls++; }; - + // Simulate Rive load mockRive.simulateLoad(); await settled(); - + // Add cleanup handler directly const cleanupRive = () => { if (mockRive) { @@ -216,13 +212,13 @@ module('Integration | Component | gleam-logo', function (hooks) { mockRive.reset(); } }; - + // Trigger cleanup cleanupRive(); await settled(); - + assert.equal(stopCalls, 1, 'Stop was called on destroy'); assert.equal(resetCalls, 1, 'Reset was called on destroy'); assert.equal(mockRive.interval, null, 'Animation interval was cleared'); }); -}); \ No newline at end of file +}); From 72c98647a2c4e4e539fd0ce63c729d4c309b3137 Mon Sep 17 00:00:00 2001 From: Arpan Pandey Date: Sun, 20 Apr 2025 18:53:45 +0100 Subject: [PATCH 10/15] pretty much the last msg --- .../integration/components/gleam-logo-test.js | 215 +++++++++++++++++ .../integration/components/gleam-logo-test.ts | 224 ------------------ 2 files changed, 215 insertions(+), 224 deletions(-) create mode 100644 tests/integration/components/gleam-logo-test.js delete mode 100644 tests/integration/components/gleam-logo-test.ts diff --git a/tests/integration/components/gleam-logo-test.js b/tests/integration/components/gleam-logo-test.js new file mode 100644 index 0000000000..33352ce5e2 --- /dev/null +++ b/tests/integration/components/gleam-logo-test.js @@ -0,0 +1,215 @@ +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'codecrafters-frontend/tests/helpers'; +import { render, settled } from '@ember/test-helpers'; +import { hbs } from 'ember-cli-htmlbars'; +import { tracked } from '@glimmer/tracking'; + +// Mock Rive class for testing +class MockRive { + // Private properties + _interval = null; + _listeners = new Map(); + + // Public properties + @tracked canvas; + @tracked isLoaded = false; + @tracked stateMachineNames = ['State Machine 1', 'State Machine 2']; + @tracked lastPlayedStateMachine = null; + @tracked lastInputsStateMachine = null; + + // Constructor + constructor(options) { + this.canvas = options.canvas; + } + + // Getter + get interval() { + return this._interval; + } + + // Public methods + off(event, callback) { + const listeners = this._listeners.get(event); + + if (listeners) { + const index = listeners.indexOf(callback); + + if (index > -1) { + listeners.splice(index, 1); + } + } + } + + on(event, callback) { + if (!this._listeners.has(event)) { + this._listeners.set(event, []); + } + + this._listeners.get(event)?.push(callback); + } + + play(stateMachineName) { + this.lastPlayedStateMachine = stateMachineName; + } + + reset() { + // Mock reset implementation + } + + simulateLoad() { + this.isLoaded = true; + this.triggerEvent('load'); + } + + stateMachineInputs(stateMachineName) { + this.lastInputsStateMachine = stateMachineName; + + return []; + } + + stop() { + // Mock stop implementation + } + + // Helper methods + triggerEvent(event, ...args) { + const listeners = this._listeners.get(event); + + if (listeners) { + listeners.forEach((callback) => callback(event, ...args)); + } + } +} + +module('Integration | Component | gleam-logo', function (hooks) { + setupRenderingTest(hooks); + + hooks.beforeEach(function () { + // Store original Rive constructor + this.originalRive = window.Rive; + // Replace with mock + window.Rive = MockRive; + }); + + hooks.afterEach(function () { + // Restore original Rive constructor + window.Rive = this.originalRive; + }); + + module('Rendering', function () { + test('it renders a canvas element', async function (assert) { + await render(hbs``); + assert.dom('canvas').exists('Canvas element is rendered'); + }); + + test('it initializes Rive with the canvas', async function (assert) { + await render(hbs``); + + const container = document.querySelector('.gleam-logo-container'); + const canvas = container?.querySelector('canvas'); + assert.ok(canvas, 'Canvas element exists'); + + const mockRive = new MockRive({ canvas }); + container.__riveInstance = mockRive; + + mockRive.simulateLoad(); + await settled(); + + assert.deepEqual(mockRive.stateMachineNames, ['State Machine 1', 'State Machine 2'], 'State machines are available after initialization'); + }); + }); + + module('Hover Behavior', function () { + test('it triggers animation on hover', async function (assert) { + await render(hbs``); + + const container = document.querySelector('.gleam-logo-container'); + const canvas = container?.querySelector('canvas'); + assert.ok(canvas, 'Canvas element exists'); + + const mockRive = new MockRive({ canvas }); + container.__riveInstance = mockRive; + + let playCalls = 0; + let resetCalls = 0; + + mockRive.play = (stateMachineName) => { + playCalls++; + assert.strictEqual(stateMachineName, 'State Machine 1', 'Hover plays correct state machine'); + }; + + mockRive.reset = () => { + resetCalls++; + }; + + mockRive.simulateLoad(); + await settled(); + + const handleMouseEnter = () => { + if (mockRive) { + const stateMachines = mockRive.stateMachineNames; + + if (stateMachines && stateMachines.length > 0) { + const stateMachineName = 'State Machine 1'; + + if (stateMachines.includes(stateMachineName)) { + mockRive.reset(); + mockRive.play(stateMachineName); + } + } + } + }; + + container.addEventListener('mouseenter', handleMouseEnter); + + container.dispatchEvent(new MouseEvent('mouseenter')); + await settled(); + + assert.strictEqual(playCalls, 1, 'Play was called once on hover'); + assert.strictEqual(resetCalls, 1, 'Reset was called once on hover'); + + container.removeEventListener('mouseenter', handleMouseEnter); + }); + }); + + module('Cleanup', function () { + test('it cleans up resources on destroy', async function (assert) { + await render(hbs``); + + const container = document.querySelector('.gleam-logo-container'); + const canvas = container?.querySelector('canvas'); + assert.ok(canvas, 'Canvas element exists'); + + const mockRive = new MockRive({ canvas }); + container.__riveInstance = mockRive; + + let stopCalls = 0; + let resetCalls = 0; + + mockRive.stop = () => { + stopCalls++; + }; + + mockRive.reset = () => { + resetCalls++; + }; + + mockRive.simulateLoad(); + await settled(); + + const cleanupRive = () => { + if (mockRive) { + mockRive.stop(); + mockRive.reset(); + } + }; + + cleanupRive(); + await settled(); + + assert.strictEqual(stopCalls, 1, 'Stop was called on destroy'); + assert.strictEqual(resetCalls, 1, 'Reset was called on destroy'); + assert.strictEqual(mockRive.interval, null, 'Animation interval was cleared'); + }); + }); +}); diff --git a/tests/integration/components/gleam-logo-test.ts b/tests/integration/components/gleam-logo-test.ts deleted file mode 100644 index 3658e0e62f..0000000000 --- a/tests/integration/components/gleam-logo-test.ts +++ /dev/null @@ -1,224 +0,0 @@ -import { module, test } from 'qunit'; -import { setupRenderingTest } from 'ember-qunit'; -import { render, settled, triggerEvent } from '@ember/test-helpers'; -import { hbs } from 'ember-cli-htmlbars'; -import { tracked } from '@glimmer/tracking'; - -// Declare Rive type for window -declare global { - interface Window { - Rive: typeof MockRive; - } -} - -// Mock Rive class for testing -class MockRive { - @tracked canvas: HTMLCanvasElement; - @tracked stateMachineNames: string[] = ['State Machine 1', 'State Machine 2']; - @tracked isLoaded = false; - private _listeners: Map = new Map(); - private _interval: number | null = null; - - get interval(): number | null { - return this._interval; - } - - constructor(options: { canvas: HTMLCanvasElement }) { - this.canvas = options.canvas; - } - - reset() { - // Mock reset implementation - } - - play(stateMachineName: string) { - // Mock play implementation - } - - stop() { - // Mock stop implementation - } - - stateMachineInputs(stateMachineName: string) { - return []; - } - - on(event: string, callback: Function) { - if (!this._listeners.has(event)) { - this._listeners.set(event, []); - } - this._listeners.get(event)?.push(callback); - } - - off(event: string, callback: Function) { - const listeners = this._listeners.get(event); - if (listeners) { - const index = listeners.indexOf(callback); - if (index > -1) { - listeners.splice(index, 1); - } - } - } - - // Helper method to trigger events - triggerEvent(event: string, ...args: any[]) { - const listeners = this._listeners.get(event); - if (listeners) { - listeners.forEach((callback) => callback(...args)); - } - } - - // Helper method to simulate load - simulateLoad() { - this.isLoaded = true; - this.triggerEvent('load'); - } -} - -interface TestContext { - originalRive: typeof MockRive; - owner: { - lookup: (name: string) => any; - }; -} - -module('Integration | Component | gleam-logo', function (hooks) { - setupRenderingTest(hooks); - - hooks.beforeEach(function (this: TestContext) { - // Store original Rive constructor - this.originalRive = window.Rive; - // Replace with mock - window.Rive = MockRive; - }); - - hooks.afterEach(function (this: TestContext) { - // Restore original Rive constructor - window.Rive = this.originalRive; - }); - - test('it renders and initializes Rive', async function (assert) { - await render(hbs``); - - // Verify canvas is in the DOM - assert.dom('canvas').exists('Canvas element is rendered'); - - // Get the container and create mock Rive instance - const container = document.querySelector('.gleam-logo-container') as HTMLElement; - const canvas = container?.querySelector('canvas'); - assert.ok(canvas, 'Canvas element exists'); - - // Create and attach mock Rive instance - const mockRive = new MockRive({ canvas: canvas as HTMLCanvasElement }); - (container as any).__riveInstance = mockRive; - - // Simulate Rive load - mockRive.simulateLoad(); - await settled(); - - // Verify state machines are available - assert.deepEqual(mockRive.stateMachineNames, ['State Machine 1', 'State Machine 2'], 'State machines are available'); - }); - - test('it handles hover events', async function (assert) { - await render(hbs``); - - // Get the container and create mock Rive instance - const container = document.querySelector('.gleam-logo-container') as HTMLElement; - const canvas = container?.querySelector('canvas'); - assert.ok(canvas, 'Canvas element exists'); - - // Create and attach mock Rive instance - const mockRive = new MockRive({ canvas: canvas as HTMLCanvasElement }); - (container as any).__riveInstance = mockRive; - - // Track calls to play and reset - let playCalls = 0; - let resetCalls = 0; - - // Override the mock methods before simulating load - mockRive.play = (stateMachineName: string) => { - playCalls++; - assert.equal(stateMachineName, 'State Machine 1', 'Hover plays correct state machine'); - }; - - mockRive.reset = () => { - resetCalls++; - }; - - // Simulate Rive load - mockRive.simulateLoad(); - await settled(); - - // Add event handler directly to the container - const handleMouseEnter = () => { - if (mockRive) { - const stateMachines = mockRive.stateMachineNames; - if (stateMachines && stateMachines.length > 0) { - const stateMachineName = 'State Machine 1'; - if (stateMachines.includes(stateMachineName)) { - mockRive.reset(); - mockRive.play(stateMachineName); - } - } - } - }; - - container.addEventListener('mouseenter', handleMouseEnter); - - // Trigger the event - container.dispatchEvent(new MouseEvent('mouseenter')); - await settled(); - - assert.equal(playCalls, 1, 'Play was called once on hover'); - assert.equal(resetCalls, 1, 'Reset was called once on hover'); - - // Clean up - container.removeEventListener('mouseenter', handleMouseEnter); - }); - - test('it cleans up resources on destroy', async function (this: TestContext, assert) { - await render(hbs``); - - // Get the container and create mock Rive instance - const container = document.querySelector('.gleam-logo-container') as HTMLElement; - const canvas = container?.querySelector('canvas'); - assert.ok(canvas, 'Canvas element exists'); - - // Create and attach mock Rive instance - const mockRive = new MockRive({ canvas: canvas as HTMLCanvasElement }); - (container as any).__riveInstance = mockRive; - - // Track cleanup calls - let stopCalls = 0; - let resetCalls = 0; - - mockRive.stop = () => { - stopCalls++; - }; - - mockRive.reset = () => { - resetCalls++; - }; - - // Simulate Rive load - mockRive.simulateLoad(); - await settled(); - - // Add cleanup handler directly - const cleanupRive = () => { - if (mockRive) { - mockRive.stop(); - mockRive.reset(); - } - }; - - // Trigger cleanup - cleanupRive(); - await settled(); - - assert.equal(stopCalls, 1, 'Stop was called on destroy'); - assert.equal(resetCalls, 1, 'Reset was called on destroy'); - assert.equal(mockRive.interval, null, 'Animation interval was cleared'); - }); -}); From 4ecb22e80c91e03dd468481e389a3ba26dc2e1a9 Mon Sep 17 00:00:00 2001 From: Arpan Pandey Date: Mon, 21 Apr 2025 18:41:18 +0100 Subject: [PATCH 11/15] quality fix? --- app/components/gleam-logo.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/app/components/gleam-logo.ts b/app/components/gleam-logo.ts index e42a43b85a..c2af334bca 100644 --- a/app/components/gleam-logo.ts +++ b/app/components/gleam-logo.ts @@ -61,10 +61,17 @@ export default class GleamLogoComponent extends Component { try { const canvas = document.createElement('canvas'); - canvas.width = 141; - canvas.height = 144; + const devicePixelRatio = window.devicePixelRatio || 1; + + // Set the canvas size to be 2x the original size for better quality + canvas.width = 141 * 4; + canvas.height = 144 * 4; + + // Scale the canvas to fit the container while maintaining aspect ratio canvas.style.width = '100%'; canvas.style.height = '100%'; + canvas.style.objectFit = 'contain'; + element.appendChild(canvas); this.riveInstance = new Rive({ From 0ac1be37999e3b3bd7bcf845ddd6c4ded13f24dd Mon Sep 17 00:00:00 2001 From: Arpan Pandey Date: Mon, 21 Apr 2025 18:42:46 +0100 Subject: [PATCH 12/15] linter --- app/components/gleam-logo.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/components/gleam-logo.ts b/app/components/gleam-logo.ts index c2af334bca..68f100ae99 100644 --- a/app/components/gleam-logo.ts +++ b/app/components/gleam-logo.ts @@ -61,9 +61,8 @@ export default class GleamLogoComponent extends Component { try { const canvas = document.createElement('canvas'); - const devicePixelRatio = window.devicePixelRatio || 1; - // Set the canvas size to be 2x the original size for better quality + // Set the canvas size to be 4x the original size for better quality canvas.width = 141 * 4; canvas.height = 144 * 4; From cf62de1238028d0344690ff375ba90173ff2ae15 Mon Sep 17 00:00:00 2001 From: Arpan Pandey Date: Mon, 21 Apr 2025 18:46:12 +0100 Subject: [PATCH 13/15] tests ain't running From 1d67c55ff395b1013924420cdcf931c803845d1b Mon Sep 17 00:00:00 2001 From: Arpan Pandey Date: Mon, 21 Apr 2025 20:07:12 +0100 Subject: [PATCH 14/15] Fixed most stuff except maybe the moseevents --- app/components/gleam-logo.hbs | 5 +- app/components/gleam-logo.ts | 127 +++++++++--------- app/components/language-logo.hbs | 2 +- app/components/language-logo.ts | 1 - app/styles/components/gleam-logo.css | 12 -- .../integration/components/gleam-logo-test.js | 79 +++-------- 6 files changed, 87 insertions(+), 139 deletions(-) delete mode 100644 app/styles/components/gleam-logo.css diff --git a/app/components/gleam-logo.hbs b/app/components/gleam-logo.hbs index 35c221e981..08780ea3a1 100644 --- a/app/components/gleam-logo.hbs +++ b/app/components/gleam-logo.hbs @@ -1,9 +1,10 @@
{{! The canvas will be inserted here by Rive }}
\ No newline at end of file diff --git a/app/components/gleam-logo.ts b/app/components/gleam-logo.ts index 68f100ae99..0bc6505665 100644 --- a/app/components/gleam-logo.ts +++ b/app/components/gleam-logo.ts @@ -1,16 +1,18 @@ import Component from '@glimmer/component'; import { tracked } from '@glimmer/tracking'; import { action } from '@ember/object'; -import { Rive } from '@rive-app/canvas'; +import { Rive, Layout, Fit } from '@rive-app/canvas'; interface GleamLogoSignature { Element: HTMLDivElement; + Args: { - class?: string; - style?: string; - [key: string]: unknown; + height: number; + }; + + Blocks: { + default: []; }; - Blocks: Record; } export default class GleamLogoComponent extends Component { @@ -18,87 +20,37 @@ export default class GleamLogoComponent extends Component { container: HTMLElement | null = null; animationInterval: number | null = null; - @action - cleanupRive() { - if (this.animationInterval) { - clearInterval(this.animationInterval); - this.animationInterval = null; - } - - if (this.riveInstance) { - this.riveInstance.stop(); - this.riveInstance = null; - } - } - - @action - handleMouseEnter() { - if (this.riveInstance) { - const stateMachines = this.riveInstance.stateMachineNames; - - if (stateMachines && stateMachines.length > 0) { - const stateMachineName = 'State Machine 1'; - - if (stateMachines.includes(stateMachineName)) { - this.riveInstance.reset(); - this.riveInstance.play(stateMachineName); - } - } - } - } - - @action - handleMouseLeave() { - if (this.riveInstance) { - this.riveInstance.stop(); - this.riveInstance.reset(); - } + get containerStyle(): string { + return `height: ${this.args.height}px; width: auto;`; } @action - setupRive(element: HTMLDivElement) { + handleDidInsert(element: HTMLDivElement) { this.container = element; try { const canvas = document.createElement('canvas'); - // Set the canvas size to be 4x the original size for better quality - canvas.width = 141 * 4; - canvas.height = 144 * 4; + // Set initial canvas size for high quality + canvas.width = 400; // Base size for quality + canvas.height = 400; // Will adjust based on container - // Scale the canvas to fit the container while maintaining aspect ratio + // Let the canvas scale naturally within its container canvas.style.width = '100%'; canvas.style.height = '100%'; - canvas.style.objectFit = 'contain'; + canvas.style.display = 'block'; element.appendChild(canvas); this.riveInstance = new Rive({ src: '/assets/animations/gleam_logo_animation.riv', canvas: canvas, + layout: new Layout({ + fit: Fit.Contain, + }), autoplay: true, automaticallyHandleEvents: true, onLoad: () => { - // Set up interval to play animation every 15 seconds - this.animationInterval = window.setInterval(() => { - if (this.riveInstance) { - const stateMachines = this.riveInstance.stateMachineNames; - - if (stateMachines && stateMachines.length > 0) { - const stateMachineName = 'State Machine 2'; - - if (stateMachines.includes(stateMachineName)) { - this.riveInstance.reset(); - this.riveInstance.play(stateMachineName); - } - } - } - - // Simulate mouse leave after a short delay - setTimeout(() => { - this.handleMouseLeave(); - }, 2000); - }, 15000); // Play initial animation with State Machine 2 setTimeout(() => { if (this.riveInstance) { @@ -127,4 +79,47 @@ export default class GleamLogoComponent extends Component { console.error('Error setting up Rive:', error); } } + + @action + handleMouseEnter() { + if (this.riveInstance) { + const stateMachines = this.riveInstance.stateMachineNames; + + if (stateMachines && stateMachines.length > 0) { + const stateMachineName = 'State Machine 1'; + + if (stateMachines.includes(stateMachineName)) { + this.riveInstance.reset(); + this.riveInstance.play(stateMachineName); + } + } + } + } + + @action + handleMouseLeave() { + if (this.riveInstance) { + this.riveInstance.stop(); + this.riveInstance.reset(); + } + } + + @action + cleanupRive() { + if (this.animationInterval) { + clearInterval(this.animationInterval); + this.animationInterval = null; + } + + if (this.riveInstance) { + this.riveInstance.stop(); + this.riveInstance = null; + } + } +} + +declare module '@glint/environment-ember-loose/registry' { + export default interface Registry { + GleamLogo: typeof GleamLogoComponent; + } } diff --git a/app/components/language-logo.hbs b/app/components/language-logo.hbs index 51a7117b72..d15b1a23e4 100644 --- a/app/components/language-logo.hbs +++ b/app/components/language-logo.hbs @@ -1,5 +1,5 @@ {{#if (and (eq @language.slug "gleam") (eq @variant "color"))}} - + {{else if (eq @variant "color")}} {{@language.name}} {{else if (eq @variant "gray")}} diff --git a/app/components/language-logo.ts b/app/components/language-logo.ts index 6e4d5beb0f..0137618894 100644 --- a/app/components/language-logo.ts +++ b/app/components/language-logo.ts @@ -15,6 +15,5 @@ export default class LanguageLogoComponent extends Component {} declare module '@glint/environment-ember-loose/registry' { export default interface Registry { LanguageLogo: typeof LanguageLogoComponent; - GleamLogo: typeof import('./gleam-logo').default; } } diff --git a/app/styles/components/gleam-logo.css b/app/styles/components/gleam-logo.css deleted file mode 100644 index a99adc4da6..0000000000 --- a/app/styles/components/gleam-logo.css +++ /dev/null @@ -1,12 +0,0 @@ -.gleam-logo-container { - display: block; - width: 100%; - height: 100%; -} - -.gleam-logo-container canvas { - display: block; - width: 100%; - height: 100%; - object-fit: contain; -} diff --git a/tests/integration/components/gleam-logo-test.js b/tests/integration/components/gleam-logo-test.js index 33352ce5e2..56a244c620 100644 --- a/tests/integration/components/gleam-logo-test.js +++ b/tests/integration/components/gleam-logo-test.js @@ -16,6 +16,8 @@ class MockRive { @tracked stateMachineNames = ['State Machine 1', 'State Machine 2']; @tracked lastPlayedStateMachine = null; @tracked lastInputsStateMachine = null; + @tracked playCalls = 0; + @tracked resetCalls = 0; // Constructor constructor(options) { @@ -49,11 +51,14 @@ class MockRive { } play(stateMachineName) { + this.playCalls++; this.lastPlayedStateMachine = stateMachineName; + + return stateMachineName; } reset() { - // Mock reset implementation + this.resetCalls++; } simulateLoad() { @@ -68,7 +73,7 @@ class MockRive { } stop() { - // Mock stop implementation + // No-op for test } // Helper methods @@ -130,45 +135,24 @@ module('Integration | Component | gleam-logo', function (hooks) { const mockRive = new MockRive({ canvas }); container.__riveInstance = mockRive; - let playCalls = 0; - let resetCalls = 0; - - mockRive.play = (stateMachineName) => { - playCalls++; - assert.strictEqual(stateMachineName, 'State Machine 1', 'Hover plays correct state machine'); - }; - - mockRive.reset = () => { - resetCalls++; - }; - - mockRive.simulateLoad(); - await settled(); - - const handleMouseEnter = () => { - if (mockRive) { - const stateMachines = mockRive.stateMachineNames; + // Simulate the component's handleMouseEnter logic + if (mockRive) { + const stateMachines = mockRive.stateMachineNames; - if (stateMachines && stateMachines.length > 0) { - const stateMachineName = 'State Machine 1'; + if (stateMachines && stateMachines.length > 0) { + const stateMachineName = 'State Machine 1'; - if (stateMachines.includes(stateMachineName)) { - mockRive.reset(); - mockRive.play(stateMachineName); - } + if (stateMachines.includes(stateMachineName)) { + mockRive.reset(); + mockRive.play(stateMachineName); } } - }; - - container.addEventListener('mouseenter', handleMouseEnter); + } - container.dispatchEvent(new MouseEvent('mouseenter')); await settled(); - assert.strictEqual(playCalls, 1, 'Play was called once on hover'); - assert.strictEqual(resetCalls, 1, 'Reset was called once on hover'); - - container.removeEventListener('mouseenter', handleMouseEnter); + assert.strictEqual(mockRive.playCalls, 1, 'Play was called once on hover'); + assert.strictEqual(mockRive.resetCalls, 1, 'Reset was called once on hover'); }); }); @@ -183,33 +167,14 @@ module('Integration | Component | gleam-logo', function (hooks) { const mockRive = new MockRive({ canvas }); container.__riveInstance = mockRive; - let stopCalls = 0; - let resetCalls = 0; - mockRive.stop = () => { - stopCalls++; - }; - - mockRive.reset = () => { - resetCalls++; + assert.step('stop called'); }; - mockRive.simulateLoad(); - await settled(); - - const cleanupRive = () => { - if (mockRive) { - mockRive.stop(); - mockRive.reset(); - } - }; - - cleanupRive(); - await settled(); + // Trigger cleanup + await render(hbs``); - assert.strictEqual(stopCalls, 1, 'Stop was called on destroy'); - assert.strictEqual(resetCalls, 1, 'Reset was called on destroy'); - assert.strictEqual(mockRive.interval, null, 'Animation interval was cleared'); + assert.verifySteps(['stop called'], 'Stop was called during cleanup'); }); }); }); From d007423840c95978755dd12485e8315fe4a86ce0 Mon Sep 17 00:00:00 2001 From: Arpan Pandey Date: Wed, 23 Apr 2025 18:19:05 +0100 Subject: [PATCH 15/15] used SMs and stuff --- app/components/gleam-logo.hbs | 9 +- app/components/gleam-logo.ts | 103 ++++++++---------- .../animations/gleam_logo_animation.riv | Bin 5273 -> 5329 bytes 3 files changed, 44 insertions(+), 68 deletions(-) diff --git a/app/components/gleam-logo.hbs b/app/components/gleam-logo.hbs index 08780ea3a1..1853b273fe 100644 --- a/app/components/gleam-logo.hbs +++ b/app/components/gleam-logo.hbs @@ -1,10 +1,3 @@ -
+
{{! The canvas will be inserted here by Rive }}
\ No newline at end of file diff --git a/app/components/gleam-logo.ts b/app/components/gleam-logo.ts index 0bc6505665..27354b14fa 100644 --- a/app/components/gleam-logo.ts +++ b/app/components/gleam-logo.ts @@ -24,6 +24,19 @@ export default class GleamLogoComponent extends Component { return `height: ${this.args.height}px; width: auto;`; } + @action + cleanupRive() { + if (this.animationInterval) { + clearInterval(this.animationInterval); + this.animationInterval = null; + } + + if (this.riveInstance) { + this.riveInstance.stop(); + this.riveInstance = null; + } + } + @action handleDidInsert(element: HTMLDivElement) { this.container = element; @@ -48,72 +61,42 @@ export default class GleamLogoComponent extends Component { layout: new Layout({ fit: Fit.Contain, }), - autoplay: true, - automaticallyHandleEvents: true, + autoplay: false, onLoad: () => { - // Play initial animation with State Machine 2 - setTimeout(() => { - if (this.riveInstance) { - const stateMachines = this.riveInstance.stateMachineNames; - - if (stateMachines && stateMachines.length > 0) { - const stateMachine2 = 'State Machine 2'; - - if (stateMachines.includes(stateMachine2)) { - this.riveInstance.reset(); - this.riveInstance.play(stateMachine2); - - // Then after 800ms, reset State Machine 2 - setTimeout(() => { - if (this.riveInstance) { - this.riveInstance.reset(); - } - }, 800); - } - } - } - }, 500); - }, - }); - } catch (error: unknown) { - console.error('Error setting up Rive:', error); - } - } - - @action - handleMouseEnter() { - if (this.riveInstance) { - const stateMachines = this.riveInstance.stateMachineNames; + console.log('onLoad'); - if (stateMachines && stateMachines.length > 0) { - const stateMachineName = 'State Machine 1'; + if (this.riveInstance) { + const stateMachines = this.riveInstance.stateMachineNames; - if (stateMachines.includes(stateMachineName)) { - this.riveInstance.reset(); - this.riveInstance.play(stateMachineName); - } - } - } - } + if (stateMachines?.includes('State Machine 2')) { + // Play first time immediately + this.riveInstance.play('State Machine 2'); + } - @action - handleMouseLeave() { - if (this.riveInstance) { - this.riveInstance.stop(); - this.riveInstance.reset(); - } - } + // Play State Machine 3 after 1 second to reset + setTimeout(() => { + if (this.riveInstance) { + this.riveInstance.play('State Machine 3'); + } + }, 1000); - @action - cleanupRive() { - if (this.animationInterval) { - clearInterval(this.animationInterval); - this.animationInterval = null; - } + // Set up hover state machine + canvas.addEventListener('mouseenter', () => { + if (this.riveInstance) { + this.riveInstance.play('State Machine 1'); + } + }); - if (this.riveInstance) { - this.riveInstance.stop(); - this.riveInstance = null; + canvas.addEventListener('mouseleave', () => { + if (this.riveInstance) { + this.riveInstance.play('State Machine 1'); + } + }); + } + }, + }); + } catch (error: unknown) { + console.error('Error setting up Rive:', error); } } } diff --git a/public/assets/animations/gleam_logo_animation.riv b/public/assets/animations/gleam_logo_animation.riv index d6f2739a710515db5658721027663e971fe6ced8..7f560146113f4e7d9c61c542f2bc60206c6fe167 100644 GIT binary patch delta 192 zcmbQKc~NshEbD&8H;hahlj?*fzYz3Hm636>k%+DtSb~MYj=>fv0#d{RRCJe#=^oQbMh5rUj0|&t!VVx= R29PWh$O!k@lc$Q<0044=Aa4Kw