From 1db30d1c887d9d6d1bd01e55a4a92d92cb0a2c39 Mon Sep 17 00:00:00 2001 From: hisnameisjimmy Date: Mon, 6 May 2024 17:18:00 +0000 Subject: [PATCH] deploy: a42d3188ec7fc839fac977bc7f92fe024c7aad99 --- 404.html | 24 ++++++--------- 404/index.html | 24 ++++++--------- 589-5b6bba0449f4a6fcacd1.js | 2 -- 589-5b6bba0449f4a6fcacd1.js.map | 1 - about/index.html | 24 ++++++--------- app-2db289bf6a57778cd61e.js | 3 -- app-2db289bf6a57778cd61e.js.map | 1 - app-5f75a45e93580195f195.js | 3 ++ ...=> app-5f75a45e93580195f195.js.LICENSE.txt | 0 app-5f75a45e93580195f195.js.map | 1 + .../index.html | 26 +++++++--------- blog/being-curious/index.html | 24 ++++++--------- blog/being-grateful/index.html | 24 ++++++--------- blog/debugging-tartine-sourdough/index.html | 24 ++++++--------- .../index.html | 24 ++++++--------- blog/design-for-repetition/index.html | 24 ++++++--------- .../index.html | 24 ++++++--------- .../index.html | 24 ++++++--------- blog/how-to-not-be-cynical/index.html | 24 ++++++--------- .../index.html | 24 ++++++--------- blog/listening-to-your-inner-voice/index.html | 30 ++++++++----------- .../index.html | 24 ++++++--------- .../index.html | 24 ++++++--------- chunk-map.json | 2 +- donate/index.html | 24 ++++++--------- .../dont-leave-meetings-confused/index.html | 24 ++++++--------- index.html | 24 ++++++--------- page-data/app-data.json | 2 +- portfolio/associates-dein/index.html | 26 +++++++--------- portfolio/badger-maps/index.html | 26 +++++++--------- portfolio/cloudhike/index.html | 26 +++++++--------- portfolio/fahrenheit-labs/index.html | 26 +++++++--------- portfolio/hank/index.html | 26 +++++++--------- portfolio/index.html | 24 ++++++--------- portfolio/pocket-bodhi/index.html | 26 +++++++--------- portfolio/rocky-mountain-ruby/index.html | 26 +++++++--------- rss.xml | 2 +- sitemap/sitemap-0.xml | 2 +- webpack-runtime-56bc60d9a2f6029973eb.js | 2 -- webpack-runtime-56bc60d9a2f6029973eb.js.map | 1 - webpack-runtime-f36eab2c0da52c9b15f6.js | 2 ++ webpack-runtime-f36eab2c0da52c9b15f6.js.map | 1 + webpack.stats.json | 2 +- 43 files changed, 266 insertions(+), 431 deletions(-) delete mode 100644 589-5b6bba0449f4a6fcacd1.js delete mode 100644 589-5b6bba0449f4a6fcacd1.js.map delete mode 100644 app-2db289bf6a57778cd61e.js delete mode 100644 app-2db289bf6a57778cd61e.js.map create mode 100644 app-5f75a45e93580195f195.js rename app-2db289bf6a57778cd61e.js.LICENSE.txt => app-5f75a45e93580195f195.js.LICENSE.txt (100%) create mode 100644 app-5f75a45e93580195f195.js.map delete mode 100644 webpack-runtime-56bc60d9a2f6029973eb.js delete mode 100644 webpack-runtime-56bc60d9a2f6029973eb.js.map create mode 100644 webpack-runtime-f36eab2c0da52c9b15f6.js create mode 100644 webpack-runtime-f36eab2c0da52c9b15f6.js.map diff --git a/404.html b/404.html index 4248d28..1e83b09 100644 --- a/404.html +++ b/404.html @@ -1,17 +1,11 @@ -404 | Jimmy Hooker

Page not found

Sorry 😔 we couldn’t find what you were looking for.

Go home.

404 | Jimmy Hooker

Page not found

Sorry 😔 we couldn’t find what you were looking for.

Go home.

\ No newline at end of file + if(true) { + window.dataLayer = window.dataLayer || []; + function gtag(){window.dataLayer && window.dataLayer.push(arguments);} + gtag('js', new Date()); + + gtag('config', 'G-4HPC5TT803', {"send_page_view":false}); + } + \ No newline at end of file diff --git a/404/index.html b/404/index.html index 4e40f77..37dc0fa 100644 --- a/404/index.html +++ b/404/index.html @@ -1,17 +1,11 @@ -404 | Jimmy Hooker

Page not found

Sorry 😔 we couldn’t find what you were looking for.

Go home.

404 | Jimmy Hooker

Page not found

Sorry 😔 we couldn’t find what you were looking for.

Go home.

\ No newline at end of file + if(true) { + window.dataLayer = window.dataLayer || []; + function gtag(){window.dataLayer && window.dataLayer.push(arguments);} + gtag('js', new Date()); + + gtag('config', 'G-4HPC5TT803', {"send_page_view":false}); + } + \ No newline at end of file diff --git a/589-5b6bba0449f4a6fcacd1.js b/589-5b6bba0449f4a6fcacd1.js deleted file mode 100644 index 816c450..0000000 --- a/589-5b6bba0449f4a6fcacd1.js +++ /dev/null @@ -1,2 +0,0 @@ -"use strict";(self.webpackChunkjimmy_hooker_com=self.webpackChunkjimmy_hooker_com||[]).push([[589],{1589:function(t,e,n){n.r(e),n.d(e,{getCLS:function(){return s},getFCP:function(){return v},getFID:function(){return p},getLCP:function(){return l},getTTFB:function(){return h}});var i=function(t,e){return{name:t,value:void 0===e?-1:e,delta:0,entries:[],id:"v1-".concat(Date.now(),"-").concat(Math.floor(8999999999999*Math.random())+1e12)}},a=function(t,e){try{if(PerformanceObserver.supportedEntryTypes.includes(t)){if("first-input"===t&&!("PerformanceEventTiming"in self))return;var n=new PerformanceObserver((function(t){return t.getEntries().map(e)}));return n.observe({type:t,buffered:!0}),n}}catch(t){}},r=function(t,e){var n=function n(i){"pagehide"!==i.type&&"hidden"!==document.visibilityState||(t(i),e&&(removeEventListener("visibilitychange",n,!0),removeEventListener("pagehide",n,!0)))};addEventListener("visibilitychange",n,!0),addEventListener("pagehide",n,!0)},o=function(t){addEventListener("pageshow",(function(e){e.persisted&&t(e)}),!0)},u="function"==typeof WeakSet?new WeakSet:new Set,c=function(t,e,n){var i;return function(){e.value>=0&&(n||u.has(e)||"hidden"===document.visibilityState)&&(e.delta=e.value-(i||0),(e.delta||void 0===i)&&(i=e.value,t(e)))}},s=function(t,e){var n,u=i("CLS",0),s=function(t){t.hadRecentInput||(u.value+=t.value,u.entries.push(t),n())},f=a("layout-shift",s);f&&(n=c(t,u,e),r((function(){f.takeRecords().map(s),n()})),o((function(){u=i("CLS",0),n=c(t,u,e)})))},f=-1,m=function(){r((function(t){var e=t.timeStamp;f=e}),!0)},d=function(){return f<0&&((f=self.webVitals.firstHiddenTime)===1/0&&m(),o((function(){setTimeout((function(){f="hidden"===document.visibilityState?0:1/0,m()}),0)}))),{get timeStamp(){return f}}},v=function(t,e){var n,r=d(),s=i("FCP"),f=function(t){"first-contentful-paint"===t.name&&(v&&v.disconnect(),t.startTime= 0 && (n || a.has(e) || \"hidden\" === document.visibilityState) && (e.delta = e.value - (i || 0), (e.delta || void 0 === i) && (i = e.value, t(e)));\n };\n},\n o = function o(a, _o) {\n var u,\n c = t(\"CLS\", 0),\n s = function s(t) {\n t.hadRecentInput || (c.value += t.value, c.entries.push(t), u());\n },\n f = e(\"layout-shift\", s);\n\n f && (u = r(a, c, _o), n(function () {\n f.takeRecords().map(s), u();\n }), i(function () {\n c = t(\"CLS\", 0), u = r(a, c, _o);\n }));\n},\n u = -1,\n c = function c() {\n n(function (t) {\n var e = t.timeStamp;\n u = e;\n }, !0);\n},\n s = function s() {\n return u < 0 && ((u = self.webVitals.firstHiddenTime) === 1 / 0 && c(), i(function () {\n setTimeout(function () {\n u = \"hidden\" === document.visibilityState ? 0 : 1 / 0, c();\n }, 0);\n })), {\n get timeStamp() {\n return u;\n }\n\n };\n},\n f = function f(n, o) {\n var u,\n c = s(),\n f = t(\"FCP\"),\n d = function d(t) {\n \"first-contentful-paint\" === t.name && (v && v.disconnect(), t.startTime < c.timeStamp && (f.value = t.startTime, f.entries.push(t), a.add(f), u()));\n },\n m = performance.getEntriesByName(\"first-contentful-paint\")[0],\n v = m ? null : e(\"paint\", d);\n\n (m || v) && (u = r(n, f, o), m && d(m), i(function (e) {\n f = t(\"FCP\"), u = r(n, f, o), requestAnimationFrame(function () {\n requestAnimationFrame(function () {\n f.value = performance.now() - e.timeStamp, a.add(f), u();\n });\n });\n }));\n},\n d = function d(o, u) {\n var c,\n f = s(),\n d = t(\"FID\"),\n m = function m(t) {\n t.startTime < f.timeStamp && (d.value = t.processingStart - t.startTime, d.entries.push(t), a.add(d), c());\n },\n v = e(\"first-input\", m);\n\n c = r(o, d, u), v && n(function () {\n v.takeRecords().map(m), v.disconnect();\n }, !0), v || window.webVitals.firstInputPolyfill(m), i(function () {\n d = t(\"FID\"), c = r(o, d, u), window.webVitals.resetFirstInputPolyfill(), window.webVitals.firstInputPolyfill(m);\n });\n},\n m = function m(o, u) {\n var c,\n f = s(),\n d = t(\"LCP\"),\n m = function m(t) {\n var e = t.startTime;\n e < f.timeStamp && (d.value = e, d.entries.push(t)), c();\n },\n v = e(\"largest-contentful-paint\", m);\n\n if (v) {\n c = r(o, d, u);\n\n var p = function p() {\n a.has(d) || (v.takeRecords().map(m), v.disconnect(), a.add(d), c());\n };\n\n [\"keydown\", \"click\"].forEach(function (t) {\n addEventListener(t, p, {\n once: !0,\n capture: !0\n });\n }), n(p, !0), i(function (e) {\n d = t(\"LCP\"), c = r(o, d, u), requestAnimationFrame(function () {\n requestAnimationFrame(function () {\n d.value = performance.now() - e.timeStamp, a.add(d), c();\n });\n });\n });\n }\n},\n v = function v(e) {\n var n,\n i = t(\"TTFB\");\n n = function n() {\n try {\n var t = performance.getEntriesByType(\"navigation\")[0] || function () {\n var t = performance.timing,\n e = {\n entryType: \"navigation\",\n startTime: 0\n };\n\n for (var n in t) {\n \"navigationStart\" !== n && \"toJSON\" !== n && (e[n] = Math.max(t[n] - t.navigationStart, 0));\n }\n\n return e;\n }();\n\n if (i.value = i.delta = t.responseStart, i.value < 0) return;\n i.entries = [t], e(i);\n } catch (t) {}\n }, \"complete\" === document.readyState ? setTimeout(n, 0) : addEventListener(\"pageshow\", n);\n};\n\nexport { o as getCLS, f as getFCP, d as getFID, m as getLCP, v as getTTFB };"],"names":["t","_t","e","name","value","delta","entries","id","concat","Date","now","Math","floor","random","_e","PerformanceObserver","supportedEntryTypes","includes","self","n","getEntries","map","observe","type","buffered","i","document","visibilityState","removeEventListener","addEventListener","persisted","a","WeakSet","Set","r","has","o","_o","u","c","s","hadRecentInput","push","f","takeRecords","timeStamp","webVitals","firstHiddenTime","setTimeout","d","v","disconnect","startTime","add","m","performance","getEntriesByName","requestAnimationFrame","processingStart","window","firstInputPolyfill","resetFirstInputPolyfill","p","forEach","once","capture","getEntriesByType","timing","entryType","max","navigationStart","responseStart","readyState"],"sourceRoot":""} \ No newline at end of file diff --git a/about/index.html b/about/index.html index fcf252c..de4c385 100644 --- a/about/index.html +++ b/about/index.html @@ -1,17 +1,11 @@ -About Jimmy Hooker | Jimmy Hooker

About

This is just a place for me to write about things I'm thinking about. I make no claims, I am not an academic, and it's pretty likely I don't know what I'm talking about or have significant gaps in my understanding of the problem.

More Information

Currently Lead PM at Fivetran - The leader in ELT

Previously Senior PM at Nylas, VP of Product at Badger Maps, Inc..

Side Projects

  • Museum Height - Easily hang multiple pieces of art at Museum Height (57 inches).
  • Art Diario - New, beautifully curated art on your desktop every day.
  • Grow SF - Helping SF grow!

I love making practical, easy-to-use products that are a pleasure to use over and over again.

Contact me

hisnameisjimmy@gmail.com
About Jimmy Hooker | Jimmy Hooker

About

This is just a place for me to write about things I'm thinking about. I make no claims, I am not an academic, and it's pretty likely I don't know what I'm talking about or have significant gaps in my understanding of the problem.

More Information

Currently Lead PM at Fivetran - The leader in ELT

Previously Senior PM at Nylas, VP of Product at Badger Maps, Inc..

Side Projects

  • Museum Height - Easily hang multiple pieces of art at Museum Height (57 inches).
  • Art Diario - New, beautifully curated art on your desktop every day.
  • Grow SF - Helping SF grow!

I love making practical, easy-to-use products that are a pleasure to use over and over again.

Contact me

hisnameisjimmy@gmail.com
\ No newline at end of file + if(true) { + window.dataLayer = window.dataLayer || []; + function gtag(){window.dataLayer && window.dataLayer.push(arguments);} + gtag('js', new Date()); + + gtag('config', 'G-4HPC5TT803', {"send_page_view":false}); + } + \ No newline at end of file diff --git a/app-2db289bf6a57778cd61e.js b/app-2db289bf6a57778cd61e.js deleted file mode 100644 index fb0d9e9..0000000 --- a/app-2db289bf6a57778cd61e.js +++ /dev/null @@ -1,3 +0,0 @@ -/*! For license information please see app-2db289bf6a57778cd61e.js.LICENSE.txt */ -(self.webpackChunkjimmy_hooker_com=self.webpackChunkjimmy_hooker_com||[]).push([[143],{7228:function(e){e.exports=function(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n=0||(o[n]=e[n]);return o},e.exports.__esModule=!0,e.exports.default=e.exports},9489:function(e){function t(n,r){return e.exports=t=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(e,t){return e.__proto__=t,e},e.exports.__esModule=!0,e.exports.default=e.exports,t(n,r)}e.exports=t,e.exports.__esModule=!0,e.exports.default=e.exports},3038:function(e,t,n){var r=n(2858),o=n(3884),i=n(379),a=n(521);e.exports=function(e,t){return r(e)||o(e,t)||i(e,t)||a()},e.exports.__esModule=!0,e.exports.default=e.exports},379:function(e,t,n){var r=n(7228);e.exports=function(e,t){if(e){if("string"==typeof e)return r(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?r(e,t):void 0}},e.exports.__esModule=!0,e.exports.default=e.exports},456:function(e){e.exports=function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=0)}([function(e,t,n){"use strict";function r(e){return(r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function o(e){return function(e){if(Array.isArray(e)){for(var t=0,n=new Array(e.length);t0&&void 0!==arguments[0]?arguments[0]:{},n=t.registeredObservers,r=void 0===n?new Set:n,o=t.processedEntries,i=void 0===o?new Set:o,a=t.interval,u=void 0===a?100:a,c=t.context,s=void 0===c?self:c;!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),d(this,"registeredObservers",void 0),d(this,"processedEntries",void 0),d(this,"interval",void 0),d(this,"intervalId",void 0),d(this,"context",void 0),this.registeredObservers=r,this.processedEntries=i,this.interval=u,this.context=s,this.intervalId=null}var t,n,r;return t=e,(n=[{key:"getNewEntries",value:function(){var e=this;return this.context.performance.getEntries().filter((function(t){return!e.processedEntries.has(t)}))}},{key:"getObserversForType",value:function(e,t){return Array.from(e).filter((function(e){return e.entryTypes.some((function(e){return e===t}))}))}},{key:"processBuffer",value:function(e){var t=Array.from(e.buffer),n=new f(t);e.buffer.clear(),t.length&&e.callback&&e.callback.call(void 0,n,e)}},{key:"processEntries",value:function(){var e=this;this.getNewEntries().forEach((function(t){var n=t.entryType;e.getObserversForType(e.registeredObservers,n).forEach((function(e){e.buffer.add(t)})),e.processedEntries.add(t)}));var t=function(){return e.registeredObservers.forEach(e.processBuffer)};"requestAnimationFrame"in this.context?this.context.requestAnimationFrame(t):this.context.setTimeout(t,0)}},{key:"add",value:function(e){this.registeredObservers.add(e),1===this.registeredObservers.size&&this.observe()}},{key:"remove",value:function(e){this.registeredObservers.delete(e),this.registeredObservers.size||this.disconnect()}},{key:"observe",value:function(){this.intervalId=this.context.setInterval(this.processEntries.bind(this),this.interval)}},{key:"disconnect",value:function(){this.intervalId=this.context.clearInterval(this.intervalId)}}])&&p(t.prototype,n),r&&p(t,r),e}()),E=function(){function e(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:O;!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),m(this,"callback",void 0),m(this,"buffer",void 0),m(this,"entryTypes",[]),m(this,"taskQueue",void 0),this.callback=t,this.buffer=new Set,this.taskQueue=n}var t,n,r;return t=e,(n=[{key:"observe",value:function(e){if(!e)throw new Error(g);if(e.entryTypes&&e.type)throw new Error(y);var t;if(e.entryTypes)t=e.entryTypes;else{if(!e.type)throw new Error(g);t=[e.type]}var n=t.filter(P);n.length>0&&n.length!==t.length&&console.warn(w),n.length?(this.entryTypes=n,this.taskQueue.add(this)):console.warn(b)}},{key:"disconnect",value:function(){this.taskQueue.remove(this)}},{key:"takeRecords",value:function(){var e=Array.from(this.buffer);return new f(e)}}])&&h(t.prototype,n),r&&h(t,r),e}();m(E,"supportedEntryTypes",v);var j="PerformanceObserver"in self&&"function"==typeof PerformanceObserver?PerformanceObserver:E,k=self;k.PerformanceObserver||(k.PerformanceObserver=j)}])},2393:function(e,t){"use strict";var n=Object.assign||function(e){for(var t=1;t1&&void 0!==arguments[1]?arguments[1]:{},s=c.state,l=c.replace,f=void 0!==l&&l;if("number"==typeof t)e.history.go(t);else{s=n({},s,{key:Date.now()+""});try{a||f?e.history.replaceState(s,null,t):e.history.pushState(s,null,t)}catch(d){e.location[f?"replace":"assign"](t)}}i=r(e),a=!0;var p=new Promise((function(e){return u=e}));return o.forEach((function(e){return e({location:i,action:"PUSH"})})),p}}},i=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"/",t=e.indexOf("?"),n={pathname:t>-1?e.substr(0,t):e,search:t>-1?e.substr(t):""},r=0,o=[n],i=[null];return{get location(){return o[r]},addEventListener:function(e,t){},removeEventListener:function(e,t){},history:{get entries(){return o},get index(){return r},get state(){return i[r]},pushState:function(e,t,n){var a=n.split("?"),u=a[0],c=a[1],s=void 0===c?"":c;r++,o.push({pathname:u,search:s.length?"?"+s:s}),i.push(e)},replaceState:function(e,t,n){var a=n.split("?"),u=a[0],c=a[1],s=void 0===c?"":c;o[r]={pathname:u,search:s},i[r]=e},go:function(e){var t=r+e;t<0||t>i.length-1||(r=t)}}}},a=!("undefined"==typeof window||!window.document||!window.document.createElement),u=o(a?window:i()),c=u.navigate;t.V5=u},2098:function(e,t,n){"use strict";t.__esModule=!0,t.shallowCompare=t.validateRedirect=t.insertParams=t.resolve=t.match=t.pick=t.startsWith=void 0;var r,o=n(1143),i=(r=o)&&r.__esModule?r:{default:r};var a=function(e,t){return e.substr(0,t.length)===t},u=function(e,t){for(var n=void 0,r=void 0,o=t.split("?")[0],a=d(o),u=""===a[0],s=p(e),f=0,h=s.length;ft.score?-1:e.index-t.index}))},d=function(e){return e.replace(/(^\/+|\/+$)/g,"").split("/")},h=function(e){for(var t=arguments.length,n=Array(t>1?t-1:0),r=1;r0})))&&n.length>0?"?"+n.join("&"):"")},m=["uri","path"];t.startsWith=a,t.pick=u,t.match=function(e,t){return u([{path:e}],t)},t.resolve=function(e,t){if(a(e,"/"))return e;var n=e.split("?"),r=n[0],o=n[1],i=t.split("?")[0],u=d(r),c=d(i);if(""===u[0])return h(i,o);if(!a(u[0],".")){var s=c.concat(u).join("/");return h(("/"===i?"":"/")+s,o)}for(var l=c.concat(u),f=[],p=0,m=l.length;p=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var s=r.createContext({}),l=function(e){return function(t){var n=f(t.components);return r.createElement(e,i({},t,{components:n}))}},f=function(e){var t=r.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):u(u({},t),e)),n},p=function(e){var t=f(e.components);return r.createElement(s.Provider,{value:t},e.children)},d={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},h=r.forwardRef((function(e,t){var n=e.components,o=e.mdxType,i=e.originalType,a=e.parentName,s=c(e,["components","mdxType","originalType","parentName"]),l=f(n),p=o,h=l["".concat(a,".").concat(p)]||l[p]||d[p]||i;return n?r.createElement(h,u(u({ref:t},s),{},{components:n})):r.createElement(h,u({ref:t},s))}));function m(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var i=n.length,a=new Array(i);a[0]=h;var u={};for(var c in t)hasOwnProperty.call(t,c)&&(u[c]=t[c]);u.originalType=e,u.mdxType="string"==typeof e?e:o,a[1]=u;for(var s=2;s=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var a,u=!0,c=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return u=e.done,e},e:function(e){c=!0,a=e},f:function(){try{u||null==n.return||n.return()}finally{if(c)throw a}}}}function i(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n0)}))}))).observe(t),{instance:r,el:t}))},n.render=function(){var e=this,t=this.props,n=t.to,r=t.getProps,i=void 0===r?this.defaultGetProps:r,a=t.onClick,c=t.onMouseEnter,m=(t.activeClassName,t.activeStyle,t.innerRef,t.partiallyActive,t.state),v=t.replace,g=t._location,y=(0,o.default)(t,h);var b=(0,d.rewriteLinkPath)(n,g.pathname);return(0,p.isLocalLink)(b)?s.default.createElement(l.Link,(0,u.default)({to:b,state:m,getProps:i,innerRef:this.handleRef,onMouseEnter:function(e){c&&c(e);var t=(0,f.parsePath)(b);___loader.hovering(t.pathname+t.search)},onClick:function(t){if(a&&a(t),!(0!==t.button||e.props.target||t.defaultPrevented||t.metaKey||t.altKey||t.ctrlKey||t.shiftKey)){t.preventDefault();var n=v,r=encodeURI(b)===g.pathname;"boolean"!=typeof v&&r&&(n=!0),window.___navigate(b,{state:m,replace:n})}return!0}},y)):s.default.createElement("a",(0,u.default)({href:b},y))},t}(s.default.Component);w.propTypes=(0,u.default)({},y,{onClick:c.default.func,to:c.default.string.isRequired,replace:c.default.bool,state:c.default.object});var P=s.default.forwardRef((function(e,t){return s.default.createElement(b,(0,u.default)({innerRef:t},e))}));t.default=P;t.navigate=function(e,t){window.___navigate((0,d.rewriteLinkPath)(e,window.location.pathname),t)}},4587:function(e,t){"use strict";t.__esModule=!0,t.isLocalLink=void 0;var n=/^[a-zA-Z][a-zA-Z\d+\-.]*?:/;t.isLocalLink=function(e){if("string"==typeof e)return!function(e){return n.test(e)}(e)}},1752:function(e,t){"use strict";t.__esModule=!0,t.parsePath=function(e){var t=e||"/",n="",r="",o=t.indexOf("#");-1!==o&&(r=t.slice(o),t=t.slice(0,o));var i=t.indexOf("?");-1!==i&&(n=t.slice(i),t=t.slice(0,i));return{pathname:t,search:"?"===n?"":n,hash:"#"===r?"":r}}},349:function(e,t,n){"use strict";t.__esModule=!0,t.rewriteLinkPath=void 0;var r=n(2098),o=(n(8440),n(1752)),i=n(4587),a=n(8037),u=function(e){return null==e?void 0:e.startsWith("/")};t.rewriteLinkPath=function(e,t){if("number"==typeof e)return e;if(!(0,i.isLocalLink)(e))return e;var n=(0,o.parsePath)(e),c=(n.pathname,n.search,n.hash,e);return u(c)?(0,a.withPrefix)(c):function(e,t){return u(e)?e:(0,r.resolve)(e,t)}(c,t)}},9679:function(e,t,n){"use strict";t.p2=t.$C=void 0;var r=n(1432);t.$C=r.ScrollHandler;var o=n(4855);t.p2=o.useScrollRestoration},1432:function(e,t,n){"use strict";var r=n(5318);t.__esModule=!0,t.ScrollHandler=t.ScrollContext=void 0;var o=r(n(1506)),i=r(n(5354)),a=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=s(t);if(n&&n.has(e))return n.get(e);var r={},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var i in e)if("default"!==i&&Object.prototype.hasOwnProperty.call(e,i)){var a=o?Object.getOwnPropertyDescriptor(e,i):null;a&&(a.get||a.set)?Object.defineProperty(r,i,a):r[i]=e[i]}r.default=e,n&&n.set(e,r);return r}(n(7294)),u=r(n(5697)),c=n(1142);function s(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(s=function(e){return e?n:t})(e)}var l=a.createContext(new c.SessionStorage);t.ScrollContext=l,l.displayName="GatsbyScrollContext";var f=function(e){function t(){for(var t,n=arguments.length,r=new Array(n),i=0;i{let t;switch(r){case c.postHydrate:t=d(e);break;case c.idle:u((()=>{t=d(e)}));break;case c.offMainThread:if(s){const t=m(e);s(t)}}return()=>{const{script:e,loadCallback:n,errorCallback:r}=t||{};n&&(null==e||e.removeEventListener("load",n)),r&&(null==e||e.removeEventListener("error",r)),null==e||e.remove()}}),[]),r===c.offMainThread){const a=h(e),u=m(e);return"undefined"==typeof window&&(s?s(u):console.warn(`Unable to collect off-main-thread script '${t||n||"no-id-or-src"}' for configuration with Partytown.\nGatsby script components must be used either as a child of your page, in wrapPageElement, or wrapRootElement.\nSee https://gatsby.dev/gatsby-script for more information.`)),a?i.createElement("script",o()({type:"text/partytown","data-strategy":r,crossOrigin:"anonymous"},u,{dangerouslySetInnerHTML:{__html:h(e)}})):i.createElement("script",o()({type:"text/partytown",src:v(n),"data-strategy":r,crossOrigin:"anonymous"},u))}return null}function d(e){const{id:t,src:n,strategy:r=c.postHydrate,onLoad:i,onError:a}=e||{},u=t||n,s=["load","error"],p={load:i,error:a};if(u){for(const e of s)if(null!=p&&p[e]){var d;const t=f.get(u)||{},{callbacks:n=[]}=(null==t?void 0:t[e])||{};var v,y;if(n.push(null==p?void 0:p[e]),null!=t&&null!==(d=t[e])&&void 0!==d&&d.event)null==p||null===(v=p[e])||void 0===v||v.call(p,null==t||null===(y=t[e])||void 0===y?void 0:y.event);else f.set(u,o()({},t,{[e]:{callbacks:n}}))}if(l.has(u))return null}const b=h(e),w=m(e),P=document.createElement("script");t&&(P.id=t),P.dataset.strategy=r;for(const[o,c]of Object.entries(w))P.setAttribute(o,c);b&&(P.textContent=b),n&&(P.src=n);const O={};if(u){for(const e of s){const t=t=>g(t,u,e);P.addEventListener(e,t),O[`${e}Callback`]=t}l.add(u)}return document.body.appendChild(P),{script:P,loadCallback:O.loadCallback,errorCallback:O.errorCallback}}function h(e){const{dangerouslySetInnerHTML:t,children:n=""}=e||{},{__html:r=""}=t||{};return r||n}function m(e){const t={};for(const[n,r]of Object.entries(e))s.has(n)||(t[n]=r);return t}function v(e){if(e)return`/__third-party-proxy?url=${encodeURIComponent(e)}`}function g(e,t,n){const r=f.get(t)||{};for(const i of(null==r||null===(o=r[n])||void 0===o?void 0:o.callbacks)||[]){var o;i(e)}f.set(t,{[n]:{event:e}})}},5418:function(e,t,n){t.components={"component---src-pages-404-js":function(){return Promise.all([n.e(351),n.e(883)]).then(n.bind(n,429))},"component---src-pages-about-js":function(){return Promise.all([n.e(351),n.e(682)]).then(n.bind(n,5465))},"component---src-pages-donate-js":function(){return Promise.all([n.e(351),n.e(166)]).then(n.bind(n,416))},"component---src-pages-index-js":function(){return Promise.all([n.e(351),n.e(597),n.e(678)]).then(n.bind(n,5938))},"component---src-pages-mdx-frontmatter-post-type-mdx-slug-js":function(){return Promise.all([n.e(351),n.e(597),n.e(256)]).then(n.bind(n,4141))},"component---src-pages-portfolio-js":function(){return Promise.all([n.e(351),n.e(597),n.e(25)]).then(n.bind(n,7678))}}},4741:function(e,t,n){e.exports=[{plugin:n(4932),options:{plugins:[]}},{plugin:n(538),options:{plugins:[],trackingId:"UA-6279919-1",head:!1,anonymize:!1,respectDNT:!1,exclude:[],pageTransitionDelay:0,enableWebVitalsTracking:!1}},{plugin:n(9608),options:{plugins:[],icon:"src/images/icon.png",legacy:!0,theme_color_in_head:!0,cache_busting_mode:"query",crossOrigin:"anonymous",include_favicon:!0,cacheDigest:"9db651cbbdbb9d515341c1e155b24677"}},{plugin:n(2154),options:{plugins:[],maxWidth:650,linkImagesToOriginal:!0,showCaptions:!1,markdownCaptions:!1,backgroundColor:"white",quality:50,withWebp:!1,withAvif:!1,tracedSVG:!1,loading:"lazy",decoding:"async",disableBgImageOnAlpha:!1,disableBgImage:!1}},{plugin:n(6021),options:{plugins:[]}},{plugin:n(4384),options:{plugins:[{resolve:"/home/runner/work/hisnameisjimmy.github.io/hisnameisjimmy.github.io/node_modules/gatsby-remark-images",id:"c4a21c75-9c9b-598e-add9-681528ed1b49",name:"gatsby-remark-images",version:"6.16.0",pluginOptions:{plugins:[],maxWidth:650,linkImagesToOriginal:!0,showCaptions:!1,markdownCaptions:!1,backgroundColor:"white",quality:50,withWebp:!1,withAvif:!1,tracedSVG:!1,loading:"lazy",decoding:"async",disableBgImageOnAlpha:!1,disableBgImage:!1},nodeAPIs:["pluginOptionsSchema"],browserAPIs:["onRouteUpdate"],ssrAPIs:[]},{resolve:"/home/runner/work/hisnameisjimmy.github.io/hisnameisjimmy.github.io/node_modules/gatsby-remark-images-medium-zoom",id:"9a661529-a37d-52cd-b9a8-f94d932ba2e7",name:"gatsby-remark-images-medium-zoom",version:"1.7.0",pluginOptions:{plugins:[]},nodeAPIs:[],browserAPIs:["onRouteUpdate"],ssrAPIs:[]}],gatsbyRemarkPlugins:[{resolve:"/home/runner/work/hisnameisjimmy.github.io/hisnameisjimmy.github.io/node_modules/gatsby-remark-images",id:"0a918d20-5ad0-5db2-89e6-d292a6d967f4",name:"gatsby-remark-images",version:"6.16.0",modulePath:"/home/runner/work/hisnameisjimmy.github.io/hisnameisjimmy.github.io/node_modules/gatsby-remark-images/index.js",pluginOptions:{plugins:[],maxWidth:1200,linkImagesToOriginal:!1},nodeAPIs:["pluginOptionsSchema"],browserAPIs:["onRouteUpdate"],ssrAPIs:[]},{resolve:"/home/runner/work/hisnameisjimmy.github.io/hisnameisjimmy.github.io/node_modules/gatsby-remark-images-medium-zoom",id:"9a661529-a37d-52cd-b9a8-f94d932ba2e7",name:"gatsby-remark-images-medium-zoom",version:"1.7.0",modulePath:"/home/runner/work/hisnameisjimmy.github.io/hisnameisjimmy.github.io/node_modules/gatsby-remark-images-medium-zoom/index.js",module:{},pluginOptions:{plugins:[]},nodeAPIs:[],browserAPIs:["onRouteUpdate"],ssrAPIs:[]}],extensions:[".mdx"],defaultLayouts:{},lessBabel:!1,remarkPlugins:[],rehypePlugins:[],mediaTypes:["text/markdown","text/x-markdown"],root:"/home/runner/work/hisnameisjimmy.github.io/hisnameisjimmy.github.io",commonmark:!1,JSFrontmatterEngine:!1,engines:{}}},{plugin:n(2154),options:{plugins:[],maxWidth:1200,linkImagesToOriginal:!1}},{plugin:n(6021),options:{plugins:[]}},{plugin:n(2154),options:{plugins:[],maxWidth:650,linkImagesToOriginal:!0,showCaptions:!1,markdownCaptions:!1,backgroundColor:"white",quality:50,withWebp:!1,withAvif:!1,tracedSVG:!1,loading:"lazy",decoding:"async",disableBgImageOnAlpha:!1,disableBgImage:!1}},{plugin:n(6021),options:{plugins:[]}},{plugin:n(7420),options:{plugins:[]}},{plugin:n(540),options:{plugins:[]}}]},3092:function(e,t,n){var r=n(4741),o=n(1975).jN,i=o.getResourceURLsForPathname,a=o.loadPage,u=o.loadPageSync;t.h=function(e,t,n,o){void 0===t&&(t={});var c=r.map((function(n){if(n.plugin[e]){t.getResourceURLsForPathname=i,t.loadPage=a,t.loadPageSync=u;var r=n.plugin[e](t,n.options);return r&&o&&(t=o({args:t,result:r,plugin:n})),r}}));return(c=c.filter((function(e){return void 0!==e}))).length>0?c:n?[n]:[]},t.I=function(e,t,n){return r.reduce((function(n,r){return r.plugin[e]?n.then((function(){return r.plugin[e](t,r.options)})):n}),Promise.resolve())}},8299:function(e,t,n){"use strict";n.d(t,{Z:function(){return r}});var r=function(e){return e=e||Object.create(null),{on:function(t,n){(e[t]||(e[t]=[])).push(n)},off:function(t,n){e[t]&&e[t].splice(e[t].indexOf(n)>>>0,1)},emit:function(t,n){(e[t]||[]).slice().map((function(e){e(n)})),(e["*"]||[]).slice().map((function(e){e(t,n)}))}}}()},7802:function(e,t,n){"use strict";n.d(t,{UD:function(){return p},Cj:function(){return h},GA:function(){return d},DS:function(){return f}});var r=n(2098),o=n(1505),i=function(e){if(void 0===e)return e;var t=e.split("?"),n=t[0],r=t[1],o=void 0===r?"":r;return o&&(o="?"+o),"/"===n?"/"+o:"/"===n.charAt(n.length-1)?n.slice(0,-1)+o:n+o},a=n(6073),u=new Map,c=[],s=function(e){var t=e;if(-1!==e.indexOf("?")){var n=e.split("?"),r=n[0],i=n[1];t=r+"?"+encodeURIComponent(i)}var a=decodeURIComponent(t);return(0,o.Z)(a,decodeURIComponent("")).split("#")[0]};function l(e){return e.startsWith("/")||e.startsWith("https://")||e.startsWith("http://")?e:new URL(e,window.location.href+(window.location.href.endsWith("/")?"":"/")).pathname}var f=function(e){c=e},p=function(e){var t=m(e),n=c.map((function(e){var t=e.path;return{path:e.matchPath,originalPath:t}})),o=(0,r.pick)(n,t);return o?i(o.route.originalPath):null},d=function(e){var t=m(e),n=c.map((function(e){var t=e.path;return{path:e.matchPath,originalPath:t}})),o=(0,r.pick)(n,t);return o?o.params:{}},h=function e(t){var n=s(l(t));if(u.has(n))return u.get(n);var r=(0,a.J)(t);if(r)return e(r.toPath);var o=p(n);return o||(o=m(t)),u.set(n,o),o},m=function(e){var t=s(l(e));return"/index.html"===t&&(t="/"),t=i(t)}},1597:function(e,t,n){"use strict";n.r(t),n.d(t,{Link:function(){return o.default},PageRenderer:function(){return u.a},PartytownContext:function(){return s.PartytownContext},Script:function(){return s.Script},ScriptStrategy:function(){return s.ScriptStrategy},StaticQuery:function(){return d},StaticQueryContext:function(){return f},graphql:function(){return m},navigate:function(){return o.navigate},parsePath:function(){return o.parsePath},prefetchPathname:function(){return l},scriptCache:function(){return s.scriptCache},scriptCallbackCache:function(){return s.scriptCallbackCache},useScrollRestoration:function(){return i.p2},useStaticQuery:function(){return h},withAssetPrefix:function(){return o.withAssetPrefix},withPrefix:function(){return o.withPrefix}});var r=n(7294),o=n(8037),i=n(9679),a=n(2743),u=n.n(a),c=n(1975),s=n(9848),l=c.ZP.enqueue,f=r.createContext({});function p(e){var t=e.staticQueryData,n=e.data,o=e.query,i=e.render,a=n?n.data:t[o]&&t[o].data;return r.createElement(r.Fragment,null,a&&i(a),!a&&r.createElement("div",null,"Loading (StaticQuery)"))}var d=function(e){var t=e.data,n=e.query,o=e.render,i=e.children;return r.createElement(f.Consumer,null,(function(e){return r.createElement(p,{data:t,query:n,render:o||i,staticQueryData:e})}))},h=function(e){var t;r.useContext;var n=r.useContext(f);if(isNaN(Number(e)))throw new Error("useStaticQuery was called with a string but expects to be called using `graphql`. Try this:\n\nimport { useStaticQuery, graphql } from 'gatsby';\n\nuseStaticQuery(graphql`"+e+"`);\n");if(null!==(t=n[e])&&void 0!==t&&t.data)return n[e].data;throw new Error("The result of this StaticQuery could not be fetched.\n\nThis is likely a bug in Gatsby and if refreshing the page does not fix it, please open an issue in https://github.com/gatsbyjs/gatsby/issues")};function m(){throw new Error("It appears like Gatsby is misconfigured. Gatsby related `graphql` calls are supposed to only be evaluated at compile time, and then compiled away. Unfortunately, something went wrong and the query was left in the compiled code.\n\nUnless your site has a complex or custom babel/Gatsby configuration this is likely a bug in Gatsby.")}},1975:function(e,t,n){"use strict";n.d(t,{uQ:function(){return d},kL:function(){return O},ZP:function(){return k},hs:function(){return S},jN:function(){return j},N1:function(){return E}});var r=n(1721),o=n(2982),i=n(4942),a=function(e){if("undefined"==typeof document)return!1;var t=document.createElement("link");try{if(t.relList&&"function"==typeof t.relList.supports)return t.relList.supports(e)}catch(n){return!1}return!1}("prefetch")?function(e,t){return new Promise((function(n,r){if("undefined"!=typeof document){var o=document.createElement("link");o.setAttribute("rel","prefetch"),o.setAttribute("href",e),Object.keys(t).forEach((function(e){o.setAttribute(e,t[e])})),o.onload=n,o.onerror=r,(document.getElementsByTagName("head")[0]||document.getElementsByName("script")[0].parentNode).appendChild(o)}else r()}))}:function(e){return new Promise((function(t,n){var r=new XMLHttpRequest;r.open("GET",e,!0),r.onload=function(){200===r.status?t():n()},r.send(null)}))},u={},c=function(e,t){return new Promise((function(n){u[e]?n():a(e,t).then((function(){n(),u[e]=!0})).catch((function(){}))}))},s=n(8299),l=n(7802);function f(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function p(e){for(var t=1;t0)return u[u.length-1];if(e&&e.location.pathname===i)return a?decodeURI(a.slice(1)):[0,0];return!0}var P=function(e){function t(t){var n;return(n=e.call(this,t)||this).announcementRef=i.createRef(),n}(0,r.Z)(t,e);var n=t.prototype;return n.componentDidUpdate=function(e,t){var n=this;requestAnimationFrame((function(){var e="new page at "+n.props.location.pathname;document.title&&(e=document.title);var t=document.querySelectorAll("#gatsby-focus-wrapper h1");t&&t.length&&(e=t[0].textContent);var r="Navigated to "+e;n.announcementRef.current&&(n.announcementRef.current.innerText!==r&&(n.announcementRef.current.innerText=r))}))},n.render=function(){return i.createElement("div",Object.assign({},p,{ref:this.announcementRef}))},t}(i.Component),O=function(e,t){var n,r;return e.href!==t.href||(null==e||null===(n=e.state)||void 0===n?void 0:n.key)!==(null==t||null===(r=t.state)||void 0===r?void 0:r.key)},E=function(e){function t(t){var n;return n=e.call(this,t)||this,g(t.location,null),n}(0,r.Z)(t,e);var n=t.prototype;return n.componentDidMount=function(){y(this.props.location,null)},n.shouldComponentUpdate=function(e){return!!O(e.location,this.props.location)&&(g(this.props.location,e.location),!0)},n.componentDidUpdate=function(e){O(e.location,this.props.location)&&y(this.props.location,e.location)},n.render=function(){return i.createElement(i.Fragment,null,this.props.children,i.createElement(P,{location:location}))},t}(i.Component),j=n(8522),k=n(5418),S=n(4942);function x(e,t){for(var n in e)if(!(n in t))return!0;for(var r in t)if(e[r]!==t[r])return!0;return!1}function C(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function _(e){for(var t=1;t0&&n(9939);var e=function(e){return i.createElement(a.BaseContext.Provider,{value:{baseuri:"/",basepath:"/"}},i.createElement(j.Z,e))},t=i.createContext({}),l=function(e){function n(){return e.apply(this,arguments)||this}return(0,r.Z)(n,e),n.prototype.render=function(){var e=this.props.children;return i.createElement(a.Location,null,(function(n){var r=n.location;return i.createElement(L,{location:r},(function(n){var r=n.pageResources,o=n.location,a=(0,s.hs)();return i.createElement(c.StaticQueryContext.Provider,{value:a},i.createElement(t.Provider,{value:{pageResources:r,location:o}},e))}))}))},n}(i.Component),f=function(n){function o(){return n.apply(this,arguments)||this}return(0,r.Z)(o,n),o.prototype.render=function(){var n=this;return i.createElement(t.Consumer,null,(function(t){var r=t.pageResources,o=t.location;return i.createElement(E,{location:o},i.createElement(u.$C,{location:o,shouldUpdateScroll:w},i.createElement(a.Router,{basepath:"",location:o,id:"gatsby-focus-wrapper"},i.createElement(e,Object.assign({path:"/404.html"===r.page.path||"/500.html"===r.page.path?(0,D.Z)(o.pathname,""):encodeURI((r.page.matchPath||r.page.path).split("?")[0])},n.props,{location:o,pageResources:r},r.json)))))}))},o}(i.Component),p=window,d=p.pagePath,h=p.location;d&&""+d!==h.pathname+(d.includes("?")?h.search:"")&&!(A.findMatchPath((0,D.Z)(h.pathname,""))||d.match(/^\/(404|500)(\/?|.html)$/)||d.match(/^\/offline-plugin-app-shell-fallback\/?$/))&&(0,a.navigate)(""+d+(d.includes("?")?"":h.search)+h.hash,{replace:!0});var m=function(){try{return sessionStorage}catch(e){return null}};s.jN.loadPage(h.pathname+h.search).then((function(e){var t,n=m();if(null!=e&&null!==(t=e.page)&&void 0!==t&&t.webpackCompilationHash&&e.page.webpackCompilationHash!==window.___webpackCompilationHash&&("serviceWorker"in navigator&&null!==navigator.serviceWorker.controller&&"activated"===navigator.serviceWorker.controller.state&&navigator.serviceWorker.controller.postMessage({gatsbyApi:"clearPathResources"}),n&&!("1"===n.getItem(M))))return n.setItem(M,"1"),void window.location.reload(!0);if(n&&n.removeItem(M),!e||e.status===s.uQ.Error){var r="page resources for "+h.pathname+" not found. Not rendering React";if(e&&e.error)throw console.error(r),e.error;throw new Error(r)}var a=(0,o.h)("wrapRootElement",{element:i.createElement(f,null)},i.createElement(f,null),(function(e){return{element:e.result}})).pop(),u=function(){var e=i.useRef(!1);return i.useEffect((function(){e.current||(e.current=!0,performance.mark&&performance.mark("onInitialClientRender"),(0,o.h)("onInitialClientRender"))}),[]),i.createElement(l,null,a)},c=document.getElementById("gatsby-focus-wrapper"),p=T;c&&c.children.length&&(p=R);var d=(0,o.h)("replaceHydrateFunction",void 0,p)[0];function v(){var e="undefined"!=typeof window?document.getElementById("___gatsby"):null;d(i.createElement(u,null),e)}var g=document;if("complete"===g.readyState||"loading"!==g.readyState&&!g.documentElement.doScroll)setTimeout((function(){v()}),0);else{var y=function e(){g.removeEventListener("DOMContentLoaded",e,!1),window.removeEventListener("load",e,!1),v()};g.addEventListener("DOMContentLoaded",y,!1),window.addEventListener("load",y,!1)}}))}))},224:function(e,t,n){"use strict";n.r(t);var r=n(4942),o=n(7294),i=n(1975),a=n(8522);function u(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}t.default=function(e){var t=e.location,n=i.ZP.loadPageSync(t.pathname);return n?o.createElement(a.Z,function(e){for(var t=1;t1&&void 0!==arguments[1]?arguments[1]:{},r=window.Promise||function(e){function t(){}e(t,t)},i=function(e){var t=e.target;t!==L?-1!==S.indexOf(t)&&O({target:t}):P()},p=function(){if(!C&&T.original){var e=window.pageYOffset||document.documentElement.scrollTop||document.body.scrollTop||0;Math.abs(_-e)>R.scrollOffset&&setTimeout(P,150)}},d=function(e){var t=e.key||e.keyCode;"Escape"!==t&&"Esc"!==t&&27!==t||P()},h=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=e;if(e.background&&(L.style.background=e.background),e.container&&e.container instanceof Object&&(t.container=o({},R.container,e.container)),e.template){var n=a(e.template)?e.template:document.querySelector(e.template);t.template=n}return R=o({},R,t),S.forEach((function(e){e.dispatchEvent(f("medium-zoom:update",{detail:{zoom:D}}))})),D},m=function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return e(o({},R,t))},v=function(){for(var e=arguments.length,t=Array(e),n=0;n0?t.reduce((function(e,t){return[].concat(e,c(t))}),[]):S;return r.forEach((function(e){e.classList.remove("medium-zoom-image"),e.dispatchEvent(f("medium-zoom:detach",{detail:{zoom:D}}))})),S=S.filter((function(e){return-1===r.indexOf(e)})),D},y=function(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};return S.forEach((function(r){r.addEventListener("medium-zoom:"+e,t,n)})),x.push({type:"medium-zoom:"+e,listener:t,options:n}),D},b=function(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};return S.forEach((function(r){r.removeEventListener("medium-zoom:"+e,t,n)})),x=x.filter((function(n){return!(n.type==="medium-zoom:"+e&&n.listener.toString()===t.toString())})),D},w=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=e.target,n=function(){var e={width:document.documentElement.clientWidth,height:document.documentElement.clientHeight,left:0,top:0,right:0,bottom:0},t=void 0,n=void 0;if(R.container)if(R.container instanceof Object)t=(e=o({},e,R.container)).width-e.left-e.right-2*R.margin,n=e.height-e.top-e.bottom-2*R.margin;else{var r=(a(R.container)?R.container:document.querySelector(R.container)).getBoundingClientRect(),i=r.width,c=r.height,s=r.left,l=r.top;e=o({},e,{width:i,height:c,left:s,top:l})}t=t||e.width-2*R.margin,n=n||e.height-2*R.margin;var f=T.zoomedHd||T.original,p=u(f)?t:f.naturalWidth||t,d=u(f)?n:f.naturalHeight||n,h=f.getBoundingClientRect(),m=h.top,v=h.left,g=h.width,y=h.height,b=Math.min(p,t)/g,w=Math.min(d,n)/y,P=Math.min(b,w),O="scale("+P+") translate3d("+((t-g)/2-v+R.margin+e.left)/P+"px, "+((n-y)/2-m+R.margin+e.top)/P+"px, 0)";T.zoomed.style.transform=O,T.zoomedHd&&(T.zoomedHd.style.transform=O)};return new r((function(e){if(t&&-1===S.indexOf(t))e(D);else{if(T.zoomed)e(D);else{if(t)T.original=t;else{if(!(S.length>0))return void e(D);var r=S;T.original=r[0]}if(T.original.dispatchEvent(f("medium-zoom:open",{detail:{zoom:D}})),_=window.pageYOffset||document.documentElement.scrollTop||document.body.scrollTop||0,C=!0,T.zoomed=l(T.original),document.body.appendChild(L),R.template){var o=a(R.template)?R.template:document.querySelector(R.template);T.template=document.createElement("div"),T.template.appendChild(o.content.cloneNode(!0)),document.body.appendChild(T.template)}if(document.body.appendChild(T.zoomed),window.requestAnimationFrame((function(){document.body.classList.add("medium-zoom--opened")})),T.original.classList.add("medium-zoom-image--hidden"),T.zoomed.classList.add("medium-zoom-image--opened"),T.zoomed.addEventListener("click",P),T.zoomed.addEventListener("transitionend",(function t(){C=!1,T.zoomed.removeEventListener("transitionend",t),T.original.dispatchEvent(f("medium-zoom:opened",{detail:{zoom:D}})),e(D)})),T.original.getAttribute("data-zoom-src")){T.zoomedHd=T.zoomed.cloneNode(),T.zoomedHd.removeAttribute("srcset"),T.zoomedHd.removeAttribute("sizes"),T.zoomedHd.src=T.zoomed.getAttribute("data-zoom-src"),T.zoomedHd.onerror=function(){clearInterval(i),console.warn("Unable to reach the zoom image target "+T.zoomedHd.src),T.zoomedHd=null,n()};var i=setInterval((function(){T.zoomedHd.complete&&(clearInterval(i),T.zoomedHd.classList.add("medium-zoom-image--opened"),T.zoomedHd.addEventListener("click",P),document.body.appendChild(T.zoomedHd),n())}),10)}else if(T.original.hasAttribute("srcset")){T.zoomedHd=T.zoomed.cloneNode(),T.zoomedHd.removeAttribute("sizes"),T.zoomedHd.removeAttribute("loading");var u=T.zoomedHd.addEventListener("load",(function(){T.zoomedHd.removeEventListener("load",u),T.zoomedHd.classList.add("medium-zoom-image--opened"),T.zoomedHd.addEventListener("click",P),document.body.appendChild(T.zoomedHd),n()}))}else n()}}}))},P=function(){return new r((function(e){if(!C&&T.original){C=!0,document.body.classList.remove("medium-zoom--opened"),T.zoomed.style.transform="",T.zoomedHd&&(T.zoomedHd.style.transform=""),T.template&&(T.template.style.transition="opacity 150ms",T.template.style.opacity=0),T.original.dispatchEvent(f("medium-zoom:close",{detail:{zoom:D}})),T.zoomed.addEventListener("transitionend",(function t(){T.original.classList.remove("medium-zoom-image--hidden"),document.body.removeChild(T.zoomed),T.zoomedHd&&document.body.removeChild(T.zoomedHd),document.body.removeChild(L),T.zoomed.classList.remove("medium-zoom-image--opened"),T.template&&document.body.removeChild(T.template),C=!1,T.zoomed.removeEventListener("transitionend",t),T.original.dispatchEvent(f("medium-zoom:closed",{detail:{zoom:D}})),T.original=null,T.zoomed=null,T.zoomedHd=null,T.template=null,e(D)}))}else e(D)}))},O=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=e.target;return T.original?P():w({target:t})},E=function(){return R},j=function(){return S},k=function(){return T.original},S=[],x=[],C=!1,_=0,R=n,T={original:null,zoomed:null,zoomedHd:null,template:null};"[object Object]"===Object.prototype.toString.call(t)?R=t:(t||"string"==typeof t)&&v(t),R=o({margin:0,background:"#fff",scrollOffset:40,container:null,template:null},R);var L=s(R.background);document.addEventListener("click",i),document.addEventListener("keyup",d),document.addEventListener("scroll",p),window.addEventListener("resize",P);var D={open:w,close:P,toggle:O,update:h,clone:m,attach:v,detach:g,on:y,off:b,getOptions:E,getImages:j,getZoomedImage:k};return D},d=(n(456),["excludedSelector","includedSelector"]);function h(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function m(e){for(var t=1;t=0||(o[n]=e[n]);return o}(e,d),o=t?g+":not("+t+")":g,i=Array.from(document.querySelectorAll(o));if(n){var a=Array.from(document.querySelectorAll(n));i=i.concat(a)}var u=i.filter((function(e){return!e.classList.contains("medium-zoom-image")})).map((function(e){return e.addEventListener("load",(function t(){var n=e.style.transition;e.style.transition=n+", opacity 0.5s, transform .3s cubic-bezier(.2,0,.2,1)",e.removeEventListener("load",t)})),e.setAttribute("tabIndex",0),e.addEventListener("keydown",(function(t){" "!==t.key&&"Enter"!==t.key||(t.preventDefault(),e.click())})),e}));u.length>0&&p(u,r)}var w=function(e,t){var n=m(m({},v),t);!function(e){if(!document.querySelector("#"+y)){var t=e.zIndex,n=document.createElement("style"),r="\n .medium-zoom--opened > .medium-zoom-overlay,\n .medium-zoom--opened > .medium-zoom-image,\n\t img.medium-zoom-image--opened {\n z-index: "+t+"\n }\n ";n.id=y,n.innerHTML=r,document.head.appendChild(n)}}(n),function(e){if(window.performance&&window.PerformanceObserver){var t=new PerformanceObserver((function(t){return t.getEntries().filter((function(e){return"paint"===e.entryType})).map((function(e){return"first-contentful-paint"===e.name})).forEach(e)}));try{t.observe({entryTypes:["measure","paint"]})}catch(n){console.error(n),t.disconnect()}}}((function(){return b(n)})),b(n)}},855:function(e,t){"use strict";t.DEFAULT_OPTIONS={maxWidth:650,wrapperStyle:"",backgroundColor:"white",linkImagesToOriginal:!0,showCaptions:!1,markdownCaptions:!1,withWebp:!1,withAvif:!1,tracedSVG:!1,loading:"lazy",decoding:"async",disableBgImageOnAlpha:!1,disableBgImage:!1},t.EMPTY_ALT="GATSBY_EMPTY_ALT",t.imageClass="gatsby-resp-image-image",t.imageWrapperClass="gatsby-resp-image-wrapper",t.imageBackgroundClass="gatsby-resp-image-background-image"},2154:function(e,t,n){"use strict";var r=n(855),o=r.DEFAULT_OPTIONS,i=r.imageClass,a=r.imageBackgroundClass,u=r.imageWrapperClass;t.onRouteUpdate=function(e,t){for(var n=Object.assign({},o,t),r=document.querySelectorAll("."+u),c=function(e){var t=r[e],o=t.querySelector("."+a),u=t.querySelector("."+i),c=function(){o.style.transition="opacity 0.5s 0.5s",u.style.transition="opacity 0.5s",s()},s=function e(){o.style.opacity=0,u.style.opacity=1,u.style.color="inherit",u.style.boxShadow="inset 0px 0px 0px 400px "+n.backgroundColor,u.removeEventListener("load",c),u.removeEventListener("error",e)};u.style.opacity=0,u.addEventListener("load",c),u.addEventListener("error",s),u.complete&&s()},s=0;st.score?-1:e.index-t.index}))},g=function(e){return e.replace(/(^\/+|\/+$)/g,"").split("/")},y=function(e){for(var t=arguments.length,n=Array(t>1?t-1:0),r=1;r0})))&&n.length>0?"?"+n.join("&"):"")},b=["uri","path"],w=Object.assign||function(e){for(var t=1;t1&&void 0!==arguments[1]?arguments[1]:{},u=a.state,c=a.replace,s=void 0!==c&&c;if("number"==typeof t)e.history.go(t);else{u=w({},u,{key:Date.now()+""});try{o||s?e.history.replaceState(u,null,t):e.history.pushState(u,null,t)}catch(f){e.location[s?"replace":"assign"](t)}}r=P(e),o=!0;var l=new Promise((function(e){return i=e}));return n.forEach((function(e){return e({location:r,action:"PUSH"})})),l}}},E=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"/",t=e.indexOf("?"),n={pathname:t>-1?e.substr(0,t):e,search:t>-1?e.substr(t):""},r=0,o=[n],i=[null];return{get location(){return o[r]},addEventListener:function(e,t){},removeEventListener:function(e,t){},history:{get entries(){return o},get index(){return r},get state(){return i[r]},pushState:function(e,t,n){var a=n.split("?"),u=a[0],c=a[1],s=void 0===c?"":c;r++,o.push({pathname:u,search:s.length?"?"+s:s}),i.push(e)},replaceState:function(e,t,n){var a=n.split("?"),u=a[0],c=a[1],s=void 0===c?"":c;o[r]={pathname:u,search:s},i[r]=e},go:function(e){var t=r+e;t<0||t>i.length-1||(r=t)}}}},j=!("undefined"==typeof window||!window.document||!window.document.createElement),k=O(j?window:E()),S=k.navigate,x=Object.assign||function(e){for(var t=1;t=0||Object.prototype.hasOwnProperty.call(e,r)&&(n[r]=e[r]);return n}function _(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function R(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function T(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}var L=function(e,t){var n=(0,r.createContext)(t);return n.displayName=e,n},D=L("Location"),A=function(e){var t=e.children;return r.createElement(D.Consumer,null,(function(e){return e?t(e):r.createElement(z,null,t)}))},z=function(e){function t(){var n,r;_(this,t);for(var o=arguments.length,i=Array(o),a=0;a-1?(i=t.substring(0,o),a=t.substring(o)):i=t,r.createElement(D.Provider,{value:{location:{pathname:i,search:a,hash:""},navigate:function(){throw new Error("You can't call navigate on the server.")}}},n)},I=L("Base",{baseuri:"/",basepath:"/",navigate:k.navigate}),U=function(e){return r.createElement(I.Consumer,null,(function(t){return r.createElement(A,null,(function(n){return r.createElement(W,x({},t,n,e))}))}))},W=function(e){function t(){return _(this,t),R(this,e.apply(this,arguments))}return T(t,e),t.prototype.render=function(){var e=this.props,t=e.location,n=e.navigate,o=e.basepath,i=e.primary,a=e.children,u=(e.baseuri,e.component),s=void 0===u?"div":u,f=C(e,["location","navigate","basepath","primary","children","baseuri","component"]),p=r.Children.toArray(a).reduce((function(e,t){var n=ie(o)(t);return e.concat(n)}),[]),d=t.pathname,h=c(p,d);if(h){var m=h.params,v=h.uri,g=h.route,y=h.route.value;o=g.default?o:g.path.replace(/\*$/,"");var b=x({},m,{uri:v,location:t,navigate:function(e,t){return n(l(e,v),t)}}),w=r.cloneElement(y,b,y.props.children?r.createElement(U,{location:t,primary:i},y.props.children):void 0),P=i?N:s,O=i?x({uri:v,location:t,component:s},f):f;return r.createElement(I.Provider,{value:{baseuri:v,basepath:o,navigate:b.navigate}},r.createElement(P,O,w))}return null},t}(r.PureComponent);W.defaultProps={primary:!0};var H=L("Focus"),N=function(e){var t=e.uri,n=e.location,o=e.component,i=C(e,["uri","location","component"]);return r.createElement(H.Consumer,null,(function(e){return r.createElement(Z,x({},i,{component:o,requestFocus:e,uri:t,location:n}))}))},F=!0,q=0,Z=function(e){function t(){var n,r;_(this,t);for(var o=arguments.length,i=Array(o),a=0;a((e,t)=>{const{forward:n=[],...r}=e||{},o=JSON.stringify(r,((e,t)=>("function"==typeof t&&(t=String(t)).startsWith(e+"(")&&(t="function "+t),t)));return["!(function(w,p,f,c){",Object.keys(r).length>0?`c=w[p]=Object.assign(w[p]||{},${o});`:"c=w[p]=w[p]||{};","c[f]=(c[f]||[])",n.length>0?`.concat(${JSON.stringify(n)})`:"","})(window,'partytown','forward');",t].join("")})(e,'/* Partytown 0.5.4 - MIT builder.io */\n!function(t,e,n,i,r,o,a,d,s,c,p,l){function u(){l||(l=1,"/"==(a=(o.lib||"/~partytown/")+(o.debug?"debug/":""))[0]&&(s=e.querySelectorAll(\'script[type="text/partytown"]\'),i!=t?i.dispatchEvent(new CustomEvent("pt1",{detail:t})):(d=setTimeout(w,1e4),e.addEventListener("pt0",f),r?h(1):n.serviceWorker?n.serviceWorker.register(a+(o.swPath||"partytown-sw.js"),{scope:a}).then((function(t){t.active?h():t.installing&&t.installing.addEventListener("statechange",(function(t){"activated"==t.target.state&&h()}))}),console.error):w())))}function h(t){c=e.createElement(t?"script":"iframe"),t||(c.setAttribute("style","display:block;width:0;height:0;border:0;visibility:hidden"),c.setAttribute("aria-hidden",!0)),c.src=a+"partytown-"+(t?"atomics.js?v=0.5.4":"sandbox-sw.html?"+Date.now()),e.body.appendChild(c)}function w(t,n){for(f(),t=0;t{if("undefined"!=typeof document&&!document._partytown){if(!document.querySelector("script[data-partytown]")){const t=document.createElement("script");t.dataset.partytown="",t.innerHTML=o.partytownSnippet(e),document.head.appendChild(t)}document._partytown=!0}const t=o.partytownSnippet(e)+'document.currentScript.dataset.partytown="";';return a.default.createElement("script",{suppressHydrationWarning:!0,dangerouslySetInnerHTML:{__html:t}})}},907:function(e,t,n){"use strict";function r(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n arr.length) len = arr.length;\n\n for (var i = 0, arr2 = new Array(len); i < len; i++) {\n arr2[i] = arr[i];\n }\n\n return arr2;\n}\n\nmodule.exports = _arrayLikeToArray, module.exports.__esModule = true, module.exports[\"default\"] = module.exports;","function _arrayWithHoles(arr) {\n if (Array.isArray(arr)) return arr;\n}\n\nmodule.exports = _arrayWithHoles, module.exports.__esModule = true, module.exports[\"default\"] = module.exports;","function _assertThisInitialized(self) {\n if (self === void 0) {\n throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\");\n }\n\n return self;\n}\n\nmodule.exports = _assertThisInitialized, module.exports.__esModule = true, module.exports[\"default\"] = module.exports;","function _extends() {\n module.exports = _extends = Object.assign ? Object.assign.bind() : function (target) {\n for (var i = 1; i < arguments.length; i++) {\n var source = arguments[i];\n\n for (var key in source) {\n if (Object.prototype.hasOwnProperty.call(source, key)) {\n target[key] = source[key];\n }\n }\n }\n\n return target;\n }, module.exports.__esModule = true, module.exports[\"default\"] = module.exports;\n return _extends.apply(this, arguments);\n}\n\nmodule.exports = _extends, module.exports.__esModule = true, module.exports[\"default\"] = module.exports;","var setPrototypeOf = require(\"./setPrototypeOf.js\");\n\nfunction _inheritsLoose(subClass, superClass) {\n subClass.prototype = Object.create(superClass.prototype);\n subClass.prototype.constructor = subClass;\n setPrototypeOf(subClass, superClass);\n}\n\nmodule.exports = _inheritsLoose, module.exports.__esModule = true, module.exports[\"default\"] = module.exports;","function _interopRequireDefault(obj) {\n return obj && obj.__esModule ? obj : {\n \"default\": obj\n };\n}\n\nmodule.exports = _interopRequireDefault, module.exports.__esModule = true, module.exports[\"default\"] = module.exports;","function _iterableToArrayLimit(arr, i) {\n var _i = arr == null ? null : typeof Symbol !== \"undefined\" && arr[Symbol.iterator] || arr[\"@@iterator\"];\n\n if (_i == null) return;\n var _arr = [];\n var _n = true;\n var _d = false;\n\n var _s, _e;\n\n try {\n for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) {\n _arr.push(_s.value);\n\n if (i && _arr.length === i) break;\n }\n } catch (err) {\n _d = true;\n _e = err;\n } finally {\n try {\n if (!_n && _i[\"return\"] != null) _i[\"return\"]();\n } finally {\n if (_d) throw _e;\n }\n }\n\n return _arr;\n}\n\nmodule.exports = _iterableToArrayLimit, module.exports.__esModule = true, module.exports[\"default\"] = module.exports;","function _nonIterableRest() {\n throw new TypeError(\"Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.\");\n}\n\nmodule.exports = _nonIterableRest, module.exports.__esModule = true, module.exports[\"default\"] = module.exports;","function _objectWithoutPropertiesLoose(source, excluded) {\n if (source == null) return {};\n var target = {};\n var sourceKeys = Object.keys(source);\n var key, i;\n\n for (i = 0; i < sourceKeys.length; i++) {\n key = sourceKeys[i];\n if (excluded.indexOf(key) >= 0) continue;\n target[key] = source[key];\n }\n\n return target;\n}\n\nmodule.exports = _objectWithoutPropertiesLoose, module.exports.__esModule = true, module.exports[\"default\"] = module.exports;","function _setPrototypeOf(o, p) {\n module.exports = _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) {\n o.__proto__ = p;\n return o;\n }, module.exports.__esModule = true, module.exports[\"default\"] = module.exports;\n return _setPrototypeOf(o, p);\n}\n\nmodule.exports = _setPrototypeOf, module.exports.__esModule = true, module.exports[\"default\"] = module.exports;","var arrayWithHoles = require(\"./arrayWithHoles.js\");\n\nvar iterableToArrayLimit = require(\"./iterableToArrayLimit.js\");\n\nvar unsupportedIterableToArray = require(\"./unsupportedIterableToArray.js\");\n\nvar nonIterableRest = require(\"./nonIterableRest.js\");\n\nfunction _slicedToArray(arr, i) {\n return arrayWithHoles(arr) || iterableToArrayLimit(arr, i) || unsupportedIterableToArray(arr, i) || nonIterableRest();\n}\n\nmodule.exports = _slicedToArray, module.exports.__esModule = true, module.exports[\"default\"] = module.exports;","var arrayLikeToArray = require(\"./arrayLikeToArray.js\");\n\nfunction _unsupportedIterableToArray(o, minLen) {\n if (!o) return;\n if (typeof o === \"string\") return arrayLikeToArray(o, minLen);\n var n = Object.prototype.toString.call(o).slice(8, -1);\n if (n === \"Object\" && o.constructor) n = o.constructor.name;\n if (n === \"Map\" || n === \"Set\") return Array.from(o);\n if (n === \"Arguments\" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return arrayLikeToArray(o, minLen);\n}\n\nmodule.exports = _unsupportedIterableToArray, module.exports.__esModule = true, module.exports[\"default\"] = module.exports;","/*!\n * Performance Observer Polyfill\n * Version: 1.1.1\n * https://github.com/fastly/performance-observer-polyfill\n * \n * Copyright (c) 2020, Fastly, Inc. All rights reserved.\n * \n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n * \n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n * \n */\n!function (e, t) {\n \"object\" == typeof exports && \"object\" == typeof module ? module.exports = t() : \"function\" == typeof define && define.amd ? define([], t) : \"object\" == typeof exports ? exports.PerformanceOberserverPolyfill = t() : e.PerformanceOberserverPolyfill = t();\n}(this, function () {\n return function (e) {\n var t = {};\n\n function r(n) {\n if (t[n]) return t[n].exports;\n var o = t[n] = {\n i: n,\n l: !1,\n exports: {}\n };\n return e[n].call(o.exports, o, o.exports, r), o.l = !0, o.exports;\n }\n\n return r.m = e, r.c = t, r.d = function (e, t, n) {\n r.o(e, t) || Object.defineProperty(e, t, {\n enumerable: !0,\n get: n\n });\n }, r.r = function (e) {\n \"undefined\" != typeof Symbol && Symbol.toStringTag && Object.defineProperty(e, Symbol.toStringTag, {\n value: \"Module\"\n }), Object.defineProperty(e, \"__esModule\", {\n value: !0\n });\n }, r.t = function (e, t) {\n if (1 & t && (e = r(e)), 8 & t) return e;\n if (4 & t && \"object\" == typeof e && e && e.__esModule) return e;\n var n = Object.create(null);\n if (r.r(n), Object.defineProperty(n, \"default\", {\n enumerable: !0,\n value: e\n }), 2 & t && \"string\" != typeof e) for (var o in e) {\n r.d(n, o, function (t) {\n return e[t];\n }.bind(null, o));\n }\n return n;\n }, r.n = function (e) {\n var t = e && e.__esModule ? function () {\n return e.default;\n } : function () {\n return e;\n };\n return r.d(t, \"a\", t), t;\n }, r.o = function (e, t) {\n return Object.prototype.hasOwnProperty.call(e, t);\n }, r.p = \"\", r(r.s = 0);\n }([function (e, t, r) {\n \"use strict\";\n\n function n(e) {\n return (n = \"function\" == typeof Symbol && \"symbol\" == typeof Symbol.iterator ? function (e) {\n return typeof e;\n } : function (e) {\n return e && \"function\" == typeof Symbol && e.constructor === Symbol && e !== Symbol.prototype ? \"symbol\" : typeof e;\n })(e);\n }\n\n function o(e) {\n return function (e) {\n if (Array.isArray(e)) {\n for (var t = 0, r = new Array(e.length); t < e.length; t++) {\n r[t] = e[t];\n }\n\n return r;\n }\n }(e) || function (e) {\n if (Symbol.iterator in Object(e) || \"[object Arguments]\" === Object.prototype.toString.call(e)) return Array.from(e);\n }(e) || function () {\n throw new TypeError(\"Invalid attempt to spread non-iterable instance\");\n }();\n }\n\n function i(e, t) {\n for (var r = 0; r < t.length; r++) {\n var n = t[r];\n n.enumerable = n.enumerable || !1, n.configurable = !0, \"value\" in n && (n.writable = !0), Object.defineProperty(e, n.key, n);\n }\n }\n\n function u(e) {\n if (void 0 === e) throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\");\n return e;\n }\n\n function s(e) {\n var t = \"function\" == typeof Map ? new Map() : void 0;\n return (s = function s(e) {\n if (null === e || (r = e, -1 === Function.toString.call(r).indexOf(\"[native code]\"))) return e;\n var r;\n if (\"function\" != typeof e) throw new TypeError(\"Super expression must either be null or a function\");\n\n if (void 0 !== t) {\n if (t.has(e)) return t.get(e);\n t.set(e, n);\n }\n\n function n() {\n return c(e, arguments, f(this).constructor);\n }\n\n return n.prototype = Object.create(e.prototype, {\n constructor: {\n value: n,\n enumerable: !1,\n writable: !0,\n configurable: !0\n }\n }), a(n, e);\n })(e);\n }\n\n function c(e, t, r) {\n return (c = function () {\n if (\"undefined\" == typeof Reflect || !Reflect.construct) return !1;\n if (Reflect.construct.sham) return !1;\n if (\"function\" == typeof Proxy) return !0;\n\n try {\n return Date.prototype.toString.call(Reflect.construct(Date, [], function () {})), !0;\n } catch (e) {\n return !1;\n }\n }() ? Reflect.construct : function (e, t, r) {\n var n = [null];\n n.push.apply(n, t);\n var o = new (Function.bind.apply(e, n))();\n return r && a(o, r.prototype), o;\n }).apply(null, arguments);\n }\n\n function a(e, t) {\n return (a = Object.setPrototypeOf || function (e, t) {\n return e.__proto__ = t, e;\n })(e, t);\n }\n\n function f(e) {\n return (f = Object.setPrototypeOf ? Object.getPrototypeOf : function (e) {\n return e.__proto__ || Object.getPrototypeOf(e);\n })(e);\n }\n\n r.r(t);\n\n var l = function (e) {\n function t(e) {\n var r, i, s, c, a;\n return function (e, t) {\n if (!(e instanceof t)) throw new TypeError(\"Cannot call a class as a function\");\n }(this, t), i = function (e, t) {\n return !t || \"object\" !== n(t) && \"function\" != typeof t ? u(e) : t;\n }(this, (r = f(t)).call.apply(r, [this].concat(o(e)))), s = u(i), a = void 0, (c = \"_entries\") in s ? Object.defineProperty(s, c, {\n value: a,\n enumerable: !0,\n configurable: !0,\n writable: !0\n }) : s[c] = a, i._entries = e, i;\n }\n\n var r, s, c;\n return function (e, t) {\n if (\"function\" != typeof t && null !== t) throw new TypeError(\"Super expression must either be null or a function\");\n e.prototype = Object.create(t && t.prototype, {\n constructor: {\n value: e,\n writable: !0,\n configurable: !0\n }\n }), t && a(e, t);\n }(t, e), r = t, (s = [{\n key: \"getEntries\",\n value: function value() {\n return this._entries;\n }\n }, {\n key: \"getEntriesByType\",\n value: function value(e) {\n return this._entries.filter(function (t) {\n return t.entryType === e;\n });\n }\n }, {\n key: \"getEntriesByName\",\n value: function value(e, t) {\n return this._entries.filter(function (t) {\n return t.name === e;\n }).filter(function (e) {\n return !t || e.entryType === t;\n });\n }\n }]) && i(r.prototype, s), c && i(r, c), t;\n }(s(Array));\n\n function y(e, t) {\n for (var r = 0; r < t.length; r++) {\n var n = t[r];\n n.enumerable = n.enumerable || !1, n.configurable = !0, \"value\" in n && (n.writable = !0), Object.defineProperty(e, n.key, n);\n }\n }\n\n function p(e, t, r) {\n return t in e ? Object.defineProperty(e, t, {\n value: r,\n enumerable: !0,\n configurable: !0,\n writable: !0\n }) : e[t] = r, e;\n }\n\n function v(e, t) {\n for (var r = 0; r < t.length; r++) {\n var n = t[r];\n n.enumerable = n.enumerable || !1, n.configurable = !0, \"value\" in n && (n.writable = !0), Object.defineProperty(e, n.key, n);\n }\n }\n\n function b(e, t, r) {\n return t in e ? Object.defineProperty(e, t, {\n value: r,\n enumerable: !0,\n configurable: !0,\n writable: !0\n }) : e[t] = r, e;\n }\n\n var d = [\"mark\", \"measure\", \"navigation\", \"resource\"],\n h = \"Failed to execute 'observe' on 'PerformanceObserver': either an 'entryTypes' or 'type' member must be present.\",\n m = \"Failed to execute 'observe' on 'PerformanceObserver': either an 'entryTypes' or 'type' member must be present, not both.\",\n g = \"Aborting 'observe' on 'PerformanceObserver': no valid entry types present in either 'entryTypes' or 'type' member.\",\n O = \"Invalid or unsupported entry types provided to 'observe' on 'PerformanceObserver'.\",\n w = function w(e) {\n return d.some(function (t) {\n return e === t;\n });\n },\n P = new (function () {\n function e() {\n var t = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {},\n r = t.registeredObservers,\n n = void 0 === r ? new Set() : r,\n o = t.processedEntries,\n i = void 0 === o ? new Set() : o,\n u = t.interval,\n s = void 0 === u ? 100 : u,\n c = t.context,\n a = void 0 === c ? self : c;\n !function (e, t) {\n if (!(e instanceof t)) throw new TypeError(\"Cannot call a class as a function\");\n }(this, e), p(this, \"registeredObservers\", void 0), p(this, \"processedEntries\", void 0), p(this, \"interval\", void 0), p(this, \"intervalId\", void 0), p(this, \"context\", void 0), this.registeredObservers = n, this.processedEntries = i, this.interval = s, this.context = a, this.intervalId = null;\n }\n\n var t, r, n;\n return t = e, (r = [{\n key: \"getNewEntries\",\n value: function value() {\n var e = this;\n return this.context.performance.getEntries().filter(function (t) {\n return !e.processedEntries.has(t);\n });\n }\n }, {\n key: \"getObserversForType\",\n value: function value(e, t) {\n return Array.from(e).filter(function (e) {\n return e.entryTypes.some(function (e) {\n return e === t;\n });\n });\n }\n }, {\n key: \"processBuffer\",\n value: function value(e) {\n var t = Array.from(e.buffer),\n r = new l(t);\n e.buffer.clear(), t.length && e.callback && e.callback.call(void 0, r, e);\n }\n }, {\n key: \"processEntries\",\n value: function value() {\n var e = this;\n this.getNewEntries().forEach(function (t) {\n var r = t.entryType;\n e.getObserversForType(e.registeredObservers, r).forEach(function (e) {\n e.buffer.add(t);\n }), e.processedEntries.add(t);\n });\n\n var t = function t() {\n return e.registeredObservers.forEach(e.processBuffer);\n };\n\n \"requestAnimationFrame\" in this.context ? this.context.requestAnimationFrame(t) : this.context.setTimeout(t, 0);\n }\n }, {\n key: \"add\",\n value: function value(e) {\n this.registeredObservers.add(e), 1 === this.registeredObservers.size && this.observe();\n }\n }, {\n key: \"remove\",\n value: function value(e) {\n this.registeredObservers.delete(e), this.registeredObservers.size || this.disconnect();\n }\n }, {\n key: \"observe\",\n value: function value() {\n this.intervalId = this.context.setInterval(this.processEntries.bind(this), this.interval);\n }\n }, {\n key: \"disconnect\",\n value: function value() {\n this.intervalId = this.context.clearInterval(this.intervalId);\n }\n }]) && y(t.prototype, r), n && y(t, n), e;\n }())(),\n k = function () {\n function e(t) {\n var r = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : P;\n !function (e, t) {\n if (!(e instanceof t)) throw new TypeError(\"Cannot call a class as a function\");\n }(this, e), b(this, \"callback\", void 0), b(this, \"buffer\", void 0), b(this, \"entryTypes\", []), b(this, \"taskQueue\", void 0), this.callback = t, this.buffer = new Set(), this.taskQueue = r;\n }\n\n var t, r, n;\n return t = e, (r = [{\n key: \"observe\",\n value: function value(e) {\n if (!e) throw new Error(h);\n if (e.entryTypes && e.type) throw new Error(m);\n var t;\n if (e.entryTypes) t = e.entryTypes;else {\n if (!e.type) throw new Error(h);\n t = [e.type];\n }\n var r = t.filter(w);\n r.length > 0 && r.length !== t.length && console.warn(O), r.length ? (this.entryTypes = r, this.taskQueue.add(this)) : console.warn(g);\n }\n }, {\n key: \"disconnect\",\n value: function value() {\n this.taskQueue.remove(this);\n }\n }, {\n key: \"takeRecords\",\n value: function value() {\n var e = Array.from(this.buffer);\n return new l(e);\n }\n }]) && v(t.prototype, r), n && v(t, n), e;\n }();\n\n b(k, \"supportedEntryTypes\", d);\n var E = \"PerformanceObserver\" in self && \"function\" == typeof PerformanceObserver ? PerformanceObserver : k,\n j = self;\n j.PerformanceObserver || (j.PerformanceObserver = E);\n }]);\n});","\"use strict\";\n\nexports.__esModule = true;\n\nvar _extends = Object.assign || function (target) {\n for (var i = 1; i < arguments.length; i++) {\n var source = arguments[i];\n\n for (var key in source) {\n if (Object.prototype.hasOwnProperty.call(source, key)) {\n target[key] = source[key];\n }\n }\n }\n\n return target;\n};\n\nvar getLocation = function getLocation(source) {\n var _source$location = source.location,\n search = _source$location.search,\n hash = _source$location.hash,\n href = _source$location.href,\n origin = _source$location.origin,\n protocol = _source$location.protocol,\n host = _source$location.host,\n hostname = _source$location.hostname,\n port = _source$location.port;\n var pathname = source.location.pathname;\n\n if (!pathname && href && canUseDOM) {\n var url = new URL(href);\n pathname = url.pathname;\n }\n\n return {\n pathname: encodeURI(decodeURI(pathname)),\n search: search,\n hash: hash,\n href: href,\n origin: origin,\n protocol: protocol,\n host: host,\n hostname: hostname,\n port: port,\n state: source.history.state,\n key: source.history.state && source.history.state.key || \"initial\"\n };\n};\n\nvar createHistory = function createHistory(source, options) {\n var listeners = [];\n var location = getLocation(source);\n var transitioning = false;\n\n var resolveTransition = function resolveTransition() {};\n\n return {\n get location() {\n return location;\n },\n\n get transitioning() {\n return transitioning;\n },\n\n _onTransitionComplete: function _onTransitionComplete() {\n transitioning = false;\n resolveTransition();\n },\n listen: function listen(listener) {\n listeners.push(listener);\n\n var popstateListener = function popstateListener() {\n location = getLocation(source);\n listener({\n location: location,\n action: \"POP\"\n });\n };\n\n source.addEventListener(\"popstate\", popstateListener);\n return function () {\n source.removeEventListener(\"popstate\", popstateListener);\n listeners = listeners.filter(function (fn) {\n return fn !== listener;\n });\n };\n },\n navigate: function navigate(to) {\n var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},\n state = _ref.state,\n _ref$replace = _ref.replace,\n replace = _ref$replace === undefined ? false : _ref$replace;\n\n if (typeof to === \"number\") {\n source.history.go(to);\n } else {\n state = _extends({}, state, {\n key: Date.now() + \"\"\n }); // try...catch iOS Safari limits to 100 pushState calls\n\n try {\n if (transitioning || replace) {\n source.history.replaceState(state, null, to);\n } else {\n source.history.pushState(state, null, to);\n }\n } catch (e) {\n source.location[replace ? \"replace\" : \"assign\"](to);\n }\n }\n\n location = getLocation(source);\n transitioning = true;\n var transition = new Promise(function (res) {\n return resolveTransition = res;\n });\n listeners.forEach(function (listener) {\n return listener({\n location: location,\n action: \"PUSH\"\n });\n });\n return transition;\n }\n };\n}; ////////////////////////////////////////////////////////////////////////////////\n// Stores history entries in memory for testing or other platforms like Native\n\n\nvar createMemorySource = function createMemorySource() {\n var initialPath = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : \"/\";\n var searchIndex = initialPath.indexOf(\"?\");\n var initialLocation = {\n pathname: searchIndex > -1 ? initialPath.substr(0, searchIndex) : initialPath,\n search: searchIndex > -1 ? initialPath.substr(searchIndex) : \"\"\n };\n var index = 0;\n var stack = [initialLocation];\n var states = [null];\n return {\n get location() {\n return stack[index];\n },\n\n addEventListener: function addEventListener(name, fn) {},\n removeEventListener: function removeEventListener(name, fn) {},\n history: {\n get entries() {\n return stack;\n },\n\n get index() {\n return index;\n },\n\n get state() {\n return states[index];\n },\n\n pushState: function pushState(state, _, uri) {\n var _uri$split = uri.split(\"?\"),\n pathname = _uri$split[0],\n _uri$split$ = _uri$split[1],\n search = _uri$split$ === undefined ? \"\" : _uri$split$;\n\n index++;\n stack.push({\n pathname: pathname,\n search: search.length ? \"?\" + search : search\n });\n states.push(state);\n },\n replaceState: function replaceState(state, _, uri) {\n var _uri$split2 = uri.split(\"?\"),\n pathname = _uri$split2[0],\n _uri$split2$ = _uri$split2[1],\n search = _uri$split2$ === undefined ? \"\" : _uri$split2$;\n\n stack[index] = {\n pathname: pathname,\n search: search\n };\n states[index] = state;\n },\n go: function go(to) {\n var newIndex = index + to;\n\n if (newIndex < 0 || newIndex > states.length - 1) {\n return;\n }\n\n index = newIndex;\n }\n }\n };\n}; ////////////////////////////////////////////////////////////////////////////////\n// global history - uses window.history as the source if available, otherwise a\n// memory history\n\n\nvar canUseDOM = !!(typeof window !== \"undefined\" && window.document && window.document.createElement);\n\nvar getSource = function getSource() {\n return canUseDOM ? window : createMemorySource();\n};\n\nvar globalHistory = createHistory(getSource());\nvar navigate = globalHistory.navigate; ////////////////////////////////////////////////////////////////////////////////\n\nexports.globalHistory = globalHistory;\nexports.navigate = navigate;\nexports.createHistory = createHistory;\nexports.createMemorySource = createMemorySource;","\"use strict\";\n\nexports.__esModule = true;\nexports.shallowCompare = exports.validateRedirect = exports.insertParams = exports.resolve = exports.match = exports.pick = exports.startsWith = undefined;\n\nvar _invariant = require(\"invariant\");\n\nvar _invariant2 = _interopRequireDefault(_invariant);\n\nfunction _interopRequireDefault(obj) {\n return obj && obj.__esModule ? obj : {\n default: obj\n };\n} ////////////////////////////////////////////////////////////////////////////////\n// startsWith(string, search) - Check if `string` starts with `search`\n\n\nvar startsWith = function startsWith(string, search) {\n return string.substr(0, search.length) === search;\n}; ////////////////////////////////////////////////////////////////////////////////\n// pick(routes, uri)\n//\n// Ranks and picks the best route to match. Each segment gets the highest\n// amount of points, then the type of segment gets an additional amount of\n// points where\n//\n// static > dynamic > splat > root\n//\n// This way we don't have to worry about the order of our routes, let the\n// computers do it.\n//\n// A route looks like this\n//\n// { path, default, value }\n//\n// And a returned match looks like:\n//\n// { route, params, uri }\n//\n// I know, I should use TypeScript not comments for these types.\n\n\nvar pick = function pick(routes, uri) {\n var match = void 0;\n var default_ = void 0;\n\n var _uri$split = uri.split(\"?\"),\n uriPathname = _uri$split[0];\n\n var uriSegments = segmentize(uriPathname);\n var isRootUri = uriSegments[0] === \"\";\n var ranked = rankRoutes(routes);\n\n for (var i = 0, l = ranked.length; i < l; i++) {\n var missed = false;\n var route = ranked[i].route;\n\n if (route.default) {\n default_ = {\n route: route,\n params: {},\n uri: uri\n };\n continue;\n }\n\n var routeSegments = segmentize(route.path);\n var params = {};\n var max = Math.max(uriSegments.length, routeSegments.length);\n var index = 0;\n\n for (; index < max; index++) {\n var routeSegment = routeSegments[index];\n var uriSegment = uriSegments[index];\n\n if (isSplat(routeSegment)) {\n // Hit a splat, just grab the rest, and return a match\n // uri: /files/documents/work\n // route: /files/*\n var param = routeSegment.slice(1) || \"*\";\n params[param] = uriSegments.slice(index).map(decodeURIComponent).join(\"/\");\n break;\n }\n\n if (uriSegment === undefined) {\n // URI is shorter than the route, no match\n // uri: /users\n // route: /users/:userId\n missed = true;\n break;\n }\n\n var dynamicMatch = paramRe.exec(routeSegment);\n\n if (dynamicMatch && !isRootUri) {\n var matchIsNotReserved = reservedNames.indexOf(dynamicMatch[1]) === -1;\n !matchIsNotReserved ? process.env.NODE_ENV !== \"production\" ? (0, _invariant2.default)(false, \" dynamic segment \\\"\" + dynamicMatch[1] + \"\\\" is a reserved name. Please use a different name in path \\\"\" + route.path + \"\\\".\") : (0, _invariant2.default)(false) : void 0;\n var value = decodeURIComponent(uriSegment);\n params[dynamicMatch[1]] = value;\n } else if (routeSegment !== uriSegment) {\n // Current segments don't match, not dynamic, not splat, so no match\n // uri: /users/123/settings\n // route: /users/:id/profile\n missed = true;\n break;\n }\n }\n\n if (!missed) {\n match = {\n route: route,\n params: params,\n uri: \"/\" + uriSegments.slice(0, index).join(\"/\")\n };\n break;\n }\n }\n\n return match || default_ || null;\n}; ////////////////////////////////////////////////////////////////////////////////\n// match(path, uri) - Matches just one path to a uri, also lol\n\n\nvar match = function match(path, uri) {\n return pick([{\n path: path\n }], uri);\n}; ////////////////////////////////////////////////////////////////////////////////\n// resolve(to, basepath)\n//\n// Resolves URIs as though every path is a directory, no files. Relative URIs\n// in the browser can feel awkward because not only can you be \"in a directory\"\n// you can be \"at a file\", too. For example\n//\n// browserSpecResolve('foo', '/bar/') => /bar/foo\n// browserSpecResolve('foo', '/bar') => /foo\n//\n// But on the command line of a file system, it's not as complicated, you can't\n// `cd` from a file, only directories. This way, links have to know less about\n// their current path. To go deeper you can do this:\n//\n// \n// // instead of\n// \n//\n// Just like `cd`, if you want to go deeper from the command line, you do this:\n//\n// cd deeper\n// # not\n// cd $(pwd)/deeper\n//\n// By treating every path as a directory, linking to relative paths should\n// require less contextual information and (fingers crossed) be more intuitive.\n\n\nvar resolve = function resolve(to, base) {\n // /foo/bar, /baz/qux => /foo/bar\n if (startsWith(to, \"/\")) {\n return to;\n }\n\n var _to$split = to.split(\"?\"),\n toPathname = _to$split[0],\n toQuery = _to$split[1];\n\n var _base$split = base.split(\"?\"),\n basePathname = _base$split[0];\n\n var toSegments = segmentize(toPathname);\n var baseSegments = segmentize(basePathname); // ?a=b, /users?b=c => /users?a=b\n\n if (toSegments[0] === \"\") {\n return addQuery(basePathname, toQuery);\n } // profile, /users/789 => /users/789/profile\n\n\n if (!startsWith(toSegments[0], \".\")) {\n var pathname = baseSegments.concat(toSegments).join(\"/\");\n return addQuery((basePathname === \"/\" ? \"\" : \"/\") + pathname, toQuery);\n } // ./ /users/123 => /users/123\n // ../ /users/123 => /users\n // ../.. /users/123 => /\n // ../../one /a/b/c/d => /a/b/one\n // .././one /a/b/c/d => /a/b/c/one\n\n\n var allSegments = baseSegments.concat(toSegments);\n var segments = [];\n\n for (var i = 0, l = allSegments.length; i < l; i++) {\n var segment = allSegments[i];\n if (segment === \"..\") segments.pop();else if (segment !== \".\") segments.push(segment);\n }\n\n return addQuery(\"/\" + segments.join(\"/\"), toQuery);\n}; ////////////////////////////////////////////////////////////////////////////////\n// insertParams(path, params)\n\n\nvar insertParams = function insertParams(path, params) {\n var _path$split = path.split(\"?\"),\n pathBase = _path$split[0],\n _path$split$ = _path$split[1],\n query = _path$split$ === undefined ? \"\" : _path$split$;\n\n var segments = segmentize(pathBase);\n var constructedPath = \"/\" + segments.map(function (segment) {\n var match = paramRe.exec(segment);\n return match ? params[match[1]] : segment;\n }).join(\"/\");\n var _params$location = params.location;\n _params$location = _params$location === undefined ? {} : _params$location;\n var _params$location$sear = _params$location.search,\n search = _params$location$sear === undefined ? \"\" : _params$location$sear;\n var searchSplit = search.split(\"?\")[1] || \"\";\n constructedPath = addQuery(constructedPath, query, searchSplit);\n return constructedPath;\n};\n\nvar validateRedirect = function validateRedirect(from, to) {\n var filter = function filter(segment) {\n return isDynamic(segment);\n };\n\n var fromString = segmentize(from).filter(filter).sort().join(\"/\");\n var toString = segmentize(to).filter(filter).sort().join(\"/\");\n return fromString === toString;\n}; ////////////////////////////////////////////////////////////////////////////////\n// Junk\n\n\nvar paramRe = /^:(.+)/;\nvar SEGMENT_POINTS = 4;\nvar STATIC_POINTS = 3;\nvar DYNAMIC_POINTS = 2;\nvar SPLAT_PENALTY = 1;\nvar ROOT_POINTS = 1;\n\nvar isRootSegment = function isRootSegment(segment) {\n return segment === \"\";\n};\n\nvar isDynamic = function isDynamic(segment) {\n return paramRe.test(segment);\n};\n\nvar isSplat = function isSplat(segment) {\n return segment && segment[0] === \"*\";\n};\n\nvar rankRoute = function rankRoute(route, index) {\n var score = route.default ? 0 : segmentize(route.path).reduce(function (score, segment) {\n score += SEGMENT_POINTS;\n if (isRootSegment(segment)) score += ROOT_POINTS;else if (isDynamic(segment)) score += DYNAMIC_POINTS;else if (isSplat(segment)) score -= SEGMENT_POINTS + SPLAT_PENALTY;else score += STATIC_POINTS;\n return score;\n }, 0);\n return {\n route: route,\n score: score,\n index: index\n };\n};\n\nvar rankRoutes = function rankRoutes(routes) {\n return routes.map(rankRoute).sort(function (a, b) {\n return a.score < b.score ? 1 : a.score > b.score ? -1 : a.index - b.index;\n });\n};\n\nvar segmentize = function segmentize(uri) {\n return uri // strip starting/ending slashes\n .replace(/(^\\/+|\\/+$)/g, \"\").split(\"/\");\n};\n\nvar addQuery = function addQuery(pathname) {\n for (var _len = arguments.length, query = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {\n query[_key - 1] = arguments[_key];\n }\n\n query = query.filter(function (q) {\n return q && q.length > 0;\n });\n return pathname + (query && query.length > 0 ? \"?\" + query.join(\"&\") : \"\");\n};\n\nvar reservedNames = [\"uri\", \"path\"];\n/**\n * Shallow compares two objects.\n * @param {Object} obj1 The first object to compare.\n * @param {Object} obj2 The second object to compare.\n */\n\nvar shallowCompare = function shallowCompare(obj1, obj2) {\n var obj1Keys = Object.keys(obj1);\n return obj1Keys.length === Object.keys(obj2).length && obj1Keys.every(function (key) {\n return obj2.hasOwnProperty(key) && obj1[key] === obj2[key];\n });\n}; ////////////////////////////////////////////////////////////////////////////////\n\n\nexports.startsWith = startsWith;\nexports.pick = pick;\nexports.match = match;\nexports.resolve = resolve;\nexports.insertParams = insertParams;\nexports.validateRedirect = validateRedirect;\nexports.shallowCompare = shallowCompare;","import React from 'react';\n\nfunction _defineProperty(obj, key, value) {\n if (key in obj) {\n Object.defineProperty(obj, key, {\n value: value,\n enumerable: true,\n configurable: true,\n writable: true\n });\n } else {\n obj[key] = value;\n }\n\n return obj;\n}\n\nfunction _extends() {\n _extends = Object.assign || function (target) {\n for (var i = 1; i < arguments.length; i++) {\n var source = arguments[i];\n\n for (var key in source) {\n if (Object.prototype.hasOwnProperty.call(source, key)) {\n target[key] = source[key];\n }\n }\n }\n\n return target;\n };\n\n return _extends.apply(this, arguments);\n}\n\nfunction ownKeys(object, enumerableOnly) {\n var keys = Object.keys(object);\n\n if (Object.getOwnPropertySymbols) {\n var symbols = Object.getOwnPropertySymbols(object);\n if (enumerableOnly) symbols = symbols.filter(function (sym) {\n return Object.getOwnPropertyDescriptor(object, sym).enumerable;\n });\n keys.push.apply(keys, symbols);\n }\n\n return keys;\n}\n\nfunction _objectSpread2(target) {\n for (var i = 1; i < arguments.length; i++) {\n var source = arguments[i] != null ? arguments[i] : {};\n\n if (i % 2) {\n ownKeys(Object(source), true).forEach(function (key) {\n _defineProperty(target, key, source[key]);\n });\n } else if (Object.getOwnPropertyDescriptors) {\n Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));\n } else {\n ownKeys(Object(source)).forEach(function (key) {\n Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));\n });\n }\n }\n\n return target;\n}\n\nfunction _objectWithoutPropertiesLoose(source, excluded) {\n if (source == null) return {};\n var target = {};\n var sourceKeys = Object.keys(source);\n var key, i;\n\n for (i = 0; i < sourceKeys.length; i++) {\n key = sourceKeys[i];\n if (excluded.indexOf(key) >= 0) continue;\n target[key] = source[key];\n }\n\n return target;\n}\n\nfunction _objectWithoutProperties(source, excluded) {\n if (source == null) return {};\n\n var target = _objectWithoutPropertiesLoose(source, excluded);\n\n var key, i;\n\n if (Object.getOwnPropertySymbols) {\n var sourceSymbolKeys = Object.getOwnPropertySymbols(source);\n\n for (i = 0; i < sourceSymbolKeys.length; i++) {\n key = sourceSymbolKeys[i];\n if (excluded.indexOf(key) >= 0) continue;\n if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue;\n target[key] = source[key];\n }\n }\n\n return target;\n}\n\nvar isFunction = function isFunction(obj) {\n return typeof obj === 'function';\n};\n\nvar MDXContext = /*#__PURE__*/React.createContext({});\n\nvar withMDXComponents = function withMDXComponents(Component) {\n return function (props) {\n var allComponents = useMDXComponents(props.components);\n return /*#__PURE__*/React.createElement(Component, _extends({}, props, {\n components: allComponents\n }));\n };\n};\n\nvar useMDXComponents = function useMDXComponents(components) {\n var contextComponents = React.useContext(MDXContext);\n var allComponents = contextComponents;\n\n if (components) {\n allComponents = isFunction(components) ? components(contextComponents) : _objectSpread2(_objectSpread2({}, contextComponents), components);\n }\n\n return allComponents;\n};\n\nvar MDXProvider = function MDXProvider(props) {\n var allComponents = useMDXComponents(props.components);\n return /*#__PURE__*/React.createElement(MDXContext.Provider, {\n value: allComponents\n }, props.children);\n};\n\nvar TYPE_PROP_NAME = 'mdxType';\nvar DEFAULTS = {\n inlineCode: 'code',\n wrapper: function wrapper(_ref) {\n var children = _ref.children;\n return /*#__PURE__*/React.createElement(React.Fragment, {}, children);\n }\n};\nvar MDXCreateElement = /*#__PURE__*/React.forwardRef(function (props, ref) {\n var propComponents = props.components,\n mdxType = props.mdxType,\n originalType = props.originalType,\n parentName = props.parentName,\n etc = _objectWithoutProperties(props, [\"components\", \"mdxType\", \"originalType\", \"parentName\"]);\n\n var components = useMDXComponents(propComponents);\n var type = mdxType;\n var Component = components[\"\".concat(parentName, \".\").concat(type)] || components[type] || DEFAULTS[type] || originalType;\n\n if (propComponents) {\n return /*#__PURE__*/React.createElement(Component, _objectSpread2(_objectSpread2({\n ref: ref\n }, etc), {}, {\n components: propComponents\n }));\n }\n\n return /*#__PURE__*/React.createElement(Component, _objectSpread2({\n ref: ref\n }, etc));\n});\nMDXCreateElement.displayName = 'MDXCreateElement';\n\nfunction createElement(type, props) {\n var args = arguments;\n var mdxType = props && props.mdxType;\n\n if (typeof type === 'string' || mdxType) {\n var argsLength = args.length;\n var createElementArgArray = new Array(argsLength);\n createElementArgArray[0] = MDXCreateElement;\n var newProps = {};\n\n for (var key in props) {\n if (hasOwnProperty.call(props, key)) {\n newProps[key] = props[key];\n }\n }\n\n newProps.originalType = type;\n newProps[TYPE_PROP_NAME] = typeof type === 'string' ? type : mdxType;\n createElementArgArray[1] = newProps;\n\n for (var i = 2; i < argsLength; i++) {\n createElementArgArray[i] = args[i];\n }\n\n return React.createElement.apply(null, createElementArgArray);\n }\n\n return React.createElement.apply(null, args);\n}\n\nexport { MDXContext, MDXProvider, createElement as mdx, useMDXComponents, withMDXComponents };","\"use strict\";\n\nexports.__esModule = true;\nexports.applyTrailingSlashOption = void 0; // TODO(v5): Remove legacy setting and default to \"always\"\n\nvar applyTrailingSlashOption = function applyTrailingSlashOption(input, option) {\n if (option === void 0) {\n option = \"legacy\";\n }\n\n var hasHtmlSuffix = input.endsWith(\".html\");\n var hasXmlSuffix = input.endsWith(\".xml\");\n var hasPdfSuffix = input.endsWith(\".pdf\");\n if (input === \"/\") return input;\n\n if (hasHtmlSuffix || hasXmlSuffix || hasPdfSuffix) {\n option = \"never\";\n }\n\n if (option === \"always\") {\n return input.endsWith(\"/\") ? input : input + \"/\";\n }\n\n if (option === \"never\") {\n return input.endsWith(\"/\") ? input.slice(0, -1) : input;\n }\n\n return input;\n};\n\nexports.applyTrailingSlashOption = applyTrailingSlashOption;","\"use strict\";\n\nmodule.exports = Object.assign;","\"use strict\";\n\nvar _slicedToArray = require(\"/home/runner/work/hisnameisjimmy.github.io/hisnameisjimmy.github.io/node_modules/@babel/runtime/helpers/slicedToArray.js\");\n\nfunction _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== \"undefined\" && o[Symbol.iterator] || o[\"@@iterator\"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === \"number\") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError(\"Invalid attempt to iterate non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.\"); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }\n\nfunction _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === \"string\") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === \"Object\" && o.constructor) n = o.constructor.name; if (n === \"Map\" || n === \"Set\") return Array.from(o); if (n === \"Arguments\" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }\n\nfunction _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }\n\nexports.__esModule = true;\nexports.wrapRootElement = void 0;\n\nvar _react = _interopRequireWildcard(require(\"react\"));\n\nvar _react2 = require(\"@builder.io/partytown/react\");\n\nvar _gatsbyScript = require(\"gatsby-script\");\n\nfunction _getRequireWildcardCache(nodeInterop) {\n if (typeof WeakMap !== \"function\") return null;\n var cacheBabelInterop = new WeakMap();\n var cacheNodeInterop = new WeakMap();\n return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) {\n return nodeInterop ? cacheNodeInterop : cacheBabelInterop;\n })(nodeInterop);\n}\n\nfunction _interopRequireWildcard(obj, nodeInterop) {\n if (!nodeInterop && obj && obj.__esModule) {\n return obj;\n }\n\n if (obj === null || typeof obj !== \"object\" && typeof obj !== \"function\") {\n return {\n default: obj\n };\n }\n\n var cache = _getRequireWildcardCache(nodeInterop);\n\n if (cache && cache.has(obj)) {\n return cache.get(obj);\n }\n\n var newObj = {};\n var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;\n\n for (var key in obj) {\n if (key !== \"default\" && Object.prototype.hasOwnProperty.call(obj, key)) {\n var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;\n\n if (desc && (desc.get || desc.set)) {\n Object.defineProperty(newObj, key, desc);\n } else {\n newObj[key] = obj[key];\n }\n }\n }\n\n newObj.default = obj;\n\n if (cache) {\n cache.set(obj, newObj);\n }\n\n return newObj;\n}\n\nfunction PartytownProvider(_ref) {\n var children = _ref.children;\n\n var _ref2 = (0, _react.useState)({\n collectedForwards: new Set(),\n collectedAnyScript: false\n }),\n _ref3 = _slicedToArray(_ref2, 2),\n _ref3$ = _ref3[0],\n collectedForwards = _ref3$.collectedForwards,\n collectedAnyScript = _ref3$.collectedAnyScript,\n setState = _ref3[1];\n\n return /*#__PURE__*/_react.default.createElement(_gatsbyScript.PartytownContext.Provider, {\n value: {\n collectScript: function collectScript(newScript) {\n var stateShouldChange = false;\n var potentialNewState = {\n collectedAnyScript: collectedAnyScript,\n collectedForwards: collectedForwards\n };\n\n if (!collectedAnyScript) {\n potentialNewState.collectedAnyScript = true;\n stateShouldChange = true;\n }\n\n if (newScript !== null && newScript !== void 0 && newScript.forward) {\n if (Array.isArray(newScript.forward)) {\n var _iterator = _createForOfIteratorHelper(newScript.forward),\n _step;\n\n try {\n for (_iterator.s(); !(_step = _iterator.n()).done;) {\n var singleForward = _step.value;\n\n if (!potentialNewState.collectedForwards.has(singleForward)) {\n potentialNewState.collectedForwards.add(singleForward);\n stateShouldChange = true;\n }\n }\n } catch (err) {\n _iterator.e(err);\n } finally {\n _iterator.f();\n }\n } else {\n if (process.env.NODE_ENV === \"development\") {\n console.log(\"unexpected shape of forward\", newScript);\n }\n }\n }\n\n if (stateShouldChange) {\n setState(potentialNewState);\n }\n }\n }\n }, children, collectedAnyScript && /*#__PURE__*/_react.default.createElement(_react2.Partytown, {\n key: \"partytown\",\n forward: Array.from(collectedForwards)\n }));\n}\n\nvar wrapRootElement = function wrapRootElement(_ref4) {\n var element = _ref4.element;\n return /*#__PURE__*/_react.default.createElement(PartytownProvider, null, element);\n};\n\nexports.wrapRootElement = wrapRootElement;","\"use strict\";\n\nvar _interopRequireDefault = require(\"@babel/runtime/helpers/interopRequireDefault\");\n\nexports.__esModule = true;\nexports.navigate = exports.default = void 0;\nexports.withAssetPrefix = withAssetPrefix;\nexports.withPrefix = withPrefix;\n\nvar _objectWithoutPropertiesLoose2 = _interopRequireDefault(require(\"@babel/runtime/helpers/objectWithoutPropertiesLoose\"));\n\nvar _assertThisInitialized2 = _interopRequireDefault(require(\"@babel/runtime/helpers/assertThisInitialized\"));\n\nvar _inheritsLoose2 = _interopRequireDefault(require(\"@babel/runtime/helpers/inheritsLoose\"));\n\nvar _extends2 = _interopRequireDefault(require(\"@babel/runtime/helpers/extends\"));\n\nvar _propTypes = _interopRequireDefault(require(\"prop-types\"));\n\nvar _react = _interopRequireDefault(require(\"react\"));\n\nvar _reachRouter = require(\"@gatsbyjs/reach-router\");\n\nvar _parsePath = require(\"./parse-path\");\n\nexports.parsePath = _parsePath.parsePath;\n\nvar _isLocalLink = require(\"./is-local-link\");\n\nvar _rewriteLinkPath = require(\"./rewrite-link-path\");\n\nvar _excluded = [\"to\", \"getProps\", \"onClick\", \"onMouseEnter\", \"activeClassName\", \"activeStyle\", \"innerRef\", \"partiallyActive\", \"state\", \"replace\", \"_location\"];\n\nfunction withPrefix(path, prefix) {\n var _ref, _prefix;\n\n if (prefix === void 0) {\n prefix = getGlobalBasePrefix();\n }\n\n if (!(0, _isLocalLink.isLocalLink)(path)) {\n return path;\n }\n\n if (path.startsWith(\"./\") || path.startsWith(\"../\")) {\n return path;\n }\n\n var base = (_ref = (_prefix = prefix) !== null && _prefix !== void 0 ? _prefix : getGlobalPathPrefix()) !== null && _ref !== void 0 ? _ref : \"/\";\n return \"\" + (base !== null && base !== void 0 && base.endsWith(\"/\") ? base.slice(0, -1) : base) + (path.startsWith(\"/\") ? path : \"/\" + path);\n} // These global values are wrapped in typeof clauses to ensure the values exist.\n// This is especially problematic in unit testing of this component.\n\n\nvar getGlobalPathPrefix = function getGlobalPathPrefix() {\n return process.env.NODE_ENV !== \"production\" ? typeof __PATH_PREFIX__ !== \"undefined\" ? __PATH_PREFIX__ : undefined : __PATH_PREFIX__;\n};\n\nvar getGlobalBasePrefix = function getGlobalBasePrefix() {\n return process.env.NODE_ENV !== \"production\" ? typeof __BASE_PATH__ !== \"undefined\" ? __BASE_PATH__ : undefined : __BASE_PATH__;\n};\n\nfunction withAssetPrefix(path) {\n return withPrefix(path, getGlobalPathPrefix());\n}\n\nvar NavLinkPropTypes = {\n activeClassName: _propTypes.default.string,\n activeStyle: _propTypes.default.object,\n partiallyActive: _propTypes.default.bool\n}; // Set up IntersectionObserver\n\nvar createIntersectionObserver = function createIntersectionObserver(el, cb) {\n var io = new window.IntersectionObserver(function (entries) {\n entries.forEach(function (entry) {\n if (el === entry.target) {\n // Check if element is within viewport, remove listener, destroy observer, and run link callback.\n // MSEdge doesn't currently support isIntersecting, so also test for an intersectionRatio > 0\n cb(entry.isIntersecting || entry.intersectionRatio > 0);\n }\n });\n }); // Add element to the observer\n\n io.observe(el);\n return {\n instance: io,\n el: el\n };\n};\n\nfunction GatsbyLinkLocationWrapper(props) {\n return /*#__PURE__*/_react.default.createElement(_reachRouter.Location, null, function (_ref2) {\n var location = _ref2.location;\n return /*#__PURE__*/_react.default.createElement(GatsbyLink, (0, _extends2.default)({}, props, {\n _location: location\n }));\n });\n}\n\nvar GatsbyLink = /*#__PURE__*/function (_React$Component) {\n (0, _inheritsLoose2.default)(GatsbyLink, _React$Component);\n\n function GatsbyLink(props) {\n var _this;\n\n _this = _React$Component.call(this, props) || this; // Default to no support for IntersectionObserver\n\n _this.defaultGetProps = function (_ref3) {\n var isPartiallyCurrent = _ref3.isPartiallyCurrent,\n isCurrent = _ref3.isCurrent;\n\n if (_this.props.partiallyActive ? isPartiallyCurrent : isCurrent) {\n return {\n className: [_this.props.className, _this.props.activeClassName].filter(Boolean).join(\" \"),\n style: (0, _extends2.default)({}, _this.props.style, _this.props.activeStyle)\n };\n }\n\n return null;\n };\n\n var IOSupported = false;\n\n if (typeof window !== \"undefined\" && window.IntersectionObserver) {\n IOSupported = true;\n }\n\n _this.state = {\n IOSupported: IOSupported\n };\n _this.abortPrefetch = null;\n _this.handleRef = _this.handleRef.bind((0, _assertThisInitialized2.default)(_this));\n return _this;\n }\n\n var _proto = GatsbyLink.prototype;\n\n _proto._prefetch = function _prefetch() {\n var currentPath = window.location.pathname + window.location.search; // reach router should have the correct state\n\n if (this.props._location && this.props._location.pathname) {\n currentPath = this.props._location.pathname + this.props._location.search;\n }\n\n var rewrittenPath = (0, _rewriteLinkPath.rewriteLinkPath)(this.props.to, currentPath);\n var parsed = (0, _parsePath.parsePath)(rewrittenPath);\n var newPathName = parsed.pathname + parsed.search; // Prefetch is used to speed up next navigations. When you use it on the current navigation,\n // there could be a race-condition where Chrome uses the stale data instead of waiting for the network to complete\n\n if (currentPath !== newPathName) {\n return ___loader.enqueue(newPathName);\n }\n\n return undefined;\n };\n\n _proto.componentWillUnmount = function componentWillUnmount() {\n if (!this.io) {\n return;\n }\n\n var _this$io = this.io,\n instance = _this$io.instance,\n el = _this$io.el;\n\n if (this.abortPrefetch) {\n this.abortPrefetch.abort();\n }\n\n instance.unobserve(el);\n instance.disconnect();\n };\n\n _proto.handleRef = function handleRef(ref) {\n var _this2 = this;\n\n if (this.props.innerRef && Object.prototype.hasOwnProperty.call(this.props.innerRef, \"current\")) {\n this.props.innerRef.current = ref;\n } else if (this.props.innerRef) {\n this.props.innerRef(ref);\n }\n\n if (this.state.IOSupported && ref) {\n // If IO supported and element reference found, setup Observer functionality\n this.io = createIntersectionObserver(ref, function (inViewPort) {\n if (inViewPort) {\n _this2.abortPrefetch = _this2._prefetch();\n } else {\n if (_this2.abortPrefetch) {\n _this2.abortPrefetch.abort();\n }\n }\n });\n }\n };\n\n _proto.render = function render() {\n var _this3 = this;\n\n var _this$props = this.props,\n to = _this$props.to,\n _this$props$getProps = _this$props.getProps,\n getProps = _this$props$getProps === void 0 ? this.defaultGetProps : _this$props$getProps,\n _onClick = _this$props.onClick,\n _onMouseEnter = _this$props.onMouseEnter,\n $activeClassName = _this$props.activeClassName,\n $activeStyle = _this$props.activeStyle,\n $innerRef = _this$props.innerRef,\n partiallyActive = _this$props.partiallyActive,\n state = _this$props.state,\n replace = _this$props.replace,\n _location = _this$props._location,\n rest = (0, _objectWithoutPropertiesLoose2.default)(_this$props, _excluded);\n\n if (process.env.NODE_ENV !== \"production\" && !(0, _isLocalLink.isLocalLink)(to)) {\n console.warn(\"External link \" + to + \" was detected in a Link component. Use the Link component only for internal links. See: https://gatsby.dev/internal-links\");\n }\n\n var prefixedTo = (0, _rewriteLinkPath.rewriteLinkPath)(to, _location.pathname);\n\n if (!(0, _isLocalLink.isLocalLink)(prefixedTo)) {\n return /*#__PURE__*/_react.default.createElement(\"a\", (0, _extends2.default)({\n href: prefixedTo\n }, rest));\n }\n\n return /*#__PURE__*/_react.default.createElement(_reachRouter.Link, (0, _extends2.default)({\n to: prefixedTo,\n state: state,\n getProps: getProps,\n innerRef: this.handleRef,\n onMouseEnter: function onMouseEnter(e) {\n if (_onMouseEnter) {\n _onMouseEnter(e);\n }\n\n var parsed = (0, _parsePath.parsePath)(prefixedTo);\n\n ___loader.hovering(parsed.pathname + parsed.search);\n },\n onClick: function onClick(e) {\n if (_onClick) {\n _onClick(e);\n }\n\n if (e.button === 0 && // ignore right clicks\n !_this3.props.target && // let browser handle \"target=_blank\"\n !e.defaultPrevented && // onClick prevented default\n !e.metaKey && // ignore clicks with modifier keys...\n !e.altKey && !e.ctrlKey && !e.shiftKey) {\n e.preventDefault();\n var shouldReplace = replace;\n\n var isCurrent = encodeURI(prefixedTo) === _location.pathname;\n\n if (typeof replace !== \"boolean\" && isCurrent) {\n shouldReplace = true;\n } // Make sure the necessary scripts and data are\n // loaded before continuing.\n\n\n window.___navigate(prefixedTo, {\n state: state,\n replace: shouldReplace\n });\n }\n\n return true;\n }\n }, rest));\n };\n\n return GatsbyLink;\n}(_react.default.Component);\n\nGatsbyLink.propTypes = (0, _extends2.default)({}, NavLinkPropTypes, {\n onClick: _propTypes.default.func,\n to: _propTypes.default.string.isRequired,\n replace: _propTypes.default.bool,\n state: _propTypes.default.object\n});\n\nvar _default = /*#__PURE__*/_react.default.forwardRef(function (props, ref) {\n return /*#__PURE__*/_react.default.createElement(GatsbyLinkLocationWrapper, (0, _extends2.default)({\n innerRef: ref\n }, props));\n});\n\nexports.default = _default;\n\nvar navigate = function navigate(to, options) {\n window.___navigate((0, _rewriteLinkPath.rewriteLinkPath)(to, window.location.pathname), options);\n};\n\nexports.navigate = navigate;","\"use strict\";\n\nexports.__esModule = true;\nexports.isLocalLink = void 0;\n// Copied from https://github.com/sindresorhus/is-absolute-url/blob/3ab19cc2e599a03ea691bcb8a4c09fa3ebb5da4f/index.js\nvar ABSOLUTE_URL_REGEX = /^[a-zA-Z][a-zA-Z\\d+\\-.]*?:/;\n\nvar isAbsolute = function isAbsolute(path) {\n return ABSOLUTE_URL_REGEX.test(path);\n};\n\nvar isLocalLink = function isLocalLink(path) {\n if (typeof path !== \"string\") {\n return undefined; // TODO(v5): Re-Add TypeError\n // throw new TypeError(`Expected a \\`string\\`, got \\`${typeof path}\\``)\n }\n\n return !isAbsolute(path);\n};\n\nexports.isLocalLink = isLocalLink;","\"use strict\";\n\nexports.__esModule = true;\nexports.parsePath = parsePath;\n\nfunction parsePath(path) {\n var pathname = path || \"/\";\n var search = \"\";\n var hash = \"\";\n var hashIndex = pathname.indexOf(\"#\");\n\n if (hashIndex !== -1) {\n hash = pathname.slice(hashIndex);\n pathname = pathname.slice(0, hashIndex);\n }\n\n var searchIndex = pathname.indexOf(\"?\");\n\n if (searchIndex !== -1) {\n search = pathname.slice(searchIndex);\n pathname = pathname.slice(0, searchIndex);\n }\n\n return {\n pathname: pathname,\n search: search === \"?\" ? \"\" : search,\n hash: hash === \"#\" ? \"\" : hash\n };\n}","\"use strict\";\n\nexports.__esModule = true;\nexports.rewriteLinkPath = void 0;\n\nvar _utils = require(\"@gatsbyjs/reach-router/lib/utils\");\n\nvar _applyTrailingSlashOption = require(\"gatsby-page-utils/apply-trailing-slash-option\");\n\nvar _parsePath2 = require(\"./parse-path\");\n\nvar _isLocalLink = require(\"./is-local-link\");\n\nvar _ = require(\".\");\n\n// Specific import to treeshake Node.js stuff\nvar isAbsolutePath = function isAbsolutePath(path) {\n return path === null || path === void 0 ? void 0 : path.startsWith(\"/\");\n};\n\nvar getGlobalTrailingSlash = function getGlobalTrailingSlash() {\n return typeof __TRAILING_SLASH__ !== \"undefined\" ? __TRAILING_SLASH__ : undefined;\n};\n\nfunction absolutify(path, current) {\n // If it's already absolute, return as-is\n if (isAbsolutePath(path)) {\n return path;\n }\n\n var option = getGlobalTrailingSlash();\n var absolutePath = (0, _utils.resolve)(path, current);\n\n if (option === \"always\" || option === \"never\") {\n return (0, _applyTrailingSlashOption.applyTrailingSlashOption)(absolutePath, option);\n }\n\n return absolutePath;\n}\n\nvar rewriteLinkPath = function rewriteLinkPath(path, relativeTo) {\n if (typeof path === \"number\") {\n return path;\n }\n\n if (!(0, _isLocalLink.isLocalLink)(path)) {\n return path;\n }\n\n var _parsePath = (0, _parsePath2.parsePath)(path),\n pathname = _parsePath.pathname,\n search = _parsePath.search,\n hash = _parsePath.hash;\n\n var option = getGlobalTrailingSlash();\n var adjustedPath = path;\n\n if (option === \"always\" || option === \"never\") {\n var output = (0, _applyTrailingSlashOption.applyTrailingSlashOption)(pathname, option);\n adjustedPath = \"\" + output + search + hash;\n }\n\n return isAbsolutePath(adjustedPath) ? (0, _.withPrefix)(adjustedPath) : absolutify(adjustedPath, relativeTo);\n};\n\nexports.rewriteLinkPath = rewriteLinkPath;","\"use strict\";\n\nexports.__esModule = true;\nexports.useScrollRestoration = exports.ScrollContext = void 0;\n\nvar _scrollHandler = require(\"./scroll-handler\");\n\nexports.ScrollContext = _scrollHandler.ScrollHandler;\n\nvar _useScrollRestoration = require(\"./use-scroll-restoration\");\n\nexports.useScrollRestoration = _useScrollRestoration.useScrollRestoration;","\"use strict\";\n\nvar _interopRequireDefault = require(\"@babel/runtime/helpers/interopRequireDefault\");\n\nexports.__esModule = true;\nexports.ScrollHandler = exports.ScrollContext = void 0;\n\nvar _assertThisInitialized2 = _interopRequireDefault(require(\"@babel/runtime/helpers/assertThisInitialized\"));\n\nvar _inheritsLoose2 = _interopRequireDefault(require(\"@babel/runtime/helpers/inheritsLoose\"));\n\nvar React = _interopRequireWildcard(require(\"react\"));\n\nvar _propTypes = _interopRequireDefault(require(\"prop-types\"));\n\nvar _sessionStorage = require(\"./session-storage\");\n\nfunction _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== \"function\") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }\n\nfunction _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== \"object\" && typeof obj !== \"function\") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== \"default\" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }\n\nvar ScrollContext = /*#__PURE__*/React.createContext(new _sessionStorage.SessionStorage());\nexports.ScrollContext = ScrollContext;\nScrollContext.displayName = \"GatsbyScrollContext\";\n\nvar ScrollHandler = /*#__PURE__*/function (_React$Component) {\n (0, _inheritsLoose2.default)(ScrollHandler, _React$Component);\n\n function ScrollHandler() {\n var _this;\n\n for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {\n args[_key] = arguments[_key];\n }\n\n _this = _React$Component.call.apply(_React$Component, [this].concat(args)) || this;\n _this._stateStorage = new _sessionStorage.SessionStorage();\n _this._isTicking = false;\n _this._latestKnownScrollY = 0;\n\n _this.scrollListener = function () {\n _this._latestKnownScrollY = window.scrollY;\n\n if (!_this._isTicking) {\n _this._isTicking = true;\n requestAnimationFrame(_this._saveScroll.bind((0, _assertThisInitialized2.default)(_this)));\n }\n };\n\n _this.windowScroll = function (position, prevProps) {\n if (_this.shouldUpdateScroll(prevProps, _this.props)) {\n window.scrollTo(0, position);\n }\n };\n\n _this.scrollToHash = function (hash, prevProps) {\n var node = document.getElementById(hash.substring(1));\n\n if (node && _this.shouldUpdateScroll(prevProps, _this.props)) {\n node.scrollIntoView();\n }\n };\n\n _this.shouldUpdateScroll = function (prevRouterProps, routerProps) {\n var shouldUpdateScroll = _this.props.shouldUpdateScroll;\n\n if (!shouldUpdateScroll) {\n return true;\n } // Hack to allow accessing this._stateStorage.\n\n\n return shouldUpdateScroll.call((0, _assertThisInitialized2.default)(_this), prevRouterProps, routerProps);\n };\n\n return _this;\n }\n\n var _proto = ScrollHandler.prototype;\n\n _proto._saveScroll = function _saveScroll() {\n var key = this.props.location.key || null;\n\n if (key) {\n this._stateStorage.save(this.props.location, key, this._latestKnownScrollY);\n }\n\n this._isTicking = false;\n };\n\n _proto.componentDidMount = function componentDidMount() {\n window.addEventListener(\"scroll\", this.scrollListener);\n var scrollPosition;\n var _this$props$location = this.props.location,\n key = _this$props$location.key,\n hash = _this$props$location.hash;\n\n if (key) {\n scrollPosition = this._stateStorage.read(this.props.location, key);\n }\n\n if (scrollPosition) {\n this.windowScroll(scrollPosition, undefined);\n } else if (hash) {\n this.scrollToHash(decodeURI(hash), undefined);\n }\n };\n\n _proto.componentWillUnmount = function componentWillUnmount() {\n window.removeEventListener(\"scroll\", this.scrollListener);\n };\n\n _proto.componentDidUpdate = function componentDidUpdate(prevProps) {\n var _this$props$location2 = this.props.location,\n hash = _this$props$location2.hash,\n key = _this$props$location2.key;\n var scrollPosition;\n\n if (key) {\n scrollPosition = this._stateStorage.read(this.props.location, key);\n }\n /** There are two pieces of state: the browser url and\n * history state which keeps track of scroll position\n * Native behaviour prescribes that we ought to restore scroll position\n * when a user navigates back in their browser (this is the `POP` action)\n * Currently, reach router has a bug that prevents this at https://github.com/reach/router/issues/228\n * So we _always_ stick to the url as a source of truth — if the url\n * contains a hash, we scroll to it\n */\n\n\n if (hash) {\n this.scrollToHash(decodeURI(hash), prevProps);\n } else {\n this.windowScroll(scrollPosition, prevProps);\n }\n };\n\n _proto.render = function render() {\n return /*#__PURE__*/React.createElement(ScrollContext.Provider, {\n value: this._stateStorage\n }, this.props.children);\n };\n\n return ScrollHandler;\n}(React.Component);\n\nexports.ScrollHandler = ScrollHandler;\nScrollHandler.propTypes = {\n shouldUpdateScroll: _propTypes.default.func,\n children: _propTypes.default.element.isRequired,\n location: _propTypes.default.object.isRequired\n};","\"use strict\";\n\nexports.__esModule = true;\nexports.SessionStorage = void 0;\nvar STATE_KEY_PREFIX = \"@@scroll|\";\nvar GATSBY_ROUTER_SCROLL_STATE = \"___GATSBY_REACT_ROUTER_SCROLL\";\n\nvar SessionStorage = /*#__PURE__*/function () {\n function SessionStorage() {}\n\n var _proto = SessionStorage.prototype;\n\n _proto.read = function read(location, key) {\n var stateKey = this.getStateKey(location, key);\n\n try {\n var value = window.sessionStorage.getItem(stateKey);\n return value ? JSON.parse(value) : 0;\n } catch (e) {\n if (process.env.NODE_ENV !== \"production\") {\n console.warn(\"[gatsby-react-router-scroll] Unable to access sessionStorage; sessionStorage is not available.\");\n }\n\n if (window && window[GATSBY_ROUTER_SCROLL_STATE] && window[GATSBY_ROUTER_SCROLL_STATE][stateKey]) {\n return window[GATSBY_ROUTER_SCROLL_STATE][stateKey];\n }\n\n return 0;\n }\n };\n\n _proto.save = function save(location, key, value) {\n var stateKey = this.getStateKey(location, key);\n var storedValue = JSON.stringify(value);\n\n try {\n window.sessionStorage.setItem(stateKey, storedValue);\n } catch (e) {\n if (window && window[GATSBY_ROUTER_SCROLL_STATE]) {\n window[GATSBY_ROUTER_SCROLL_STATE][stateKey] = JSON.parse(storedValue);\n } else {\n window[GATSBY_ROUTER_SCROLL_STATE] = {};\n window[GATSBY_ROUTER_SCROLL_STATE][stateKey] = JSON.parse(storedValue);\n }\n\n if (process.env.NODE_ENV !== \"production\") {\n console.warn(\"[gatsby-react-router-scroll] Unable to save state in sessionStorage; sessionStorage is not available.\");\n }\n }\n };\n\n _proto.getStateKey = function getStateKey(location, key) {\n var stateKeyBase = \"\" + STATE_KEY_PREFIX + location.pathname;\n return key === null || typeof key === \"undefined\" ? stateKeyBase : stateKeyBase + \"|\" + key;\n };\n\n return SessionStorage;\n}();\n\nexports.SessionStorage = SessionStorage;","\"use strict\";\n\nexports.__esModule = true;\nexports.useScrollRestoration = useScrollRestoration;\n\nvar _scrollHandler = require(\"./scroll-handler\");\n\nvar _react = require(\"react\");\n\nvar _reachRouter = require(\"@gatsbyjs/reach-router\");\n\nfunction useScrollRestoration(identifier) {\n var location = (0, _reachRouter.useLocation)();\n var state = (0, _react.useContext)(_scrollHandler.ScrollContext);\n var ref = (0, _react.useRef)(null);\n (0, _react.useLayoutEffect)(function () {\n if (ref.current) {\n var position = state.read(location, identifier);\n ref.current.scrollTo(0, position || 0);\n }\n }, [location.key]);\n return {\n ref: ref,\n onScroll: function onScroll() {\n if (ref.current) {\n state.save(location, identifier, ref.current.scrollTop);\n }\n }\n };\n}","import { createContext } from \"react\";\nconst PartytownContext = /*#__PURE__*/createContext({});\nexport { PartytownContext };","// https://developer.chrome.com/blog/using-requestidlecallback/#checking-for-requestidlecallback\n// https://github.com/vercel/next.js/blob/canary/packages/next/client/request-idle-callback.ts\nexport const requestIdleCallback = typeof self !== `undefined` && self.requestIdleCallback && self.requestIdleCallback.bind(window) || function (cb) {\n const start = Date.now();\n return setTimeout(function () {\n cb({\n didTimeout: false,\n timeRemaining: function () {\n return Math.max(0, 50 - (Date.now() - start));\n }\n });\n }, 1);\n};","import _extends from \"@babel/runtime/helpers/extends\";\nimport React, { useEffect, useContext } from \"react\";\nimport { PartytownContext } from \"./partytown-context\";\nimport { requestIdleCallback } from \"./request-idle-callback-shim\";\nexport let ScriptStrategy; // eslint-disable-next-line @typescript-eslint/naming-convention\n\n(function (ScriptStrategy) {\n ScriptStrategy[\"postHydrate\"] = \"post-hydrate\";\n ScriptStrategy[\"idle\"] = \"idle\";\n ScriptStrategy[\"offMainThread\"] = \"off-main-thread\";\n})(ScriptStrategy || (ScriptStrategy = {}));\n\nconst handledProps = new Set([`src`, `strategy`, `dangerouslySetInnerHTML`, `children`, `onLoad`, `onError`]);\nexport const scriptCache = new Set();\nexport const scriptCallbackCache = new Map();\nexport function Script(props) {\n const {\n id,\n src,\n strategy = ScriptStrategy.postHydrate\n } = props || {};\n const {\n collectScript\n } = useContext(PartytownContext);\n useEffect(() => {\n let details;\n\n switch (strategy) {\n case ScriptStrategy.postHydrate:\n details = injectScript(props);\n break;\n\n case ScriptStrategy.idle:\n requestIdleCallback(() => {\n details = injectScript(props);\n });\n break;\n\n case ScriptStrategy.offMainThread:\n if (collectScript) {\n const attributes = resolveAttributes(props);\n collectScript(attributes);\n }\n\n break;\n }\n\n return () => {\n const {\n script,\n loadCallback,\n errorCallback\n } = details || {};\n\n if (loadCallback) {\n script === null || script === void 0 ? void 0 : script.removeEventListener(`load`, loadCallback);\n }\n\n if (errorCallback) {\n script === null || script === void 0 ? void 0 : script.removeEventListener(`error`, errorCallback);\n }\n\n script === null || script === void 0 ? void 0 : script.remove();\n };\n }, []);\n\n if (strategy === ScriptStrategy.offMainThread) {\n const inlineScript = resolveInlineScript(props);\n const attributes = resolveAttributes(props);\n\n if (typeof window === `undefined`) {\n if (collectScript) {\n collectScript(attributes);\n } else {\n console.warn(`Unable to collect off-main-thread script '${id || src || `no-id-or-src`}' for configuration with Partytown.\\nGatsby script components must be used either as a child of your page, in wrapPageElement, or wrapRootElement.\\nSee https://gatsby.dev/gatsby-script for more information.`);\n }\n }\n\n if (inlineScript) {\n return /*#__PURE__*/React.createElement(\"script\", _extends({\n type: \"text/partytown\",\n \"data-strategy\": strategy,\n crossOrigin: \"anonymous\"\n }, attributes, {\n dangerouslySetInnerHTML: {\n __html: resolveInlineScript(props)\n }\n }));\n }\n\n return /*#__PURE__*/React.createElement(\"script\", _extends({\n type: \"text/partytown\",\n src: proxyPartytownUrl(src),\n \"data-strategy\": strategy,\n crossOrigin: \"anonymous\"\n }, attributes));\n }\n\n return null;\n}\n\nfunction injectScript(props) {\n const {\n id,\n src,\n strategy = ScriptStrategy.postHydrate,\n onLoad,\n onError\n } = props || {};\n const scriptKey = id || src;\n const callbackNames = [`load`, `error`];\n const currentCallbacks = {\n load: onLoad,\n error: onError\n };\n\n if (scriptKey) {\n /**\n * If a duplicate script is already loaded/errored, we replay load/error callbacks with the original event.\n * If it's not yet loaded/errored, keep track of callbacks so we can call load/error callbacks for each when the event occurs.\n */\n for (const name of callbackNames) {\n if (currentCallbacks !== null && currentCallbacks !== void 0 && currentCallbacks[name]) {\n var _cachedCallbacks$name;\n\n const cachedCallbacks = scriptCallbackCache.get(scriptKey) || {};\n const {\n callbacks = []\n } = (cachedCallbacks === null || cachedCallbacks === void 0 ? void 0 : cachedCallbacks[name]) || {};\n callbacks.push(currentCallbacks === null || currentCallbacks === void 0 ? void 0 : currentCallbacks[name]);\n\n if (cachedCallbacks !== null && cachedCallbacks !== void 0 && (_cachedCallbacks$name = cachedCallbacks[name]) !== null && _cachedCallbacks$name !== void 0 && _cachedCallbacks$name.event) {\n var _currentCallbacks$nam, _cachedCallbacks$name2;\n\n currentCallbacks === null || currentCallbacks === void 0 ? void 0 : (_currentCallbacks$nam = currentCallbacks[name]) === null || _currentCallbacks$nam === void 0 ? void 0 : _currentCallbacks$nam.call(currentCallbacks, cachedCallbacks === null || cachedCallbacks === void 0 ? void 0 : (_cachedCallbacks$name2 = cachedCallbacks[name]) === null || _cachedCallbacks$name2 === void 0 ? void 0 : _cachedCallbacks$name2.event);\n } else {\n scriptCallbackCache.set(scriptKey, _extends({}, cachedCallbacks, {\n [name]: {\n callbacks\n }\n }));\n }\n }\n } // Avoid injecting duplicate scripts into the DOM\n\n\n if (scriptCache.has(scriptKey)) {\n return null;\n }\n }\n\n const inlineScript = resolveInlineScript(props);\n const attributes = resolveAttributes(props);\n const script = document.createElement(`script`);\n\n if (id) {\n script.id = id;\n }\n\n script.dataset.strategy = strategy;\n\n for (const [key, value] of Object.entries(attributes)) {\n script.setAttribute(key, value);\n }\n\n if (inlineScript) {\n script.textContent = inlineScript;\n }\n\n if (src) {\n script.src = src;\n }\n\n const wrappedCallbacks = {};\n\n if (scriptKey) {\n // Add listeners on injected scripts so events are cached for use in de-duplicated script callbacks\n for (const name of callbackNames) {\n const wrappedEventCallback = event => onEventCallback(event, scriptKey, name);\n\n script.addEventListener(name, wrappedEventCallback);\n wrappedCallbacks[`${name}Callback`] = wrappedEventCallback;\n }\n\n scriptCache.add(scriptKey);\n }\n\n document.body.appendChild(script);\n return {\n script,\n loadCallback: wrappedCallbacks.loadCallback,\n errorCallback: wrappedCallbacks.errorCallback\n };\n}\n\nfunction resolveInlineScript(props) {\n const {\n dangerouslySetInnerHTML,\n children = ``\n } = props || {};\n const {\n __html: dangerousHTML = ``\n } = dangerouslySetInnerHTML || {};\n return dangerousHTML || children;\n}\n\nfunction resolveAttributes(props) {\n const attributes = {};\n\n for (const [key, value] of Object.entries(props)) {\n if (handledProps.has(key)) {\n continue;\n }\n\n attributes[key] = value;\n }\n\n return attributes;\n}\n\nfunction proxyPartytownUrl(url) {\n if (!url) {\n return undefined;\n }\n\n return `/__third-party-proxy?url=${encodeURIComponent(url)}`;\n}\n\nfunction onEventCallback(event, scriptKey, eventName) {\n const cachedCallbacks = scriptCallbackCache.get(scriptKey) || {};\n\n for (const callback of (cachedCallbacks === null || cachedCallbacks === void 0 ? void 0 : (_cachedCallbacks$even = cachedCallbacks[eventName]) === null || _cachedCallbacks$even === void 0 ? void 0 : _cachedCallbacks$even.callbacks) || []) {\n var _cachedCallbacks$even;\n\n callback(event);\n }\n\n scriptCallbackCache.set(scriptKey, {\n [eventName]: {\n event\n }\n });\n}","exports.components = {\n \"component---src-pages-404-js\": () => import(\"./../../../src/pages/404.js\" /* webpackChunkName: \"component---src-pages-404-js\" */),\n \"component---src-pages-about-js\": () => import(\"./../../../src/pages/about.js\" /* webpackChunkName: \"component---src-pages-about-js\" */),\n \"component---src-pages-donate-js\": () => import(\"./../../../src/pages/donate.js\" /* webpackChunkName: \"component---src-pages-donate-js\" */),\n \"component---src-pages-index-js\": () => import(\"./../../../src/pages/index.js\" /* webpackChunkName: \"component---src-pages-index-js\" */),\n \"component---src-pages-mdx-frontmatter-post-type-mdx-slug-js\": () => import(\"./../../../src/pages/{mdx.frontmatter__post_type}/{mdx.slug}.js\" /* webpackChunkName: \"component---src-pages-mdx-frontmatter-post-type-mdx-slug-js\" */),\n \"component---src-pages-portfolio-js\": () => import(\"./../../../src/pages/portfolio.js\" /* webpackChunkName: \"component---src-pages-portfolio-js\" */)\n}\n\n","module.exports = [{\n plugin: require('../node_modules/gatsby-plugin-twitter/gatsby-browser.js'),\n options: {\"plugins\":[]},\n },{\n plugin: require('../node_modules/gatsby-plugin-google-analytics/gatsby-browser.js'),\n options: {\"plugins\":[],\"trackingId\":\"UA-6279919-1\",\"head\":false,\"anonymize\":false,\"respectDNT\":false,\"exclude\":[],\"pageTransitionDelay\":0,\"enableWebVitalsTracking\":false},\n },{\n plugin: require('../node_modules/gatsby-plugin-manifest/gatsby-browser.js'),\n options: {\"plugins\":[],\"icon\":\"src/images/icon.png\",\"legacy\":true,\"theme_color_in_head\":true,\"cache_busting_mode\":\"query\",\"crossOrigin\":\"anonymous\",\"include_favicon\":true,\"cacheDigest\":\"9db651cbbdbb9d515341c1e155b24677\"},\n },{\n plugin: require('../node_modules/gatsby-remark-images/gatsby-browser.js'),\n options: {\"plugins\":[],\"maxWidth\":650,\"linkImagesToOriginal\":true,\"showCaptions\":false,\"markdownCaptions\":false,\"backgroundColor\":\"white\",\"quality\":50,\"withWebp\":false,\"withAvif\":false,\"tracedSVG\":false,\"loading\":\"lazy\",\"decoding\":\"async\",\"disableBgImageOnAlpha\":false,\"disableBgImage\":false},\n },{\n plugin: require('../node_modules/gatsby-remark-images-medium-zoom/gatsby-browser.js'),\n options: {\"plugins\":[]},\n },{\n plugin: require('../node_modules/gatsby-plugin-mdx/gatsby-browser.js'),\n options: {\"plugins\":[{\"resolve\":\"/home/runner/work/hisnameisjimmy.github.io/hisnameisjimmy.github.io/node_modules/gatsby-remark-images\",\"id\":\"c4a21c75-9c9b-598e-add9-681528ed1b49\",\"name\":\"gatsby-remark-images\",\"version\":\"6.16.0\",\"pluginOptions\":{\"plugins\":[],\"maxWidth\":650,\"linkImagesToOriginal\":true,\"showCaptions\":false,\"markdownCaptions\":false,\"backgroundColor\":\"white\",\"quality\":50,\"withWebp\":false,\"withAvif\":false,\"tracedSVG\":false,\"loading\":\"lazy\",\"decoding\":\"async\",\"disableBgImageOnAlpha\":false,\"disableBgImage\":false},\"nodeAPIs\":[\"pluginOptionsSchema\"],\"browserAPIs\":[\"onRouteUpdate\"],\"ssrAPIs\":[]},{\"resolve\":\"/home/runner/work/hisnameisjimmy.github.io/hisnameisjimmy.github.io/node_modules/gatsby-remark-images-medium-zoom\",\"id\":\"9a661529-a37d-52cd-b9a8-f94d932ba2e7\",\"name\":\"gatsby-remark-images-medium-zoom\",\"version\":\"1.7.0\",\"pluginOptions\":{\"plugins\":[]},\"nodeAPIs\":[],\"browserAPIs\":[\"onRouteUpdate\"],\"ssrAPIs\":[]}],\"gatsbyRemarkPlugins\":[{\"resolve\":\"/home/runner/work/hisnameisjimmy.github.io/hisnameisjimmy.github.io/node_modules/gatsby-remark-images\",\"id\":\"0a918d20-5ad0-5db2-89e6-d292a6d967f4\",\"name\":\"gatsby-remark-images\",\"version\":\"6.16.0\",\"modulePath\":\"/home/runner/work/hisnameisjimmy.github.io/hisnameisjimmy.github.io/node_modules/gatsby-remark-images/index.js\",\"pluginOptions\":{\"plugins\":[],\"maxWidth\":1200,\"linkImagesToOriginal\":false},\"nodeAPIs\":[\"pluginOptionsSchema\"],\"browserAPIs\":[\"onRouteUpdate\"],\"ssrAPIs\":[]},{\"resolve\":\"/home/runner/work/hisnameisjimmy.github.io/hisnameisjimmy.github.io/node_modules/gatsby-remark-images-medium-zoom\",\"id\":\"9a661529-a37d-52cd-b9a8-f94d932ba2e7\",\"name\":\"gatsby-remark-images-medium-zoom\",\"version\":\"1.7.0\",\"modulePath\":\"/home/runner/work/hisnameisjimmy.github.io/hisnameisjimmy.github.io/node_modules/gatsby-remark-images-medium-zoom/index.js\",\"module\":{},\"pluginOptions\":{\"plugins\":[]},\"nodeAPIs\":[],\"browserAPIs\":[\"onRouteUpdate\"],\"ssrAPIs\":[]}],\"extensions\":[\".mdx\"],\"defaultLayouts\":{},\"lessBabel\":false,\"remarkPlugins\":[],\"rehypePlugins\":[],\"mediaTypes\":[\"text/markdown\",\"text/x-markdown\"],\"root\":\"/home/runner/work/hisnameisjimmy.github.io/hisnameisjimmy.github.io\",\"commonmark\":false,\"JSFrontmatterEngine\":false,\"engines\":{}},\n },{\n plugin: require('../node_modules/gatsby-remark-images/gatsby-browser.js'),\n options: {\"plugins\":[],\"maxWidth\":1200,\"linkImagesToOriginal\":false},\n },{\n plugin: require('../node_modules/gatsby-remark-images-medium-zoom/gatsby-browser.js'),\n options: {\"plugins\":[]},\n },{\n plugin: require('../node_modules/gatsby-remark-images/gatsby-browser.js'),\n options: {\"plugins\":[],\"maxWidth\":650,\"linkImagesToOriginal\":true,\"showCaptions\":false,\"markdownCaptions\":false,\"backgroundColor\":\"white\",\"quality\":50,\"withWebp\":false,\"withAvif\":false,\"tracedSVG\":false,\"loading\":\"lazy\",\"decoding\":\"async\",\"disableBgImageOnAlpha\":false,\"disableBgImage\":false},\n },{\n plugin: require('../node_modules/gatsby-remark-images-medium-zoom/gatsby-browser.js'),\n options: {\"plugins\":[]},\n },{\n plugin: require('../gatsby-browser.js'),\n options: {\"plugins\":[]},\n },{\n plugin: require('../node_modules/gatsby/dist/internal-plugins/partytown/gatsby-browser.js'),\n options: {\"plugins\":[]},\n }]\n","const plugins = require(`./api-runner-browser-plugins`)\nconst { getResourceURLsForPathname, loadPage, loadPageSync } =\n require(`./loader`).publicLoader\n\nexports.apiRunner = (api, args = {}, defaultReturn, argTransform) => {\n // Hooks for gatsby-cypress's API handler\n if (process.env.CYPRESS_SUPPORT) {\n if (window.___apiHandler) {\n window.___apiHandler(api)\n } else if (window.___resolvedAPIs) {\n window.___resolvedAPIs.push(api)\n } else {\n window.___resolvedAPIs = [api]\n }\n }\n\n let results = plugins.map(plugin => {\n if (!plugin.plugin[api]) {\n return undefined\n }\n\n args.getResourceURLsForPathname = getResourceURLsForPathname\n args.loadPage = loadPage\n args.loadPageSync = loadPageSync\n\n const result = plugin.plugin[api](args, plugin.options)\n if (result && argTransform) {\n args = argTransform({ args, result, plugin })\n }\n return result\n })\n\n // Filter out undefined results.\n results = results.filter(result => typeof result !== `undefined`)\n\n if (results.length > 0) {\n return results\n } else if (defaultReturn) {\n return [defaultReturn]\n } else {\n return []\n }\n}\n\nexports.apiRunnerAsync = (api, args, defaultReturn) =>\n plugins.reduce(\n (previous, next) =>\n next.plugin[api]\n ? previous.then(() => next.plugin[api](args, next.options))\n : previous,\n Promise.resolve()\n )\n","// \n// An event handler can take an optional event argument\n// and should not return a value\n \n \n\n// An array of all currently registered event handlers for a type\n \n \n// A map of event types and their corresponding event handlers.\n \n \n \n \n\n/** Mitt: Tiny (~200b) functional event emitter / pubsub.\n * @name mitt\n * @returns {Mitt}\n */\nfunction mitt(all ) {\n\tall = all || Object.create(null);\n\n\treturn {\n\t\t/**\n\t\t * Register an event handler for the given type.\n\t\t *\n\t\t * @param {String} type\tType of event to listen for, or `\"*\"` for all events\n\t\t * @param {Function} handler Function to call in response to given event\n\t\t * @memberOf mitt\n\t\t */\n\t\ton: function on(type , handler ) {\n\t\t\t(all[type] || (all[type] = [])).push(handler);\n\t\t},\n\n\t\t/**\n\t\t * Remove an event handler for the given type.\n\t\t *\n\t\t * @param {String} type\tType of event to unregister `handler` from, or `\"*\"`\n\t\t * @param {Function} handler Handler function to remove\n\t\t * @memberOf mitt\n\t\t */\n\t\toff: function off(type , handler ) {\n\t\t\tif (all[type]) {\n\t\t\t\tall[type].splice(all[type].indexOf(handler) >>> 0, 1);\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Invoke all handlers for the given type.\n\t\t * If present, `\"*\"` handlers are invoked after type-matched handlers.\n\t\t *\n\t\t * @param {String} type The event type to invoke\n\t\t * @param {Any} [evt] Any value (object is recommended and powerful), passed to each handler\n\t\t * @memberOf mitt\n\t\t */\n\t\temit: function emit(type , evt ) {\n\t\t\t(all[type] || []).slice().map(function (handler) { handler(evt); });\n\t\t\t(all['*'] || []).slice().map(function (handler) { handler(type, evt); });\n\t\t}\n\t};\n}\n\nexport default mitt;\n//# sourceMappingURL=mitt.es.js.map\n","import mitt from \"mitt\"\n\nconst emitter = mitt()\nexport default emitter\n","export default pathAndSearch => {\n if (pathAndSearch === undefined) {\n return pathAndSearch\n }\n let [path, search = ``] = pathAndSearch.split(`?`)\n if (search) {\n search = `?` + search\n }\n\n if (path === `/`) {\n return `/` + search\n }\n if (path.charAt(path.length - 1) === `/`) {\n return path.slice(0, -1) + search\n }\n return path + search\n}\n","import { pick } from \"@gatsbyjs/reach-router/lib/utils\"\nimport stripPrefix from \"./strip-prefix\"\nimport normalizePagePath from \"./normalize-page-path\"\nimport { maybeGetBrowserRedirect } from \"./redirect-utils.js\"\n\nconst pathCache = new Map()\nlet matchPaths = []\n\nconst trimPathname = rawPathname => {\n let newRawPathname = rawPathname\n const queryIndex = rawPathname.indexOf(`?`)\n\n if (queryIndex !== -1) {\n const [path, qs] = rawPathname.split(`?`)\n newRawPathname = `${path}?${encodeURIComponent(qs)}`\n }\n\n const pathname = decodeURIComponent(newRawPathname)\n\n // Remove the pathPrefix from the pathname.\n const trimmedPathname = stripPrefix(\n pathname,\n decodeURIComponent(__BASE_PATH__)\n )\n // Remove any hashfragment\n .split(`#`)[0]\n\n return trimmedPathname\n}\n\nfunction absolutify(path) {\n // If it's already absolute, return as-is\n if (\n path.startsWith(`/`) ||\n path.startsWith(`https://`) ||\n path.startsWith(`http://`)\n ) {\n return path\n }\n // Calculate path relative to current location, adding a trailing slash to\n // match behavior of @reach/router\n return new URL(\n path,\n window.location.href + (window.location.href.endsWith(`/`) ? `` : `/`)\n ).pathname\n}\n\n/**\n * Set list of matchPaths\n *\n * @param {Array<{path: string, matchPath: string}>} value collection of matchPaths\n */\nexport const setMatchPaths = value => {\n matchPaths = value\n}\n\n/**\n * Return a matchpath url\n * if `match-paths.json` contains `{ \"/foo*\": \"/page1\", ...}`, then\n * `/foo?bar=far` => `/page1`\n *\n * @param {string} rawPathname A raw pathname\n * @return {string|null}\n */\nexport const findMatchPath = rawPathname => {\n const trimmedPathname = cleanPath(rawPathname)\n\n const pickPaths = matchPaths.map(({ path, matchPath }) => {\n return {\n path: matchPath,\n originalPath: path,\n }\n })\n\n const path = pick(pickPaths, trimmedPathname)\n\n if (path) {\n return normalizePagePath(path.route.originalPath)\n }\n\n return null\n}\n\n/**\n * Return a matchpath params from reach/router rules\n * if `match-paths.json` contains `{ \":bar/*foo\" }`, and the path is /baz/zaz/zoo\n * then it returns\n * { bar: baz, foo: zaz/zoo }\n *\n * @param {string} rawPathname A raw pathname\n * @return {object}\n */\nexport const grabMatchParams = rawPathname => {\n const trimmedPathname = cleanPath(rawPathname)\n\n const pickPaths = matchPaths.map(({ path, matchPath }) => {\n return {\n path: matchPath,\n originalPath: path,\n }\n })\n\n const path = pick(pickPaths, trimmedPathname)\n\n if (path) {\n return path.params\n }\n\n return {}\n}\n\n// Given a raw URL path, returns the cleaned version of it (trim off\n// `#` and query params), or if it matches an entry in\n// `match-paths.json`, its matched path is returned\n//\n// E.g. `/foo?bar=far` => `/foo`\n//\n// Or if `match-paths.json` contains `{ \"/foo*\": \"/page1\", ...}`, then\n// `/foo?bar=far` => `/page1`\nexport const findPath = rawPathname => {\n const trimmedPathname = trimPathname(absolutify(rawPathname))\n if (pathCache.has(trimmedPathname)) {\n return pathCache.get(trimmedPathname)\n }\n\n const redirect = maybeGetBrowserRedirect(rawPathname)\n if (redirect) {\n return findPath(redirect.toPath)\n }\n\n let foundPath = findMatchPath(trimmedPathname)\n\n if (!foundPath) {\n foundPath = cleanPath(rawPathname)\n }\n\n pathCache.set(trimmedPathname, foundPath)\n\n return foundPath\n}\n\n/**\n * Clean a url and converts /index.html => /\n * E.g. `/foo?bar=far` => `/foo`\n *\n * @param {string} rawPathname A raw pathname\n * @return {string}\n */\nexport const cleanPath = rawPathname => {\n const trimmedPathname = trimPathname(absolutify(rawPathname))\n\n let foundPath = trimmedPathname\n if (foundPath === `/index.html`) {\n foundPath = `/`\n }\n\n foundPath = normalizePagePath(foundPath)\n\n return foundPath\n}\n","import React from \"react\"\nimport PropTypes from \"prop-types\"\nimport Link, {\n withPrefix,\n withAssetPrefix,\n navigate,\n push,\n replace,\n navigateTo,\n parsePath,\n} from \"gatsby-link\"\nimport { useScrollRestoration } from \"gatsby-react-router-scroll\"\nimport PageRenderer from \"./public-page-renderer\"\nimport loader from \"./loader\"\n\nconst prefetchPathname = loader.enqueue\n\nconst StaticQueryContext = React.createContext({})\n\nfunction StaticQueryDataRenderer({ staticQueryData, data, query, render }) {\n const finalData = data\n ? data.data\n : staticQueryData[query] && staticQueryData[query].data\n\n return (\n \n {finalData && render(finalData)}\n {!finalData &&
Loading (StaticQuery)
}\n
\n )\n}\n\nconst StaticQuery = props => {\n const { data, query, render, children } = props\n\n return (\n \n {staticQueryData => (\n \n )}\n \n )\n}\n\nconst useStaticQuery = query => {\n if (\n typeof React.useContext !== `function` &&\n process.env.NODE_ENV === `development`\n ) {\n throw new Error(\n `You're likely using a version of React that doesn't support Hooks\\n` +\n `Please update React and ReactDOM to 16.8.0 or later to use the useStaticQuery hook.`\n )\n }\n const context = React.useContext(StaticQueryContext)\n\n // query is a stringified number like `3303882` when wrapped with graphql, If a user forgets\n // to wrap the query in a grqphql, then casting it to a Number results in `NaN` allowing us to\n // catch the misuse of the API and give proper direction\n if (isNaN(Number(query))) {\n throw new Error(`useStaticQuery was called with a string but expects to be called using \\`graphql\\`. Try this:\n\nimport { useStaticQuery, graphql } from 'gatsby';\n\nuseStaticQuery(graphql\\`${query}\\`);\n`)\n }\n\n if (context[query]?.data) {\n return context[query].data\n } else {\n throw new Error(\n `The result of this StaticQuery could not be fetched.\\n\\n` +\n `This is likely a bug in Gatsby and if refreshing the page does not fix it, ` +\n `please open an issue in https://github.com/gatsbyjs/gatsby/issues`\n )\n }\n}\n\nStaticQuery.propTypes = {\n data: PropTypes.object,\n query: PropTypes.string.isRequired,\n render: PropTypes.func,\n children: PropTypes.func,\n}\n\nfunction graphql() {\n throw new Error(\n `It appears like Gatsby is misconfigured. Gatsby related \\`graphql\\` calls ` +\n `are supposed to only be evaluated at compile time, and then compiled away. ` +\n `Unfortunately, something went wrong and the query was left in the compiled code.\\n\\n` +\n `Unless your site has a complex or custom babel/Gatsby configuration this is likely a bug in Gatsby.`\n )\n}\n\nexport {\n Link,\n withAssetPrefix,\n withPrefix,\n graphql,\n parsePath,\n navigate,\n useScrollRestoration,\n StaticQueryContext,\n StaticQuery,\n PageRenderer,\n useStaticQuery,\n prefetchPathname,\n}\n\nexport * from \"gatsby-script\"\n","const support = function (feature) {\n if (typeof document === `undefined`) {\n return false\n }\n const fakeLink = document.createElement(`link`)\n try {\n if (fakeLink.relList && typeof fakeLink.relList.supports === `function`) {\n return fakeLink.relList.supports(feature)\n }\n } catch (err) {\n return false\n }\n return false\n}\n\nconst linkPrefetchStrategy = function (url, options) {\n return new Promise((resolve, reject) => {\n if (typeof document === `undefined`) {\n reject()\n return\n }\n\n const link = document.createElement(`link`)\n link.setAttribute(`rel`, `prefetch`)\n link.setAttribute(`href`, url)\n\n Object.keys(options).forEach(key => {\n link.setAttribute(key, options[key])\n })\n\n link.onload = resolve\n link.onerror = reject\n\n const parentElement =\n document.getElementsByTagName(`head`)[0] ||\n document.getElementsByName(`script`)[0].parentNode\n parentElement.appendChild(link)\n })\n}\n\nconst xhrPrefetchStrategy = function (url) {\n return new Promise((resolve, reject) => {\n const req = new XMLHttpRequest()\n req.open(`GET`, url, true)\n\n req.onload = () => {\n if (req.status === 200) {\n resolve()\n } else {\n reject()\n }\n }\n\n req.send(null)\n })\n}\n\nconst supportedPrefetchStrategy = support(`prefetch`)\n ? linkPrefetchStrategy\n : xhrPrefetchStrategy\n\nconst preFetched = {}\n\nconst prefetch = function (url, options) {\n return new Promise(resolve => {\n if (preFetched[url]) {\n resolve()\n return\n }\n\n supportedPrefetchStrategy(url, options)\n .then(() => {\n resolve()\n preFetched[url] = true\n })\n .catch(() => {}) // 404s are logged to the console anyway\n })\n}\n\nexport default prefetch\n","import prefetchHelper from \"./prefetch\"\nimport emitter from \"./emitter\"\nimport { setMatchPaths, findPath, findMatchPath } from \"./find-path\"\n\n/**\n * Available resource loading statuses\n */\nexport const PageResourceStatus = {\n /**\n * At least one of critical resources failed to load\n */\n Error: `error`,\n /**\n * Resources loaded successfully\n */\n Success: `success`,\n}\n\nconst preferDefault = m => (m && m.default) || m\n\nconst stripSurroundingSlashes = s => {\n s = s[0] === `/` ? s.slice(1) : s\n s = s.endsWith(`/`) ? s.slice(0, -1) : s\n return s\n}\n\nconst createPageDataUrl = rawPath => {\n const [path, maybeSearch] = rawPath.split(`?`)\n const fixedPath = path === `/` ? `index` : stripSurroundingSlashes(path)\n return `${__PATH_PREFIX__}/page-data/${fixedPath}/page-data.json${\n maybeSearch ? `?${maybeSearch}` : ``\n }`\n}\n\nfunction doFetch(url, method = `GET`) {\n return new Promise(resolve => {\n const req = new XMLHttpRequest()\n req.open(method, url, true)\n req.onreadystatechange = () => {\n if (req.readyState == 4) {\n resolve(req)\n }\n }\n req.send(null)\n })\n}\n\nconst doesConnectionSupportPrefetch = () => {\n if (\n `connection` in navigator &&\n typeof navigator.connection !== `undefined`\n ) {\n if ((navigator.connection.effectiveType || ``).includes(`2g`)) {\n return false\n }\n if (navigator.connection.saveData) {\n return false\n }\n }\n return true\n}\n\n// Regex that matches common search crawlers\nconst BOT_REGEX = /bot|crawler|spider|crawling/i\n\nconst toPageResources = (pageData, component = null) => {\n const page = {\n componentChunkName: pageData.componentChunkName,\n path: pageData.path,\n webpackCompilationHash: pageData.webpackCompilationHash,\n matchPath: pageData.matchPath,\n staticQueryHashes: pageData.staticQueryHashes,\n getServerDataError: pageData.getServerDataError,\n }\n\n return {\n component,\n json: pageData.result,\n page,\n }\n}\n\nexport class BaseLoader {\n constructor(loadComponent, matchPaths) {\n // Map of pagePath -> Page. Where Page is an object with: {\n // status: PageResourceStatus.Success || PageResourceStatus.Error,\n // payload: PageResources, // undefined if PageResourceStatus.Error\n // }\n // PageResources is {\n // component,\n // json: pageData.result,\n // page: {\n // componentChunkName,\n // path,\n // webpackCompilationHash,\n // staticQueryHashes\n // },\n // staticQueryResults\n // }\n this.pageDb = new Map()\n this.inFlightDb = new Map()\n this.staticQueryDb = {}\n this.pageDataDb = new Map()\n this.isPrefetchQueueRunning = false\n this.prefetchQueued = []\n this.prefetchTriggered = new Set()\n this.prefetchCompleted = new Set()\n this.loadComponent = loadComponent\n setMatchPaths(matchPaths)\n }\n\n inFlightNetworkRequests = new Map()\n\n memoizedGet(url) {\n let inFlightPromise = this.inFlightNetworkRequests.get(url)\n\n if (!inFlightPromise) {\n inFlightPromise = doFetch(url, `GET`)\n this.inFlightNetworkRequests.set(url, inFlightPromise)\n }\n\n // Prefer duplication with then + catch over .finally to prevent problems in ie11 + firefox\n return inFlightPromise\n .then(response => {\n this.inFlightNetworkRequests.delete(url)\n return response\n })\n .catch(err => {\n this.inFlightNetworkRequests.delete(url)\n throw err\n })\n }\n\n setApiRunner(apiRunner) {\n this.apiRunner = apiRunner\n this.prefetchDisabled = apiRunner(`disableCorePrefetching`).some(a => a)\n }\n\n fetchPageDataJson(loadObj) {\n const { pagePath, retries = 0 } = loadObj\n const url = createPageDataUrl(pagePath)\n return this.memoizedGet(url).then(req => {\n const { status, responseText } = req\n\n // Handle 200\n if (status === 200) {\n try {\n const jsonPayload = JSON.parse(responseText)\n if (jsonPayload.path === undefined) {\n throw new Error(`not a valid pageData response`)\n }\n\n const maybeSearch = pagePath.split(`?`)[1]\n if (maybeSearch && !jsonPayload.path.includes(maybeSearch)) {\n jsonPayload.path += `?${maybeSearch}`\n }\n\n return Object.assign(loadObj, {\n status: PageResourceStatus.Success,\n payload: jsonPayload,\n })\n } catch (err) {\n // continue regardless of error\n }\n }\n\n // Handle 404\n if (status === 404 || status === 200) {\n // If the request was for a 404/500 page and it doesn't exist, we're done\n if (pagePath === `/404.html` || pagePath === `/500.html`) {\n return Object.assign(loadObj, {\n status: PageResourceStatus.Error,\n })\n }\n\n // Need some code here to cache the 404 request. In case\n // multiple loadPageDataJsons result in 404s\n return this.fetchPageDataJson(\n Object.assign(loadObj, { pagePath: `/404.html`, notFound: true })\n )\n }\n\n // handle 500 response (Unrecoverable)\n if (status === 500) {\n return this.fetchPageDataJson(\n Object.assign(loadObj, {\n pagePath: `/500.html`,\n internalServerError: true,\n })\n )\n }\n\n // Handle everything else, including status === 0, and 503s. Should retry\n if (retries < 3) {\n return this.fetchPageDataJson(\n Object.assign(loadObj, { retries: retries + 1 })\n )\n }\n\n // Retried 3 times already, result is an error.\n return Object.assign(loadObj, {\n status: PageResourceStatus.Error,\n })\n })\n }\n\n loadPageDataJson(rawPath) {\n const pagePath = findPath(rawPath)\n if (this.pageDataDb.has(pagePath)) {\n const pageData = this.pageDataDb.get(pagePath)\n if (process.env.BUILD_STAGE !== `develop` || !pageData.stale) {\n return Promise.resolve(pageData)\n }\n }\n\n return this.fetchPageDataJson({ pagePath }).then(pageData => {\n this.pageDataDb.set(pagePath, pageData)\n\n return pageData\n })\n }\n\n findMatchPath(rawPath) {\n return findMatchPath(rawPath)\n }\n\n // TODO check all uses of this and whether they use undefined for page resources not exist\n loadPage(rawPath) {\n const pagePath = findPath(rawPath)\n if (this.pageDb.has(pagePath)) {\n const page = this.pageDb.get(pagePath)\n if (process.env.BUILD_STAGE !== `develop` || !page.payload.stale) {\n if (page.error) {\n return {\n error: page.error,\n status: page.status,\n }\n }\n\n return Promise.resolve(page.payload)\n }\n }\n\n if (this.inFlightDb.has(pagePath)) {\n return this.inFlightDb.get(pagePath)\n }\n\n const inFlightPromise = Promise.all([\n this.loadAppData(),\n this.loadPageDataJson(pagePath),\n ]).then(allData => {\n const result = allData[1]\n if (result.status === PageResourceStatus.Error) {\n return {\n status: PageResourceStatus.Error,\n }\n }\n\n let pageData = result.payload\n const { componentChunkName, staticQueryHashes = [] } = pageData\n\n const finalResult = {}\n\n const componentChunkPromise = this.loadComponent(componentChunkName).then(\n component => {\n finalResult.createdAt = new Date()\n let pageResources\n if (!component || component instanceof Error) {\n finalResult.status = PageResourceStatus.Error\n finalResult.error = component\n } else {\n finalResult.status = PageResourceStatus.Success\n if (result.notFound === true) {\n finalResult.notFound = true\n }\n pageData = Object.assign(pageData, {\n webpackCompilationHash: allData[0]\n ? allData[0].webpackCompilationHash\n : ``,\n })\n pageResources = toPageResources(pageData, component)\n }\n // undefined if final result is an error\n return pageResources\n }\n )\n\n const staticQueryBatchPromise = Promise.all(\n staticQueryHashes.map(staticQueryHash => {\n // Check for cache in case this static query result has already been loaded\n if (this.staticQueryDb[staticQueryHash]) {\n const jsonPayload = this.staticQueryDb[staticQueryHash]\n return { staticQueryHash, jsonPayload }\n }\n\n return this.memoizedGet(\n `${__PATH_PREFIX__}/page-data/sq/d/${staticQueryHash}.json`\n )\n .then(req => {\n const jsonPayload = JSON.parse(req.responseText)\n return { staticQueryHash, jsonPayload }\n })\n .catch(() => {\n throw new Error(\n `We couldn't load \"${__PATH_PREFIX__}/page-data/sq/d/${staticQueryHash}.json\"`\n )\n })\n })\n ).then(staticQueryResults => {\n const staticQueryResultsMap = {}\n\n staticQueryResults.forEach(({ staticQueryHash, jsonPayload }) => {\n staticQueryResultsMap[staticQueryHash] = jsonPayload\n this.staticQueryDb[staticQueryHash] = jsonPayload\n })\n\n return staticQueryResultsMap\n })\n\n return (\n Promise.all([componentChunkPromise, staticQueryBatchPromise])\n .then(([pageResources, staticQueryResults]) => {\n let payload\n if (pageResources) {\n payload = { ...pageResources, staticQueryResults }\n finalResult.payload = payload\n emitter.emit(`onPostLoadPageResources`, {\n page: payload,\n pageResources: payload,\n })\n }\n\n this.pageDb.set(pagePath, finalResult)\n\n if (finalResult.error) {\n return {\n error: finalResult.error,\n status: finalResult.status,\n }\n }\n\n return payload\n })\n // when static-query fail to load we throw a better error\n .catch(err => {\n return {\n error: err,\n status: PageResourceStatus.Error,\n }\n })\n )\n })\n\n inFlightPromise\n .then(() => {\n this.inFlightDb.delete(pagePath)\n })\n .catch(error => {\n this.inFlightDb.delete(pagePath)\n throw error\n })\n\n this.inFlightDb.set(pagePath, inFlightPromise)\n\n return inFlightPromise\n }\n\n // returns undefined if the page does not exists in cache\n loadPageSync(rawPath, options = {}) {\n const pagePath = findPath(rawPath)\n if (this.pageDb.has(pagePath)) {\n const pageData = this.pageDb.get(pagePath)\n\n if (pageData.payload) {\n return pageData.payload\n }\n\n if (options?.withErrorDetails) {\n return {\n error: pageData.error,\n status: pageData.status,\n }\n }\n }\n return undefined\n }\n\n shouldPrefetch(pagePath) {\n // Skip prefetching if we know user is on slow or constrained connection\n if (!doesConnectionSupportPrefetch()) {\n return false\n }\n\n // Don't prefetch if this is a crawler bot\n if (navigator.userAgent && BOT_REGEX.test(navigator.userAgent)) {\n return false\n }\n\n // Check if the page exists.\n if (this.pageDb.has(pagePath)) {\n return false\n }\n\n return true\n }\n\n prefetch(pagePath) {\n if (!this.shouldPrefetch(pagePath)) {\n return {\n then: resolve => resolve(false),\n abort: () => {},\n }\n }\n if (this.prefetchTriggered.has(pagePath)) {\n return {\n then: resolve => resolve(true),\n abort: () => {},\n }\n }\n\n const defer = {\n resolve: null,\n reject: null,\n promise: null,\n }\n defer.promise = new Promise((resolve, reject) => {\n defer.resolve = resolve\n defer.reject = reject\n })\n this.prefetchQueued.push([pagePath, defer])\n const abortC = new AbortController()\n abortC.signal.addEventListener(`abort`, () => {\n const index = this.prefetchQueued.findIndex(([p]) => p === pagePath)\n // remove from the queue\n if (index !== -1) {\n this.prefetchQueued.splice(index, 1)\n }\n })\n\n if (!this.isPrefetchQueueRunning) {\n this.isPrefetchQueueRunning = true\n setTimeout(() => {\n this._processNextPrefetchBatch()\n }, 3000)\n }\n\n return {\n then: (resolve, reject) => defer.promise.then(resolve, reject),\n abort: abortC.abort.bind(abortC),\n }\n }\n\n _processNextPrefetchBatch() {\n const idleCallback = window.requestIdleCallback || (cb => setTimeout(cb, 0))\n\n idleCallback(() => {\n const toPrefetch = this.prefetchQueued.splice(0, 4)\n const prefetches = Promise.all(\n toPrefetch.map(([pagePath, dPromise]) => {\n // Tell plugins with custom prefetching logic that they should start\n // prefetching this path.\n if (!this.prefetchTriggered.has(pagePath)) {\n this.apiRunner(`onPrefetchPathname`, { pathname: pagePath })\n this.prefetchTriggered.add(pagePath)\n }\n\n // If a plugin has disabled core prefetching, stop now.\n if (this.prefetchDisabled) {\n return dPromise.resolve(false)\n }\n\n return this.doPrefetch(findPath(pagePath)).then(() => {\n if (!this.prefetchCompleted.has(pagePath)) {\n this.apiRunner(`onPostPrefetchPathname`, { pathname: pagePath })\n this.prefetchCompleted.add(pagePath)\n }\n\n dPromise.resolve(true)\n })\n })\n )\n\n if (this.prefetchQueued.length) {\n prefetches.then(() => {\n setTimeout(() => {\n this._processNextPrefetchBatch()\n }, 3000)\n })\n } else {\n this.isPrefetchQueueRunning = false\n }\n })\n }\n\n doPrefetch(pagePath) {\n const pageDataUrl = createPageDataUrl(pagePath)\n return prefetchHelper(pageDataUrl, {\n crossOrigin: `anonymous`,\n as: `fetch`,\n }).then(() =>\n // This was just prefetched, so will return a response from\n // the cache instead of making another request to the server\n this.loadPageDataJson(pagePath)\n )\n }\n\n hovering(rawPath) {\n this.loadPage(rawPath)\n }\n\n getResourceURLsForPathname(rawPath) {\n const pagePath = findPath(rawPath)\n const page = this.pageDataDb.get(pagePath)\n if (page) {\n const pageResources = toPageResources(page.payload)\n\n return [\n ...createComponentUrls(pageResources.page.componentChunkName),\n createPageDataUrl(pagePath),\n ]\n } else {\n return null\n }\n }\n\n isPageNotFound(rawPath) {\n const pagePath = findPath(rawPath)\n const page = this.pageDb.get(pagePath)\n return !page || page.notFound\n }\n\n loadAppData(retries = 0) {\n return this.memoizedGet(`${__PATH_PREFIX__}/page-data/app-data.json`).then(\n req => {\n const { status, responseText } = req\n\n let appData\n\n if (status !== 200 && retries < 3) {\n // Retry 3 times incase of non-200 responses\n return this.loadAppData(retries + 1)\n }\n\n // Handle 200\n if (status === 200) {\n try {\n const jsonPayload = JSON.parse(responseText)\n if (jsonPayload.webpackCompilationHash === undefined) {\n throw new Error(`not a valid app-data response`)\n }\n\n appData = jsonPayload\n } catch (err) {\n // continue regardless of error\n }\n }\n\n return appData\n }\n )\n }\n}\n\nconst createComponentUrls = componentChunkName =>\n (window.___chunkMapping[componentChunkName] || []).map(\n chunk => __PATH_PREFIX__ + chunk\n )\n\nexport class ProdLoader extends BaseLoader {\n constructor(asyncRequires, matchPaths, pageData) {\n const loadComponent = chunkName => {\n if (!asyncRequires.components[chunkName]) {\n throw new Error(\n `We couldn't find the correct component chunk with the name ${chunkName}`\n )\n }\n\n return (\n asyncRequires.components[chunkName]()\n .then(preferDefault)\n // loader will handle the case when component is error\n .catch(err => err)\n )\n }\n\n super(loadComponent, matchPaths)\n\n if (pageData) {\n this.pageDataDb.set(findPath(pageData.path), {\n pagePath: pageData.path,\n payload: pageData,\n status: `success`,\n })\n }\n }\n\n doPrefetch(pagePath) {\n return super.doPrefetch(pagePath).then(result => {\n if (result.status !== PageResourceStatus.Success) {\n return Promise.resolve()\n }\n const pageData = result.payload\n const chunkName = pageData.componentChunkName\n const componentUrls = createComponentUrls(chunkName)\n return Promise.all(componentUrls.map(prefetchHelper)).then(() => pageData)\n })\n }\n\n loadPageDataJson(rawPath) {\n return super.loadPageDataJson(rawPath).then(data => {\n if (data.notFound) {\n // check if html file exist using HEAD request:\n // if it does we should navigate to it instead of showing 404\n return doFetch(rawPath, `HEAD`).then(req => {\n if (req.status === 200) {\n // page (.html file) actually exist (or we asked for 404 )\n // returning page resources status as errored to trigger\n // regular browser navigation to given page\n return {\n status: PageResourceStatus.Error,\n }\n }\n\n // if HEAD request wasn't 200, return notFound result\n // and show 404 page\n return data\n })\n }\n return data\n })\n }\n}\n\nlet instance\n\nexport const setLoader = _loader => {\n instance = _loader\n}\n\nexport const publicLoader = {\n enqueue: rawPath => instance.prefetch(rawPath),\n\n // Real methods\n getResourceURLsForPathname: rawPath =>\n instance.getResourceURLsForPathname(rawPath),\n loadPage: rawPath => instance.loadPage(rawPath),\n // TODO add deprecation to v4 so people use withErrorDetails and then we can remove in v5 and change default behaviour\n loadPageSync: (rawPath, options = {}) =>\n instance.loadPageSync(rawPath, options),\n prefetch: rawPath => instance.prefetch(rawPath),\n isPageNotFound: rawPath => instance.isPageNotFound(rawPath),\n hovering: rawPath => instance.hovering(rawPath),\n loadAppData: () => instance.loadAppData(),\n}\n\nexport default publicLoader\n\nexport function getStaticQueryResults() {\n if (instance) {\n return instance.staticQueryDb\n } else {\n return {}\n }\n}\n","import React, { createElement } from \"react\"\nimport PropTypes from \"prop-types\"\nimport { apiRunner } from \"./api-runner-browser\"\nimport { grabMatchParams } from \"./find-path\"\n\n// Renders page\nclass PageRenderer extends React.Component {\n render() {\n const props = {\n ...this.props,\n params: {\n ...grabMatchParams(this.props.location.pathname),\n ...this.props.pageResources.json.pageContext.__params,\n },\n }\n\n const pageElement = createElement(this.props.pageResources.component, {\n ...props,\n key: this.props.path || this.props.pageResources.page.path,\n })\n\n const wrappedPage = apiRunner(\n `wrapPageElement`,\n { element: pageElement, props },\n pageElement,\n ({ result }) => {\n return { element: result, props }\n }\n ).pop()\n\n return wrappedPage\n }\n}\n\nPageRenderer.propTypes = {\n location: PropTypes.object.isRequired,\n pageResources: PropTypes.object.isRequired,\n data: PropTypes.object,\n pageContext: PropTypes.object.isRequired,\n}\n\nexport default PageRenderer\n","// This is extracted to separate module because it's shared\n// between browser and SSR code\nexport const RouteAnnouncerProps = {\n id: `gatsby-announcer`,\n style: {\n position: `absolute`,\n top: 0,\n width: 1,\n height: 1,\n padding: 0,\n overflow: `hidden`,\n clip: `rect(0, 0, 0, 0)`,\n whiteSpace: `nowrap`,\n border: 0,\n },\n \"aria-live\": `assertive`,\n \"aria-atomic\": `true`,\n}\n","import React from \"react\"\nimport PropTypes from \"prop-types\"\nimport loader, { PageResourceStatus } from \"./loader\"\nimport { maybeGetBrowserRedirect } from \"./redirect-utils.js\"\nimport { apiRunner } from \"./api-runner-browser\"\nimport emitter from \"./emitter\"\nimport { RouteAnnouncerProps } from \"./route-announcer-props\"\nimport { navigate as reachNavigate } from \"@gatsbyjs/reach-router\"\nimport { globalHistory } from \"@gatsbyjs/reach-router/lib/history\"\nimport { parsePath } from \"gatsby-link\"\n\nfunction maybeRedirect(pathname) {\n const redirect = maybeGetBrowserRedirect(pathname)\n const { hash, search } = window.location\n\n if (redirect != null) {\n window.___replace(redirect.toPath + search + hash)\n return true\n } else {\n return false\n }\n}\n\n// Catch unhandled chunk loading errors and force a restart of the app.\nlet nextRoute = ``\n\nwindow.addEventListener(`unhandledrejection`, event => {\n if (/loading chunk \\d* failed./i.test(event.reason)) {\n if (nextRoute) {\n window.location.pathname = nextRoute\n }\n }\n})\n\nconst onPreRouteUpdate = (location, prevLocation) => {\n if (!maybeRedirect(location.pathname)) {\n nextRoute = location.pathname\n apiRunner(`onPreRouteUpdate`, { location, prevLocation })\n }\n}\n\nconst onRouteUpdate = (location, prevLocation) => {\n if (!maybeRedirect(location.pathname)) {\n apiRunner(`onRouteUpdate`, { location, prevLocation })\n if (\n process.env.GATSBY_EXPERIMENTAL_QUERY_ON_DEMAND &&\n process.env.GATSBY_QUERY_ON_DEMAND_LOADING_INDICATOR === `true`\n ) {\n emitter.emit(`onRouteUpdate`, { location, prevLocation })\n }\n }\n}\n\nconst navigate = (to, options = {}) => {\n // Support forward/backward navigation with numbers\n // navigate(-2) (jumps back 2 history steps)\n // navigate(2) (jumps forward 2 history steps)\n if (typeof to === `number`) {\n globalHistory.navigate(to)\n return\n }\n\n const { pathname, search, hash } = parsePath(to)\n const redirect = maybeGetBrowserRedirect(pathname)\n\n // If we're redirecting, just replace the passed in pathname\n // to the one we want to redirect to.\n if (redirect) {\n to = redirect.toPath + search + hash\n }\n\n // If we had a service worker update, no matter the path, reload window and\n // reset the pathname whitelist\n if (window.___swUpdated) {\n window.location = pathname + search + hash\n return\n }\n\n // Start a timer to wait for a second before transitioning and showing a\n // loader in case resources aren't around yet.\n const timeoutId = setTimeout(() => {\n emitter.emit(`onDelayedLoadPageResources`, { pathname })\n apiRunner(`onRouteUpdateDelayed`, {\n location: window.location,\n })\n }, 1000)\n\n loader.loadPage(pathname + search).then(pageResources => {\n // If no page resources, then refresh the page\n // Do this, rather than simply `window.location.reload()`, so that\n // pressing the back/forward buttons work - otherwise when pressing\n // back, the browser will just change the URL and expect JS to handle\n // the change, which won't always work since it might not be a Gatsby\n // page.\n if (!pageResources || pageResources.status === PageResourceStatus.Error) {\n window.history.replaceState({}, ``, location.href)\n window.location = pathname\n clearTimeout(timeoutId)\n return\n }\n\n // If the loaded page has a different compilation hash to the\n // window, then a rebuild has occurred on the server. Reload.\n if (process.env.NODE_ENV === `production` && pageResources) {\n if (\n pageResources.page.webpackCompilationHash !==\n window.___webpackCompilationHash\n ) {\n // Purge plugin-offline cache\n if (\n `serviceWorker` in navigator &&\n navigator.serviceWorker.controller !== null &&\n navigator.serviceWorker.controller.state === `activated`\n ) {\n navigator.serviceWorker.controller.postMessage({\n gatsbyApi: `clearPathResources`,\n })\n }\n\n window.location = pathname + search + hash\n }\n }\n reachNavigate(to, options)\n clearTimeout(timeoutId)\n })\n}\n\nfunction shouldUpdateScroll(prevRouterProps, { location }) {\n const { pathname, hash } = location\n const results = apiRunner(`shouldUpdateScroll`, {\n prevRouterProps,\n // `pathname` for backwards compatibility\n pathname,\n routerProps: { location },\n getSavedScrollPosition: args => [\n 0,\n // FIXME this is actually a big code smell, we should fix this\n // eslint-disable-next-line @babel/no-invalid-this\n this._stateStorage.read(args, args.key),\n ],\n })\n if (results.length > 0) {\n // Use the latest registered shouldUpdateScroll result, this allows users to override plugin's configuration\n // @see https://github.com/gatsbyjs/gatsby/issues/12038\n return results[results.length - 1]\n }\n\n if (prevRouterProps) {\n const {\n location: { pathname: oldPathname },\n } = prevRouterProps\n if (oldPathname === pathname) {\n // Scroll to element if it exists, if it doesn't, or no hash is provided,\n // scroll to top.\n return hash ? decodeURI(hash.slice(1)) : [0, 0]\n }\n }\n return true\n}\n\nfunction init() {\n // The \"scroll-behavior\" package expects the \"action\" to be on the location\n // object so let's copy it over.\n globalHistory.listen(args => {\n args.location.action = args.action\n })\n\n window.___push = to => navigate(to, { replace: false })\n window.___replace = to => navigate(to, { replace: true })\n window.___navigate = (to, options) => navigate(to, options)\n}\n\nclass RouteAnnouncer extends React.Component {\n constructor(props) {\n super(props)\n this.announcementRef = React.createRef()\n }\n\n componentDidUpdate(prevProps, nextProps) {\n requestAnimationFrame(() => {\n let pageName = `new page at ${this.props.location.pathname}`\n if (document.title) {\n pageName = document.title\n }\n const pageHeadings = document.querySelectorAll(`#gatsby-focus-wrapper h1`)\n if (pageHeadings && pageHeadings.length) {\n pageName = pageHeadings[0].textContent\n }\n const newAnnouncement = `Navigated to ${pageName}`\n if (this.announcementRef.current) {\n const oldAnnouncement = this.announcementRef.current.innerText\n if (oldAnnouncement !== newAnnouncement) {\n this.announcementRef.current.innerText = newAnnouncement\n }\n }\n })\n }\n\n render() {\n return
\n }\n}\n\nconst compareLocationProps = (prevLocation, nextLocation) => {\n if (prevLocation.href !== nextLocation.href) {\n return true\n }\n\n if (prevLocation?.state?.key !== nextLocation?.state?.key) {\n return true\n }\n\n return false\n}\n\n// Fire on(Pre)RouteUpdate APIs\nclass RouteUpdates extends React.Component {\n constructor(props) {\n super(props)\n onPreRouteUpdate(props.location, null)\n }\n\n componentDidMount() {\n onRouteUpdate(this.props.location, null)\n }\n\n shouldComponentUpdate(prevProps) {\n if (compareLocationProps(prevProps.location, this.props.location)) {\n onPreRouteUpdate(this.props.location, prevProps.location)\n return true\n }\n return false\n }\n\n componentDidUpdate(prevProps) {\n if (compareLocationProps(prevProps.location, this.props.location)) {\n onRouteUpdate(this.props.location, prevProps.location)\n }\n }\n\n render() {\n return (\n \n {this.props.children}\n \n \n )\n }\n}\n\nRouteUpdates.propTypes = {\n location: PropTypes.object.isRequired,\n}\n\nexport { init, shouldUpdateScroll, RouteUpdates, maybeGetBrowserRedirect }\n","// Pulled from react-compat\n// https://github.com/developit/preact-compat/blob/7c5de00e7c85e2ffd011bf3af02899b63f699d3a/src/index.js#L349\nfunction shallowDiffers(a, b) {\n for (var i in a) {\n if (!(i in b)) return true;\n }for (var _i in b) {\n if (a[_i] !== b[_i]) return true;\n }return false;\n}\n\nexport default (function (instance, nextProps, nextState) {\n return shallowDiffers(instance.props, nextProps) || shallowDiffers(instance.state, nextState);\n});","/* global HAS_REACT_18 */\nimport { apiRunner, apiRunnerAsync } from \"./api-runner-browser\"\nimport React from \"react\"\nimport { Router, navigate, Location, BaseContext } from \"@gatsbyjs/reach-router\"\nimport { ScrollContext } from \"gatsby-react-router-scroll\"\nimport { StaticQueryContext } from \"gatsby\"\nimport {\n shouldUpdateScroll,\n init as navigationInit,\n RouteUpdates,\n} from \"./navigation\"\nimport emitter from \"./emitter\"\nimport PageRenderer from \"./page-renderer\"\nimport asyncRequires from \"$virtual/async-requires\"\nimport {\n setLoader,\n ProdLoader,\n publicLoader,\n PageResourceStatus,\n getStaticQueryResults,\n} from \"./loader\"\nimport EnsureResources from \"./ensure-resources\"\nimport stripPrefix from \"./strip-prefix\"\n\n// Generated during bootstrap\nimport matchPaths from \"$virtual/match-paths.json\"\n\nconst loader = new ProdLoader(asyncRequires, matchPaths, window.pageData)\nsetLoader(loader)\nloader.setApiRunner(apiRunner)\n\nlet reactHydrate\nlet reactRender\nif (HAS_REACT_18) {\n const reactDomClient = require(`react-dom/client`)\n reactRender = (Component, el) =>\n reactDomClient.createRoot(el).render(Component)\n reactHydrate = (Component, el) => reactDomClient.hydrateRoot(el, Component)\n} else {\n const reactDomClient = require(`react-dom`)\n reactRender = reactDomClient.render\n reactHydrate = reactDomClient.hydrate\n}\n\nwindow.asyncRequires = asyncRequires\nwindow.___emitter = emitter\nwindow.___loader = publicLoader\n\nnavigationInit()\n\nconst reloadStorageKey = `gatsby-reload-compilation-hash-match`\n\napiRunnerAsync(`onClientEntry`).then(() => {\n // Let plugins register a service worker. The plugin just needs\n // to return true.\n if (apiRunner(`registerServiceWorker`).filter(Boolean).length > 0) {\n require(`./register-service-worker`)\n }\n\n // In gatsby v2 if Router is used in page using matchPaths\n // paths need to contain full path.\n // For example:\n // - page have `/app/*` matchPath\n // - inside template user needs to use `/app/xyz` as path\n // Resetting `basepath`/`baseuri` keeps current behaviour\n // to not introduce breaking change.\n // Remove this in v3\n const RouteHandler = props => (\n \n \n \n )\n\n const DataContext = React.createContext({})\n\n class GatsbyRoot extends React.Component {\n render() {\n const { children } = this.props\n return (\n \n {({ location }) => (\n \n {({ pageResources, location }) => {\n const staticQueryResults = getStaticQueryResults()\n return (\n \n \n {children}\n \n \n )\n }}\n \n )}\n \n )\n }\n }\n\n class LocationHandler extends React.Component {\n render() {\n return (\n \n {({ pageResources, location }) => (\n \n \n \n \n
\n \n \n )}\n \n )\n }\n }\n\n const { pagePath, location: browserLoc } = window\n\n // Explicitly call navigate if the canonical path (window.pagePath)\n // is different to the browser path (window.location.pathname). SSR\n // page paths might include search params, while SSG and DSG won't.\n // If page path include search params we also compare query params.\n // But only if NONE of the following conditions hold:\n //\n // - The url matches a client side route (page.matchPath)\n // - it's a 404 page\n // - it's the offline plugin shell (/offline-plugin-app-shell-fallback/)\n if (\n pagePath &&\n __BASE_PATH__ + pagePath !==\n browserLoc.pathname + (pagePath.includes(`?`) ? browserLoc.search : ``) &&\n !(\n loader.findMatchPath(stripPrefix(browserLoc.pathname, __BASE_PATH__)) ||\n pagePath.match(/^\\/(404|500)(\\/?|.html)$/) ||\n pagePath.match(/^\\/offline-plugin-app-shell-fallback\\/?$/)\n )\n ) {\n navigate(\n __BASE_PATH__ +\n pagePath +\n (!pagePath.includes(`?`) ? browserLoc.search : ``) +\n browserLoc.hash,\n {\n replace: true,\n }\n )\n }\n\n // It's possible that sessionStorage can throw an exception if access is not granted, see https://github.com/gatsbyjs/gatsby/issues/34512\n const getSessionStorage = () => {\n try {\n return sessionStorage\n } catch {\n return null\n }\n }\n\n publicLoader.loadPage(browserLoc.pathname + browserLoc.search).then(page => {\n const sessionStorage = getSessionStorage()\n\n if (\n page?.page?.webpackCompilationHash &&\n page.page.webpackCompilationHash !== window.___webpackCompilationHash\n ) {\n // Purge plugin-offline cache\n if (\n `serviceWorker` in navigator &&\n navigator.serviceWorker.controller !== null &&\n navigator.serviceWorker.controller.state === `activated`\n ) {\n navigator.serviceWorker.controller.postMessage({\n gatsbyApi: `clearPathResources`,\n })\n }\n\n // We have not matching html + js (inlined `window.___webpackCompilationHash`)\n // with our data (coming from `app-data.json` file). This can cause issues such as\n // errors trying to load static queries (as list of static queries is inside `page-data`\n // which might not match to currently loaded `.js` scripts).\n // We are making attempt to reload if hashes don't match, but we also have to handle case\n // when reload doesn't fix it (possibly broken deploy) so we don't end up in infinite reload loop\n if (sessionStorage) {\n const isReloaded = sessionStorage.getItem(reloadStorageKey) === `1`\n\n if (!isReloaded) {\n sessionStorage.setItem(reloadStorageKey, `1`)\n window.location.reload(true)\n return\n }\n }\n }\n\n if (sessionStorage) {\n sessionStorage.removeItem(reloadStorageKey)\n }\n\n if (!page || page.status === PageResourceStatus.Error) {\n const message = `page resources for ${browserLoc.pathname} not found. Not rendering React`\n\n // if the chunk throws an error we want to capture the real error\n // This should help with https://github.com/gatsbyjs/gatsby/issues/19618\n if (page && page.error) {\n console.error(message)\n throw page.error\n }\n\n throw new Error(message)\n }\n\n const SiteRoot = apiRunner(\n `wrapRootElement`,\n { element: },\n ,\n ({ result }) => {\n return { element: result }\n }\n ).pop()\n\n const App = function App() {\n const onClientEntryRanRef = React.useRef(false)\n\n React.useEffect(() => {\n if (!onClientEntryRanRef.current) {\n onClientEntryRanRef.current = true\n if (performance.mark) {\n performance.mark(`onInitialClientRender`)\n }\n\n apiRunner(`onInitialClientRender`)\n }\n }, [])\n\n return {SiteRoot}\n }\n\n const focusEl = document.getElementById(`gatsby-focus-wrapper`)\n\n // Client only pages have any empty body so we just do a normal\n // render to avoid React complaining about hydration mis-matches.\n let defaultRenderer = reactRender\n if (focusEl && focusEl.children.length) {\n defaultRenderer = reactHydrate\n }\n\n const renderer = apiRunner(\n `replaceHydrateFunction`,\n undefined,\n defaultRenderer\n )[0]\n\n function runRender() {\n const rootElement =\n typeof window !== `undefined`\n ? document.getElementById(`___gatsby`)\n : null\n\n renderer(, rootElement)\n }\n\n // https://github.com/madrobby/zepto/blob/b5ed8d607f67724788ec9ff492be297f64d47dfc/src/zepto.js#L439-L450\n // TODO remove IE 10 support\n const doc = document\n if (\n doc.readyState === `complete` ||\n (doc.readyState !== `loading` && !doc.documentElement.doScroll)\n ) {\n setTimeout(function () {\n runRender()\n }, 0)\n } else {\n const handler = function () {\n doc.removeEventListener(`DOMContentLoaded`, handler, false)\n window.removeEventListener(`load`, handler, false)\n\n runRender()\n }\n\n doc.addEventListener(`DOMContentLoaded`, handler, false)\n window.addEventListener(`load`, handler, false)\n }\n })\n})\n","import React from \"react\"\nimport loader, { PageResourceStatus } from \"./loader\"\nimport shallowCompare from \"shallow-compare\"\n\nclass EnsureResources extends React.Component {\n constructor(props) {\n super()\n const { location, pageResources } = props\n this.state = {\n location: { ...location },\n pageResources:\n pageResources ||\n loader.loadPageSync(location.pathname + location.search, {\n withErrorDetails: true,\n }),\n }\n }\n\n static getDerivedStateFromProps({ location }, prevState) {\n if (prevState.location.href !== location.href) {\n const pageResources = loader.loadPageSync(\n location.pathname + location.search,\n {\n withErrorDetails: true,\n }\n )\n\n return {\n pageResources,\n location: { ...location },\n }\n }\n\n return {\n location: { ...location },\n }\n }\n\n loadResources(rawPath) {\n loader.loadPage(rawPath).then(pageResources => {\n if (pageResources && pageResources.status !== PageResourceStatus.Error) {\n this.setState({\n location: { ...window.location },\n pageResources,\n })\n } else {\n window.history.replaceState({}, ``, location.href)\n window.location = rawPath\n }\n })\n }\n\n shouldComponentUpdate(nextProps, nextState) {\n // Always return false if we're missing resources.\n if (!nextState.pageResources) {\n this.loadResources(\n nextProps.location.pathname + nextProps.location.search\n )\n return false\n }\n\n if (\n process.env.BUILD_STAGE === `develop` &&\n nextState.pageResources.stale\n ) {\n this.loadResources(\n nextProps.location.pathname + nextProps.location.search\n )\n return false\n }\n\n // Check if the component or json have changed.\n if (this.state.pageResources !== nextState.pageResources) {\n return true\n }\n if (\n this.state.pageResources.component !== nextState.pageResources.component\n ) {\n return true\n }\n\n if (this.state.pageResources.json !== nextState.pageResources.json) {\n return true\n }\n // Check if location has changed on a page using internal routing\n // via matchPath configuration.\n if (\n this.state.location.key !== nextState.location.key &&\n nextState.pageResources.page &&\n (nextState.pageResources.page.matchPath ||\n nextState.pageResources.page.path)\n ) {\n return true\n }\n return shallowCompare(this, nextProps, nextState)\n }\n\n render() {\n if (\n process.env.NODE_ENV !== `production` &&\n (!this.state.pageResources ||\n this.state.pageResources.status === PageResourceStatus.Error)\n ) {\n const message = `EnsureResources was not able to find resources for path: \"${this.props.location.pathname}\"\nThis typically means that an issue occurred building components for that path.\nRun \\`gatsby clean\\` to remove any cached elements.`\n if (this.state.pageResources?.error) {\n console.error(message)\n throw this.state.pageResources.error\n }\n\n throw new Error(message)\n }\n\n return this.props.children(this.state)\n }\n}\n\nexport default EnsureResources\n","import React from \"react\"\nimport PropTypes from \"prop-types\"\n\nimport loader from \"./loader\"\nimport InternalPageRenderer from \"./page-renderer\"\n\nconst ProdPageRenderer = ({ location }) => {\n const pageResources = loader.loadPageSync(location.pathname)\n if (!pageResources) {\n return null\n }\n return React.createElement(InternalPageRenderer, {\n location,\n pageResources,\n ...pageResources.json,\n })\n}\n\nProdPageRenderer.propTypes = {\n location: PropTypes.shape({\n pathname: PropTypes.string.isRequired,\n }).isRequired,\n}\n\nexport default ProdPageRenderer\n","const preferDefault = m => (m && m.default) || m\n\nif (process.env.BUILD_STAGE === `develop`) {\n module.exports = preferDefault(require(`./public-page-renderer-dev`))\n} else if (process.env.BUILD_STAGE === `build-javascript`) {\n module.exports = preferDefault(require(`./public-page-renderer-prod`))\n} else {\n module.exports = () => null\n}\n","exports.polyfill = Component => Component\n","import redirects from \"./redirects.json\"\n\n// Convert to a map for faster lookup in maybeRedirect()\n\nconst redirectMap = new Map()\nconst redirectIgnoreCaseMap = new Map()\n\nredirects.forEach(redirect => {\n if (redirect.ignoreCase) {\n redirectIgnoreCaseMap.set(redirect.fromPath, redirect)\n } else {\n redirectMap.set(redirect.fromPath, redirect)\n }\n})\n\nexport function maybeGetBrowserRedirect(pathname) {\n let redirect = redirectMap.get(pathname)\n if (!redirect) {\n redirect = redirectIgnoreCaseMap.get(pathname.toLowerCase())\n }\n return redirect\n}\n","import { apiRunner } from \"./api-runner-browser\"\n\nif (\n window.location.protocol !== `https:` &&\n window.location.hostname !== `localhost`\n) {\n console.error(\n `Service workers can only be used over HTTPS, or on localhost for development`\n )\n} else if (`serviceWorker` in navigator) {\n navigator.serviceWorker\n .register(`${__BASE_PATH__}/sw.js`)\n .then(function (reg) {\n reg.addEventListener(`updatefound`, () => {\n apiRunner(`onServiceWorkerUpdateFound`, { serviceWorker: reg })\n // The updatefound event implies that reg.installing is set; see\n // https://w3c.github.io/ServiceWorker/#service-worker-registration-updatefound-event\n const installingWorker = reg.installing\n console.log(`installingWorker`, installingWorker)\n installingWorker.addEventListener(`statechange`, () => {\n switch (installingWorker.state) {\n case `installed`:\n if (navigator.serviceWorker.controller) {\n // At this point, the old content will have been purged and the fresh content will\n // have been added to the cache.\n\n // We set a flag so Gatsby Link knows to refresh the page on next navigation attempt\n window.___swUpdated = true\n // We call the onServiceWorkerUpdateReady API so users can show update prompts.\n apiRunner(`onServiceWorkerUpdateReady`, { serviceWorker: reg })\n\n // If resources failed for the current page, reload.\n if (window.___failedResources) {\n console.log(`resources failed, SW updated - reloading`)\n window.location.reload()\n }\n } else {\n // At this point, everything has been precached.\n // It's the perfect time to display a \"Content is cached for offline use.\" message.\n console.log(`Content is now available offline!`)\n\n // Post to service worker that install is complete.\n // Delay to allow time for the event listener to be added --\n // otherwise fetch is called too soon and resources aren't cached.\n apiRunner(`onServiceWorkerInstalled`, { serviceWorker: reg })\n }\n break\n\n case `redundant`:\n console.error(`The installing service worker became redundant.`)\n apiRunner(`onServiceWorkerRedundant`, { serviceWorker: reg })\n break\n\n case `activated`:\n apiRunner(`onServiceWorkerActive`, { serviceWorker: reg })\n break\n }\n })\n })\n })\n .catch(function (e) {\n console.error(`Error during service worker registration:`, e)\n })\n}\n","/**\n * Remove a prefix from a string. Return the input string if the given prefix\n * isn't found.\n */\n\nexport default function stripPrefix(str, prefix = ``) {\n if (!prefix) {\n return str\n }\n\n if (str === prefix) {\n return `/`\n }\n\n if (str.startsWith(`${prefix}/`)) {\n return str.slice(prefix.length)\n }\n\n return str\n}\n","const listOfMetricsSend = new Set();\n\nfunction debounce(fn, timeout) {\n let timer = null;\n return function (...args) {\n if (timer) {\n clearTimeout(timer);\n }\n\n timer = setTimeout(fn, timeout, ...args);\n };\n}\n\nfunction sendWebVitals() {\n function sendData(data) {\n if (listOfMetricsSend.has(data.name)) {\n return;\n }\n\n listOfMetricsSend.add(data.name);\n sendToGoogleAnalytics(data);\n }\n\n return import(`web-vitals/base`).then(({\n getLCP,\n getFID,\n getCLS\n }) => {\n const debouncedCLS = debounce(sendData, 3000); // we don't need to debounce FID - we send it when it happens\n\n const debouncedFID = sendData; // LCP can occur multiple times so we debounce it\n\n const debouncedLCP = debounce(sendData, 3000); // With the true flag, we measure all previous occurences too, in case we start listening to late.\n\n getCLS(debouncedCLS, true);\n getFID(debouncedFID, true);\n getLCP(debouncedLCP, true);\n });\n}\n\nfunction sendToGoogleAnalytics({\n name,\n value,\n id\n}) {\n window.ga(`send`, `event`, {\n eventCategory: `Web Vitals`,\n eventAction: name,\n // The `id` value will be unique to the current page load. When sending\n // multiple values from the same page (e.g. for CLS), Google Analytics can\n // compute a total by grouping on this ID (note: requires `eventLabel` to\n // be a dimension in your report).\n eventLabel: id,\n // Google Analytics metrics must be integers, so the value is rounded.\n // For CLS the value is first multiplied by 1000 for greater precision\n // (note: increase the multiplier for greater precision if needed).\n eventValue: Math.round(name === `CLS` ? value * 1000 : value),\n // Use a non-interaction event to avoid affecting bounce rate.\n nonInteraction: true,\n // Use `sendBeacon()` if the browser supports it.\n transport: `beacon`\n });\n}\n\nexport const onRouteUpdate = ({\n location\n}, pluginOptions = {}) => {\n const ga = window.ga;\n\n if (process.env.NODE_ENV !== `production` || typeof ga !== `function`) {\n return null;\n }\n\n const pathIsExcluded = location && typeof window.excludeGAPaths !== `undefined` && window.excludeGAPaths.some(rx => rx.test(location.pathname));\n if (pathIsExcluded) return null; // wrap inside a timeout to make sure react-helmet is done with it's changes (https://github.com/gatsbyjs/gatsby/issues/9139)\n // reactHelmet is using requestAnimationFrame: https://github.com/nfl/react-helmet/blob/5.2.0/src/HelmetUtils.js#L296-L299\n\n const sendPageView = () => {\n const pagePath = location ? location.pathname + location.search + location.hash : undefined;\n ga(`set`, `page`, pagePath);\n ga(`send`, `pageview`);\n }; // Minimum delay for reactHelmet's requestAnimationFrame\n\n\n const delay = Math.max(32, pluginOptions.pageTransitionDelay || 0);\n setTimeout(sendPageView, delay);\n return null;\n};\nexport function onInitialClientRender(_, pluginOptions) {\n if (process.env.NODE_ENV === `production` && typeof ga === `function` && pluginOptions.enableWebVitalsTracking) {\n sendWebVitals();\n }\n}","/* global __MANIFEST_PLUGIN_HAS_LOCALISATION__ */\nimport { withPrefix } from \"gatsby\";\nimport getManifestForPathname from \"./get-manifest-pathname\"; // when we don't have localisation in our manifest, we tree shake everything away\n\nexport const onRouteUpdate = function onRouteUpdate({\n location\n}, pluginOptions) {\n if (__MANIFEST_PLUGIN_HAS_LOCALISATION__) {\n const {\n localize\n } = pluginOptions;\n const manifestFilename = getManifestForPathname(location.pathname, localize, true);\n const manifestEl = document.head.querySelector(`link[rel=\"manifest\"]`);\n\n if (manifestEl) {\n manifestEl.setAttribute(`href`, withPrefix(manifestFilename));\n }\n }\n};","\"use strict\";\n\nexports.__esModule = true;\nexports.default = void 0;\n\nvar _gatsby = require(\"gatsby\");\n\n/**\n * Get a manifest filename depending on localized pathname\n *\n * @param {string} pathname\n * @param {Array<{start_url: string, lang: string}>} localizedManifests\n * @param {boolean} shouldPrependPathPrefix\n * @return string\n */\nvar _default = (pathname, localizedManifests, shouldPrependPathPrefix = false) => {\n const defaultFilename = `manifest.webmanifest`;\n\n if (!Array.isArray(localizedManifests)) {\n return defaultFilename;\n }\n\n const localizedManifest = localizedManifests.find(app => {\n let startUrl = app.start_url;\n\n if (shouldPrependPathPrefix) {\n startUrl = (0, _gatsby.withPrefix)(startUrl);\n }\n\n return pathname.startsWith(startUrl);\n });\n\n if (!localizedManifest) {\n return defaultFilename;\n }\n\n return `manifest_${localizedManifest.lang}.webmanifest`;\n};\n\nexports.default = _default;","\"use strict\";\n\nvar injectTwitterScript = function injectTwitterScript() {\n function addJS(jsCode) {\n var s = document.createElement(\"script\");\n s.type = \"text/javascript\";\n s.innerText = jsCode;\n document.getElementsByTagName(\"head\")[0].appendChild(s);\n }\n\n addJS(\"\\n window.twttr = (function(d, s, id) {\\n var js,\\n fjs = d.getElementsByTagName(s)[0],\\n t = window.twttr || {};\\n if (d.getElementById(id)) return t;\\n js = d.createElement(s);\\n js.id = id;\\n js.src = \\\"https://platform.twitter.com/widgets.js\\\";\\n fjs.parentNode.insertBefore(js, fjs);\\n t._e = [];\\n t.ready = function(f) {\\n t._e.push(f);\\n };\\n return t;\\n })(document, \\\"script\\\", \\\"twitter-wjs\\\");\\n \");\n};\n\nvar injectedTwitterScript = false;\nvar embedClasses = [\".twitter-tweet\", \".twitter-timeline\", \".twitter-follow-button\", \".twitter-share-button\"].join(\",\");\n\nexports.onRouteUpdate = function () {\n // If there's an embedded element, lazy-load the twitter script (if it hasn't\n // already been loaded), and then run the twitter load function.\n if (document.querySelector(embedClasses) !== null) {\n if (!injectedTwitterScript) {\n injectTwitterScript();\n injectedTwitterScript = true;\n }\n\n if (typeof twttr !== \"undefined\" && window.twttr.widgets && typeof window.twttr.widgets.load === \"function\") {\n window.twttr.widgets.load();\n }\n }\n};","/*! medium-zoom 1.0.6 | MIT License | https://github.com/francoischalifour/medium-zoom */\nvar _extends = Object.assign || function (target) {\n for (var i = 1; i < arguments.length; i++) {\n var source = arguments[i];\n\n for (var key in source) {\n if (Object.prototype.hasOwnProperty.call(source, key)) {\n target[key] = source[key];\n }\n }\n }\n\n return target;\n};\n\nvar isSupported = function isSupported(node) {\n return node.tagName === 'IMG';\n};\n/* eslint-disable-next-line no-prototype-builtins */\n\n\nvar isNodeList = function isNodeList(selector) {\n return NodeList.prototype.isPrototypeOf(selector);\n};\n\nvar isNode = function isNode(selector) {\n return selector && selector.nodeType === 1;\n};\n\nvar isSvg = function isSvg(image) {\n var source = image.currentSrc || image.src;\n return source.substr(-4).toLowerCase() === '.svg';\n};\n\nvar getImagesFromSelector = function getImagesFromSelector(selector) {\n try {\n if (Array.isArray(selector)) {\n return selector.filter(isSupported);\n }\n\n if (isNodeList(selector)) {\n // Do not use spread operator or Array.from() for IE support\n return [].slice.call(selector).filter(isSupported);\n }\n\n if (isNode(selector)) {\n return [selector].filter(isSupported);\n }\n\n if (typeof selector === 'string') {\n // Do not use spread operator or Array.from() for IE support\n return [].slice.call(document.querySelectorAll(selector)).filter(isSupported);\n }\n\n return [];\n } catch (err) {\n throw new TypeError('The provided selector is invalid.\\n' + 'Expects a CSS selector, a Node element, a NodeList or an array.\\n' + 'See: https://github.com/francoischalifour/medium-zoom');\n }\n};\n\nvar createOverlay = function createOverlay(background) {\n var overlay = document.createElement('div');\n overlay.classList.add('medium-zoom-overlay');\n overlay.style.background = background;\n return overlay;\n};\n\nvar cloneTarget = function cloneTarget(template) {\n var _template$getBounding = template.getBoundingClientRect(),\n top = _template$getBounding.top,\n left = _template$getBounding.left,\n width = _template$getBounding.width,\n height = _template$getBounding.height;\n\n var clone = template.cloneNode();\n var scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;\n var scrollLeft = window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft || 0;\n clone.removeAttribute('id');\n clone.style.position = 'absolute';\n clone.style.top = top + scrollTop + 'px';\n clone.style.left = left + scrollLeft + 'px';\n clone.style.width = width + 'px';\n clone.style.height = height + 'px';\n clone.style.transform = '';\n return clone;\n};\n\nvar createCustomEvent = function createCustomEvent(type, params) {\n var eventParams = _extends({\n bubbles: false,\n cancelable: false,\n detail: undefined\n }, params);\n\n if (typeof window.CustomEvent === 'function') {\n return new CustomEvent(type, eventParams);\n }\n\n var customEvent = document.createEvent('CustomEvent');\n customEvent.initCustomEvent(type, eventParams.bubbles, eventParams.cancelable, eventParams.detail);\n return customEvent;\n};\n\nvar mediumZoom = function mediumZoom(selector) {\n var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n /**\n * Ensure the compatibility with IE11 if no Promise polyfill are used.\n */\n\n var Promise = window.Promise || function Promise(fn) {\n function noop() {}\n\n fn(noop, noop);\n };\n\n var _handleClick = function _handleClick(event) {\n var target = event.target;\n\n if (target === overlay) {\n close();\n return;\n }\n\n if (images.indexOf(target) === -1) {\n return;\n }\n\n toggle({\n target: target\n });\n };\n\n var _handleScroll = function _handleScroll() {\n if (isAnimating || !active.original) {\n return;\n }\n\n var currentScroll = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;\n\n if (Math.abs(scrollTop - currentScroll) > zoomOptions.scrollOffset) {\n setTimeout(close, 150);\n }\n };\n\n var _handleKeyUp = function _handleKeyUp(event) {\n var key = event.key || event.keyCode; // Close if escape key is pressed\n\n if (key === 'Escape' || key === 'Esc' || key === 27) {\n close();\n }\n };\n\n var update = function update() {\n var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n var newOptions = options;\n\n if (options.background) {\n overlay.style.background = options.background;\n }\n\n if (options.container && options.container instanceof Object) {\n newOptions.container = _extends({}, zoomOptions.container, options.container);\n }\n\n if (options.template) {\n var template = isNode(options.template) ? options.template : document.querySelector(options.template);\n newOptions.template = template;\n }\n\n zoomOptions = _extends({}, zoomOptions, newOptions);\n images.forEach(function (image) {\n image.dispatchEvent(createCustomEvent('medium-zoom:update', {\n detail: {\n zoom: zoom\n }\n }));\n });\n return zoom;\n };\n\n var clone = function clone() {\n var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n return mediumZoom(_extends({}, zoomOptions, options));\n };\n\n var attach = function attach() {\n for (var _len = arguments.length, selectors = Array(_len), _key = 0; _key < _len; _key++) {\n selectors[_key] = arguments[_key];\n }\n\n var newImages = selectors.reduce(function (imagesAccumulator, currentSelector) {\n return [].concat(imagesAccumulator, getImagesFromSelector(currentSelector));\n }, []);\n newImages.filter(function (newImage) {\n return images.indexOf(newImage) === -1;\n }).forEach(function (newImage) {\n images.push(newImage);\n newImage.classList.add('medium-zoom-image');\n });\n eventListeners.forEach(function (_ref) {\n var type = _ref.type,\n listener = _ref.listener,\n options = _ref.options;\n newImages.forEach(function (image) {\n image.addEventListener(type, listener, options);\n });\n });\n return zoom;\n };\n\n var detach = function detach() {\n for (var _len2 = arguments.length, selectors = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {\n selectors[_key2] = arguments[_key2];\n }\n\n if (active.zoomed) {\n close();\n }\n\n var imagesToDetach = selectors.length > 0 ? selectors.reduce(function (imagesAccumulator, currentSelector) {\n return [].concat(imagesAccumulator, getImagesFromSelector(currentSelector));\n }, []) : images;\n imagesToDetach.forEach(function (image) {\n image.classList.remove('medium-zoom-image');\n image.dispatchEvent(createCustomEvent('medium-zoom:detach', {\n detail: {\n zoom: zoom\n }\n }));\n });\n images = images.filter(function (image) {\n return imagesToDetach.indexOf(image) === -1;\n });\n return zoom;\n };\n\n var on = function on(type, listener) {\n var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};\n images.forEach(function (image) {\n image.addEventListener('medium-zoom:' + type, listener, options);\n });\n eventListeners.push({\n type: 'medium-zoom:' + type,\n listener: listener,\n options: options\n });\n return zoom;\n };\n\n var off = function off(type, listener) {\n var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};\n images.forEach(function (image) {\n image.removeEventListener('medium-zoom:' + type, listener, options);\n });\n eventListeners = eventListeners.filter(function (eventListener) {\n return !(eventListener.type === 'medium-zoom:' + type && eventListener.listener.toString() === listener.toString());\n });\n return zoom;\n };\n\n var open = function open() {\n var _ref2 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},\n target = _ref2.target;\n\n var _animate = function _animate() {\n var container = {\n width: document.documentElement.clientWidth,\n height: document.documentElement.clientHeight,\n left: 0,\n top: 0,\n right: 0,\n bottom: 0\n };\n var viewportWidth = void 0;\n var viewportHeight = void 0;\n\n if (zoomOptions.container) {\n if (zoomOptions.container instanceof Object) {\n // The container is given as an object with properties like width, height, left, top\n container = _extends({}, container, zoomOptions.container); // We need to adjust custom options like container.right or container.bottom\n\n viewportWidth = container.width - container.left - container.right - zoomOptions.margin * 2;\n viewportHeight = container.height - container.top - container.bottom - zoomOptions.margin * 2;\n } else {\n // The container is given as an element\n var zoomContainer = isNode(zoomOptions.container) ? zoomOptions.container : document.querySelector(zoomOptions.container);\n\n var _zoomContainer$getBou = zoomContainer.getBoundingClientRect(),\n _width = _zoomContainer$getBou.width,\n _height = _zoomContainer$getBou.height,\n _left = _zoomContainer$getBou.left,\n _top = _zoomContainer$getBou.top;\n\n container = _extends({}, container, {\n width: _width,\n height: _height,\n left: _left,\n top: _top\n });\n }\n }\n\n viewportWidth = viewportWidth || container.width - zoomOptions.margin * 2;\n viewportHeight = viewportHeight || container.height - zoomOptions.margin * 2;\n var zoomTarget = active.zoomedHd || active.original;\n var naturalWidth = isSvg(zoomTarget) ? viewportWidth : zoomTarget.naturalWidth || viewportWidth;\n var naturalHeight = isSvg(zoomTarget) ? viewportHeight : zoomTarget.naturalHeight || viewportHeight;\n\n var _zoomTarget$getBoundi = zoomTarget.getBoundingClientRect(),\n top = _zoomTarget$getBoundi.top,\n left = _zoomTarget$getBoundi.left,\n width = _zoomTarget$getBoundi.width,\n height = _zoomTarget$getBoundi.height;\n\n var scaleX = Math.min(naturalWidth, viewportWidth) / width;\n var scaleY = Math.min(naturalHeight, viewportHeight) / height;\n var scale = Math.min(scaleX, scaleY);\n var translateX = (-left + (viewportWidth - width) / 2 + zoomOptions.margin + container.left) / scale;\n var translateY = (-top + (viewportHeight - height) / 2 + zoomOptions.margin + container.top) / scale;\n var transform = 'scale(' + scale + ') translate3d(' + translateX + 'px, ' + translateY + 'px, 0)';\n active.zoomed.style.transform = transform;\n\n if (active.zoomedHd) {\n active.zoomedHd.style.transform = transform;\n }\n };\n\n return new Promise(function (resolve) {\n if (target && images.indexOf(target) === -1) {\n resolve(zoom);\n return;\n }\n\n var _handleOpenEnd = function _handleOpenEnd() {\n isAnimating = false;\n active.zoomed.removeEventListener('transitionend', _handleOpenEnd);\n active.original.dispatchEvent(createCustomEvent('medium-zoom:opened', {\n detail: {\n zoom: zoom\n }\n }));\n resolve(zoom);\n };\n\n if (active.zoomed) {\n resolve(zoom);\n return;\n }\n\n if (target) {\n // The zoom was triggered manually via a click\n active.original = target;\n } else if (images.length > 0) {\n var _images = images;\n active.original = _images[0];\n } else {\n resolve(zoom);\n return;\n }\n\n active.original.dispatchEvent(createCustomEvent('medium-zoom:open', {\n detail: {\n zoom: zoom\n }\n }));\n scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;\n isAnimating = true;\n active.zoomed = cloneTarget(active.original);\n document.body.appendChild(overlay);\n\n if (zoomOptions.template) {\n var template = isNode(zoomOptions.template) ? zoomOptions.template : document.querySelector(zoomOptions.template);\n active.template = document.createElement('div');\n active.template.appendChild(template.content.cloneNode(true));\n document.body.appendChild(active.template);\n }\n\n document.body.appendChild(active.zoomed);\n window.requestAnimationFrame(function () {\n document.body.classList.add('medium-zoom--opened');\n });\n active.original.classList.add('medium-zoom-image--hidden');\n active.zoomed.classList.add('medium-zoom-image--opened');\n active.zoomed.addEventListener('click', close);\n active.zoomed.addEventListener('transitionend', _handleOpenEnd);\n\n if (active.original.getAttribute('data-zoom-src')) {\n active.zoomedHd = active.zoomed.cloneNode(); // Reset the `scrset` property or the HD image won't load.\n\n active.zoomedHd.removeAttribute('srcset');\n active.zoomedHd.removeAttribute('sizes');\n active.zoomedHd.src = active.zoomed.getAttribute('data-zoom-src');\n\n active.zoomedHd.onerror = function () {\n clearInterval(getZoomTargetSize);\n console.warn('Unable to reach the zoom image target ' + active.zoomedHd.src);\n active.zoomedHd = null;\n\n _animate();\n }; // We need to access the natural size of the full HD\n // target as fast as possible to compute the animation.\n\n\n var getZoomTargetSize = setInterval(function () {\n if (active.zoomedHd.complete) {\n clearInterval(getZoomTargetSize);\n active.zoomedHd.classList.add('medium-zoom-image--opened');\n active.zoomedHd.addEventListener('click', close);\n document.body.appendChild(active.zoomedHd);\n\n _animate();\n }\n }, 10);\n } else if (active.original.hasAttribute('srcset')) {\n // If an image has a `srcset` attribuet, we don't know the dimensions of the\n // zoomed (HD) image (like when `data-zoom-src` is specified).\n // Therefore the approach is quite similar.\n active.zoomedHd = active.zoomed.cloneNode(); // Resetting the sizes attribute tells the browser to load the\n // image best fitting the current viewport size, respecting the `srcset`.\n\n active.zoomedHd.removeAttribute('sizes'); // In Firefox, the `loading` attribute needs to be set to `eager` (default\n // value) for the load event to be fired.\n\n active.zoomedHd.removeAttribute('loading'); // Wait for the load event of the hd image. This will fire if the image\n // is already cached.\n\n var loadEventListener = active.zoomedHd.addEventListener('load', function () {\n active.zoomedHd.removeEventListener('load', loadEventListener);\n active.zoomedHd.classList.add('medium-zoom-image--opened');\n active.zoomedHd.addEventListener('click', close);\n document.body.appendChild(active.zoomedHd);\n\n _animate();\n });\n } else {\n _animate();\n }\n });\n };\n\n var close = function close() {\n return new Promise(function (resolve) {\n if (isAnimating || !active.original) {\n resolve(zoom);\n return;\n }\n\n var _handleCloseEnd = function _handleCloseEnd() {\n active.original.classList.remove('medium-zoom-image--hidden');\n document.body.removeChild(active.zoomed);\n\n if (active.zoomedHd) {\n document.body.removeChild(active.zoomedHd);\n }\n\n document.body.removeChild(overlay);\n active.zoomed.classList.remove('medium-zoom-image--opened');\n\n if (active.template) {\n document.body.removeChild(active.template);\n }\n\n isAnimating = false;\n active.zoomed.removeEventListener('transitionend', _handleCloseEnd);\n active.original.dispatchEvent(createCustomEvent('medium-zoom:closed', {\n detail: {\n zoom: zoom\n }\n }));\n active.original = null;\n active.zoomed = null;\n active.zoomedHd = null;\n active.template = null;\n resolve(zoom);\n };\n\n isAnimating = true;\n document.body.classList.remove('medium-zoom--opened');\n active.zoomed.style.transform = '';\n\n if (active.zoomedHd) {\n active.zoomedHd.style.transform = '';\n } // Fade out the template so it's not too abrupt\n\n\n if (active.template) {\n active.template.style.transition = 'opacity 150ms';\n active.template.style.opacity = 0;\n }\n\n active.original.dispatchEvent(createCustomEvent('medium-zoom:close', {\n detail: {\n zoom: zoom\n }\n }));\n active.zoomed.addEventListener('transitionend', _handleCloseEnd);\n });\n };\n\n var toggle = function toggle() {\n var _ref3 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},\n target = _ref3.target;\n\n if (active.original) {\n return close();\n }\n\n return open({\n target: target\n });\n };\n\n var getOptions = function getOptions() {\n return zoomOptions;\n };\n\n var getImages = function getImages() {\n return images;\n };\n\n var getZoomedImage = function getZoomedImage() {\n return active.original;\n };\n\n var images = [];\n var eventListeners = [];\n var isAnimating = false;\n var scrollTop = 0;\n var zoomOptions = options;\n var active = {\n original: null,\n zoomed: null,\n zoomedHd: null,\n template: null // If the selector is omitted, it's replaced by the options\n\n };\n\n if (Object.prototype.toString.call(selector) === '[object Object]') {\n zoomOptions = selector;\n } else if (selector || typeof selector === 'string' // to process empty string as a selector\n ) {\n attach(selector);\n } // Apply the default option values\n\n\n zoomOptions = _extends({\n margin: 0,\n background: '#fff',\n scrollOffset: 40,\n container: null,\n template: null\n }, zoomOptions);\n var overlay = createOverlay(zoomOptions.background);\n document.addEventListener('click', _handleClick);\n document.addEventListener('keyup', _handleKeyUp);\n document.addEventListener('scroll', _handleScroll);\n window.addEventListener('resize', close);\n var zoom = {\n open: open,\n close: close,\n toggle: toggle,\n update: update,\n clone: clone,\n attach: attach,\n detach: detach,\n on: on,\n off: off,\n getOptions: getOptions,\n getImages: getImages,\n getZoomedImage: getZoomedImage\n };\n return zoom;\n};\n\nfunction styleInject(css, ref) {\n if (ref === void 0) ref = {};\n var insertAt = ref.insertAt;\n\n if (!css || typeof document === 'undefined') {\n return;\n }\n\n var head = document.head || document.getElementsByTagName('head')[0];\n var style = document.createElement('style');\n style.type = 'text/css';\n\n if (insertAt === 'top') {\n if (head.firstChild) {\n head.insertBefore(style, head.firstChild);\n } else {\n head.appendChild(style);\n }\n } else {\n head.appendChild(style);\n }\n\n if (style.styleSheet) {\n style.styleSheet.cssText = css;\n } else {\n style.appendChild(document.createTextNode(css));\n }\n}\n\nvar css = \".medium-zoom-overlay{position:fixed;top:0;right:0;bottom:0;left:0;opacity:0;transition:opacity .3s;will-change:opacity}.medium-zoom--opened .medium-zoom-overlay{cursor:pointer;cursor:zoom-out;opacity:1}.medium-zoom-image{cursor:pointer;cursor:zoom-in;transition:transform .3s cubic-bezier(.2,0,.2,1)!important}.medium-zoom-image--hidden{visibility:hidden}.medium-zoom-image--opened{position:relative;cursor:pointer;cursor:zoom-out;will-change:transform}\";\nstyleInject(css);\nexport default mediumZoom;","import mediumZoom from 'medium-zoom'\nimport '@fastly/performance-observer-polyfill/polyfill'\n\n// @see https://github.com/francoischalifour/medium-zoom#options\nconst defaultOptions = {\n margin: 24,\n background: '#fff',\n scrollOffset: 40,\n container: null,\n template: null,\n zIndex: 999,\n excludedSelector: null\n}\n\n// @see https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby-remark-images/src/constants.js#L1\nconst imageClass = '.gatsby-resp-image-image'\n\nconst FIRST_CONTENTFUL_PAINT = 'first-contentful-paint'\nconst ZOOM_STYLE_ID = 'medium-zoom-styles'\nconst TRANSITION_EFFECT = 'opacity 0.5s, transform .3s cubic-bezier(.2,0,.2,1)'\n\nfunction onFCP(callback) {\n // @see https://developers.google.com/web/updates/2016/06/performance-observer\n if (!window.performance || !window.PerformanceObserver) {\n return\n }\n\n const po = new PerformanceObserver(list =>\n list\n .getEntries()\n .filter(({ entryType }) => entryType === 'paint')\n .map(({ name }) => name === FIRST_CONTENTFUL_PAINT)\n .forEach(callback)\n )\n\n try {\n po.observe({ entryTypes: ['measure', 'paint'] })\n } catch (e) {\n console.error(e)\n po.disconnect()\n }\n}\n\nfunction injectStyles(options) {\n const styleTag = document.querySelector(`#${ZOOM_STYLE_ID}`)\n if (styleTag) {\n return\n }\n\n const { zIndex } = options\n const node = document.createElement('style')\n const styles = `\n .medium-zoom--opened > .medium-zoom-overlay,\n .medium-zoom--opened > .medium-zoom-image,\n\t img.medium-zoom-image--opened {\n z-index: ${zIndex}\n }\n `\n node.id = ZOOM_STYLE_ID\n node.innerHTML = styles\n document.head.appendChild(node)\n}\n\nfunction applyZoomEffect({ excludedSelector, includedSelector, ...options }) {\n const imagesSelector = excludedSelector\n ? `${imageClass}:not(${excludedSelector})`\n : imageClass\n\n let imageElements = Array.from(document.querySelectorAll(imagesSelector))\n if (includedSelector) {\n const includedEls = Array.from(document.querySelectorAll(includedSelector))\n imageElements = imageElements.concat(includedEls)\n }\n const images = imageElements\n .filter(el => !el.classList.contains('medium-zoom-image'))\n .map(el => {\n function onImageLoad() {\n const originalTransition = el.style.transition\n el.style.transition = `${originalTransition}, ${TRANSITION_EFFECT}`\n el.removeEventListener('load', onImageLoad)\n }\n el.addEventListener('load', onImageLoad)\n el.setAttribute('tabIndex', 0)\n el.addEventListener('keydown', e => {\n if (e.key === ' ' || e.key === 'Enter') {\n e.preventDefault()\n el.click()\n }\n })\n return el\n })\n\n if (images.length > 0) {\n mediumZoom(images, options)\n }\n}\n\nexport const onRouteUpdate = (_, pluginOptions) => {\n const options = { ...defaultOptions, ...pluginOptions }\n injectStyles(options)\n\n onFCP(() => applyZoomEffect(options))\n applyZoomEffect(options)\n}\n","export default function _objectWithoutPropertiesLoose(source, excluded) {\n if (source == null) return {};\n var target = {};\n var sourceKeys = Object.keys(source);\n var key, i;\n\n for (i = 0; i < sourceKeys.length; i++) {\n key = sourceKeys[i];\n if (excluded.indexOf(key) >= 0) continue;\n target[key] = source[key];\n }\n\n return target;\n}","\"use strict\";\n\nexports.DEFAULT_OPTIONS = {\n maxWidth: 650,\n wrapperStyle: \"\",\n backgroundColor: \"white\",\n linkImagesToOriginal: true,\n showCaptions: false,\n markdownCaptions: false,\n withWebp: false,\n withAvif: false,\n tracedSVG: false,\n loading: \"lazy\",\n decoding: \"async\",\n disableBgImageOnAlpha: false,\n disableBgImage: false\n};\nexports.EMPTY_ALT = \"GATSBY_EMPTY_ALT\";\nexports.imageClass = \"gatsby-resp-image-image\";\nexports.imageWrapperClass = \"gatsby-resp-image-wrapper\";\nexports.imageBackgroundClass = \"gatsby-resp-image-background-image\";","\"use strict\";\n\nvar _require = require(\"./constants\"),\n DEFAULT_OPTIONS = _require.DEFAULT_OPTIONS,\n imageClass = _require.imageClass,\n imageBackgroundClass = _require.imageBackgroundClass,\n imageWrapperClass = _require.imageWrapperClass;\n\nexports.onRouteUpdate = function (apiCallbackContext, pluginOptions) {\n var options = Object.assign({}, DEFAULT_OPTIONS, pluginOptions);\n var imageWrappers = document.querySelectorAll(\".\" + imageWrapperClass); // https://css-tricks.com/snippets/javascript/loop-queryselectorall-matches/\n // for cross-browser looping through NodeList without polyfills\n\n var _loop = function _loop(i) {\n var imageWrapper = imageWrappers[i];\n var backgroundElement = imageWrapper.querySelector(\".\" + imageBackgroundClass);\n var imageElement = imageWrapper.querySelector(\".\" + imageClass);\n\n var onImageLoad = function onImageLoad() {\n backgroundElement.style.transition = \"opacity 0.5s 0.5s\";\n imageElement.style.transition = \"opacity 0.5s\";\n onImageComplete();\n };\n\n var onImageComplete = function onImageComplete() {\n backgroundElement.style.opacity = 0;\n imageElement.style.opacity = 1;\n imageElement.style.color = \"inherit\";\n imageElement.style.boxShadow = \"inset 0px 0px 0px 400px \" + options.backgroundColor;\n imageElement.removeEventListener(\"load\", onImageLoad);\n imageElement.removeEventListener(\"error\", onImageComplete);\n };\n\n imageElement.style.opacity = 0;\n imageElement.addEventListener(\"load\", onImageLoad);\n imageElement.addEventListener(\"error\", onImageComplete);\n\n if (imageElement.complete) {\n onImageComplete();\n }\n };\n\n for (var i = 0; i < imageWrappers.length; i++) {\n _loop(i);\n }\n};","import * as React from 'react';\nexport default {\n React\n};","import React, { createContext, useContext } from \"react\"\n\nconst GatsbyMDXScopeContext = createContext({})\n\nexport const useMDXScope = scope => {\n const contextScope = useContext(GatsbyMDXScopeContext)\n return scope || contextScope\n}\n\nexport const MDXScopeProvider = ({ __mdxScope, children }) =>\n React.createElement(\n GatsbyMDXScopeContext.Provider,\n { value: __mdxScope },\n children\n )\n","var scope_0 = require('/home/runner/work/hisnameisjimmy.github.io/hisnameisjimmy.github.io/.cache/caches/gatsby-plugin-mdx/mdx-scopes-dir/e92f8988d65cf25c087d226e6c0ef06f.js').default;export default\n Object.assign({}, scope_0 )\n ","import React from \"react\"\nimport { MDXProvider, withMDXComponents } from \"@mdx-js/react\"\nimport { MDXScopeProvider } from \"./context\"\n\n/**\n * so, this import is weird right?\n *\n * # What it looks like:\n * we're importing a webpack loader directly into our runtime bundle\n *\n * # What it's actually doing:\n * We configure the `mdx-components` loader in gatsby-node's\n * `onCreateWebpackConfig`. The configuration sets the loader to handle its\n * own file, so if we import `./loaders/mdx-components`, the `mdx-components`\n * loader handles loading itself.\n *\n * # Why does this work?\n * The loader doesn't use the file argument to itself and instead returns\n * a generated file that includes the `gatsby-config` mdxPlugins wrapped in\n * require() statements. This results in the `mdxPlugins` being required\n * and available to the code after this import.\n *\n * # Have a better solution to this?\n * Submit a PR\n */\nimport { plugins as mdxPlugins } from \"./loaders/mdx-components\"\nimport scopeContexts from \"./loaders/mdx-scopes\"\n\nconst componentsAndGuards = {}\n\nconst componentFromGuards = arr =>\n function GatsbyMDXComponentFinder(props) {\n const { Component } = arr.find(({ guard }) => (guard ? guard(props) : true))\n return \n }\n\nmdxPlugins.forEach(({ guards = {}, components }) => {\n Object.entries(components).forEach(([componentName, Component]) => {\n if (componentsAndGuards[componentName]) {\n componentsAndGuards.push({ guard: guards[componentName], Component })\n } else {\n componentsAndGuards[componentName] = [\n { guard: guards[componentName], Component },\n ]\n }\n })\n})\n\nconst components = Object.entries(componentsAndGuards)\n .map(([name, arr]) => {\n return {\n [name]: componentFromGuards(\n arr.concat({ guard: undefined, Component: name })\n ),\n }\n })\n .reduce((acc, obj) => {\n return { ...acc, ...obj }\n }, {})\n\n// merge any components in wrapRootElement above this wrapRoot\nconst MDXConsumer = withMDXComponents(\n ({ components: componentsFromContext, children }) => (\n \n \n {children}\n \n \n )\n)\n\nconst WrapRootElement = ({ element }) => {element}\n\nexport default WrapRootElement\n","import wrap from \"./wrap-root-element\"\nexport const wrapRootElement = wrap\n","module.exports = {plugins: []}","import invariant from \"invariant\"; ////////////////////////////////////////////////////////////////////////////////\n// startsWith(string, search) - Check if `string` starts with `search`\n\nvar startsWith = function startsWith(string, search) {\n return string.substr(0, search.length) === search;\n}; ////////////////////////////////////////////////////////////////////////////////\n// pick(routes, uri)\n//\n// Ranks and picks the best route to match. Each segment gets the highest\n// amount of points, then the type of segment gets an additional amount of\n// points where\n//\n// static > dynamic > splat > root\n//\n// This way we don't have to worry about the order of our routes, let the\n// computers do it.\n//\n// A route looks like this\n//\n// { path, default, value }\n//\n// And a returned match looks like:\n//\n// { route, params, uri }\n//\n// I know, I should use TypeScript not comments for these types.\n\n\nvar pick = function pick(routes, uri) {\n var match = void 0;\n var default_ = void 0;\n\n var _uri$split = uri.split(\"?\"),\n uriPathname = _uri$split[0];\n\n var uriSegments = segmentize(uriPathname);\n var isRootUri = uriSegments[0] === \"\";\n var ranked = rankRoutes(routes);\n\n for (var i = 0, l = ranked.length; i < l; i++) {\n var missed = false;\n var route = ranked[i].route;\n\n if (route.default) {\n default_ = {\n route: route,\n params: {},\n uri: uri\n };\n continue;\n }\n\n var routeSegments = segmentize(route.path);\n var params = {};\n var max = Math.max(uriSegments.length, routeSegments.length);\n var index = 0;\n\n for (; index < max; index++) {\n var routeSegment = routeSegments[index];\n var uriSegment = uriSegments[index];\n\n if (isSplat(routeSegment)) {\n // Hit a splat, just grab the rest, and return a match\n // uri: /files/documents/work\n // route: /files/*\n var param = routeSegment.slice(1) || \"*\";\n params[param] = uriSegments.slice(index).map(decodeURIComponent).join(\"/\");\n break;\n }\n\n if (uriSegment === undefined) {\n // URI is shorter than the route, no match\n // uri: /users\n // route: /users/:userId\n missed = true;\n break;\n }\n\n var dynamicMatch = paramRe.exec(routeSegment);\n\n if (dynamicMatch && !isRootUri) {\n var matchIsNotReserved = reservedNames.indexOf(dynamicMatch[1]) === -1;\n !matchIsNotReserved ? process.env.NODE_ENV !== \"production\" ? invariant(false, \" dynamic segment \\\"\" + dynamicMatch[1] + \"\\\" is a reserved name. Please use a different name in path \\\"\" + route.path + \"\\\".\") : invariant(false) : void 0;\n var value = decodeURIComponent(uriSegment);\n params[dynamicMatch[1]] = value;\n } else if (routeSegment !== uriSegment) {\n // Current segments don't match, not dynamic, not splat, so no match\n // uri: /users/123/settings\n // route: /users/:id/profile\n missed = true;\n break;\n }\n }\n\n if (!missed) {\n match = {\n route: route,\n params: params,\n uri: \"/\" + uriSegments.slice(0, index).join(\"/\")\n };\n break;\n }\n }\n\n return match || default_ || null;\n}; ////////////////////////////////////////////////////////////////////////////////\n// match(path, uri) - Matches just one path to a uri, also lol\n\n\nvar match = function match(path, uri) {\n return pick([{\n path: path\n }], uri);\n}; ////////////////////////////////////////////////////////////////////////////////\n// resolve(to, basepath)\n//\n// Resolves URIs as though every path is a directory, no files. Relative URIs\n// in the browser can feel awkward because not only can you be \"in a directory\"\n// you can be \"at a file\", too. For example\n//\n// browserSpecResolve('foo', '/bar/') => /bar/foo\n// browserSpecResolve('foo', '/bar') => /foo\n//\n// But on the command line of a file system, it's not as complicated, you can't\n// `cd` from a file, only directories. This way, links have to know less about\n// their current path. To go deeper you can do this:\n//\n// \n// // instead of\n// \n//\n// Just like `cd`, if you want to go deeper from the command line, you do this:\n//\n// cd deeper\n// # not\n// cd $(pwd)/deeper\n//\n// By treating every path as a directory, linking to relative paths should\n// require less contextual information and (fingers crossed) be more intuitive.\n\n\nvar resolve = function resolve(to, base) {\n // /foo/bar, /baz/qux => /foo/bar\n if (startsWith(to, \"/\")) {\n return to;\n }\n\n var _to$split = to.split(\"?\"),\n toPathname = _to$split[0],\n toQuery = _to$split[1];\n\n var _base$split = base.split(\"?\"),\n basePathname = _base$split[0];\n\n var toSegments = segmentize(toPathname);\n var baseSegments = segmentize(basePathname); // ?a=b, /users?b=c => /users?a=b\n\n if (toSegments[0] === \"\") {\n return addQuery(basePathname, toQuery);\n } // profile, /users/789 => /users/789/profile\n\n\n if (!startsWith(toSegments[0], \".\")) {\n var pathname = baseSegments.concat(toSegments).join(\"/\");\n return addQuery((basePathname === \"/\" ? \"\" : \"/\") + pathname, toQuery);\n } // ./ /users/123 => /users/123\n // ../ /users/123 => /users\n // ../.. /users/123 => /\n // ../../one /a/b/c/d => /a/b/one\n // .././one /a/b/c/d => /a/b/c/one\n\n\n var allSegments = baseSegments.concat(toSegments);\n var segments = [];\n\n for (var i = 0, l = allSegments.length; i < l; i++) {\n var segment = allSegments[i];\n if (segment === \"..\") segments.pop();else if (segment !== \".\") segments.push(segment);\n }\n\n return addQuery(\"/\" + segments.join(\"/\"), toQuery);\n}; ////////////////////////////////////////////////////////////////////////////////\n// insertParams(path, params)\n\n\nvar insertParams = function insertParams(path, params) {\n var _path$split = path.split(\"?\"),\n pathBase = _path$split[0],\n _path$split$ = _path$split[1],\n query = _path$split$ === undefined ? \"\" : _path$split$;\n\n var segments = segmentize(pathBase);\n var constructedPath = \"/\" + segments.map(function (segment) {\n var match = paramRe.exec(segment);\n return match ? params[match[1]] : segment;\n }).join(\"/\");\n var _params$location = params.location;\n _params$location = _params$location === undefined ? {} : _params$location;\n var _params$location$sear = _params$location.search,\n search = _params$location$sear === undefined ? \"\" : _params$location$sear;\n var searchSplit = search.split(\"?\")[1] || \"\";\n constructedPath = addQuery(constructedPath, query, searchSplit);\n return constructedPath;\n};\n\nvar validateRedirect = function validateRedirect(from, to) {\n var filter = function filter(segment) {\n return isDynamic(segment);\n };\n\n var fromString = segmentize(from).filter(filter).sort().join(\"/\");\n var toString = segmentize(to).filter(filter).sort().join(\"/\");\n return fromString === toString;\n}; ////////////////////////////////////////////////////////////////////////////////\n// Junk\n\n\nvar paramRe = /^:(.+)/;\nvar SEGMENT_POINTS = 4;\nvar STATIC_POINTS = 3;\nvar DYNAMIC_POINTS = 2;\nvar SPLAT_PENALTY = 1;\nvar ROOT_POINTS = 1;\n\nvar isRootSegment = function isRootSegment(segment) {\n return segment === \"\";\n};\n\nvar isDynamic = function isDynamic(segment) {\n return paramRe.test(segment);\n};\n\nvar isSplat = function isSplat(segment) {\n return segment && segment[0] === \"*\";\n};\n\nvar rankRoute = function rankRoute(route, index) {\n var score = route.default ? 0 : segmentize(route.path).reduce(function (score, segment) {\n score += SEGMENT_POINTS;\n if (isRootSegment(segment)) score += ROOT_POINTS;else if (isDynamic(segment)) score += DYNAMIC_POINTS;else if (isSplat(segment)) score -= SEGMENT_POINTS + SPLAT_PENALTY;else score += STATIC_POINTS;\n return score;\n }, 0);\n return {\n route: route,\n score: score,\n index: index\n };\n};\n\nvar rankRoutes = function rankRoutes(routes) {\n return routes.map(rankRoute).sort(function (a, b) {\n return a.score < b.score ? 1 : a.score > b.score ? -1 : a.index - b.index;\n });\n};\n\nvar segmentize = function segmentize(uri) {\n return uri // strip starting/ending slashes\n .replace(/(^\\/+|\\/+$)/g, \"\").split(\"/\");\n};\n\nvar addQuery = function addQuery(pathname) {\n for (var _len = arguments.length, query = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {\n query[_key - 1] = arguments[_key];\n }\n\n query = query.filter(function (q) {\n return q && q.length > 0;\n });\n return pathname + (query && query.length > 0 ? \"?\" + query.join(\"&\") : \"\");\n};\n\nvar reservedNames = [\"uri\", \"path\"];\n/**\n * Shallow compares two objects.\n * @param {Object} obj1 The first object to compare.\n * @param {Object} obj2 The second object to compare.\n */\n\nvar shallowCompare = function shallowCompare(obj1, obj2) {\n var obj1Keys = Object.keys(obj1);\n return obj1Keys.length === Object.keys(obj2).length && obj1Keys.every(function (key) {\n return obj2.hasOwnProperty(key) && obj1[key] === obj2[key];\n });\n}; ////////////////////////////////////////////////////////////////////////////////\n\n\nexport { startsWith, pick, match, resolve, insertParams, validateRedirect, shallowCompare };","var _extends = Object.assign || function (target) {\n for (var i = 1; i < arguments.length; i++) {\n var source = arguments[i];\n\n for (var key in source) {\n if (Object.prototype.hasOwnProperty.call(source, key)) {\n target[key] = source[key];\n }\n }\n }\n\n return target;\n};\n\nvar getLocation = function getLocation(source) {\n var _source$location = source.location,\n search = _source$location.search,\n hash = _source$location.hash,\n href = _source$location.href,\n origin = _source$location.origin,\n protocol = _source$location.protocol,\n host = _source$location.host,\n hostname = _source$location.hostname,\n port = _source$location.port;\n var pathname = source.location.pathname;\n\n if (!pathname && href && canUseDOM) {\n var url = new URL(href);\n pathname = url.pathname;\n }\n\n return {\n pathname: encodeURI(decodeURI(pathname)),\n search: search,\n hash: hash,\n href: href,\n origin: origin,\n protocol: protocol,\n host: host,\n hostname: hostname,\n port: port,\n state: source.history.state,\n key: source.history.state && source.history.state.key || \"initial\"\n };\n};\n\nvar createHistory = function createHistory(source, options) {\n var listeners = [];\n var location = getLocation(source);\n var transitioning = false;\n\n var resolveTransition = function resolveTransition() {};\n\n return {\n get location() {\n return location;\n },\n\n get transitioning() {\n return transitioning;\n },\n\n _onTransitionComplete: function _onTransitionComplete() {\n transitioning = false;\n resolveTransition();\n },\n listen: function listen(listener) {\n listeners.push(listener);\n\n var popstateListener = function popstateListener() {\n location = getLocation(source);\n listener({\n location: location,\n action: \"POP\"\n });\n };\n\n source.addEventListener(\"popstate\", popstateListener);\n return function () {\n source.removeEventListener(\"popstate\", popstateListener);\n listeners = listeners.filter(function (fn) {\n return fn !== listener;\n });\n };\n },\n navigate: function navigate(to) {\n var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},\n state = _ref.state,\n _ref$replace = _ref.replace,\n replace = _ref$replace === undefined ? false : _ref$replace;\n\n if (typeof to === \"number\") {\n source.history.go(to);\n } else {\n state = _extends({}, state, {\n key: Date.now() + \"\"\n }); // try...catch iOS Safari limits to 100 pushState calls\n\n try {\n if (transitioning || replace) {\n source.history.replaceState(state, null, to);\n } else {\n source.history.pushState(state, null, to);\n }\n } catch (e) {\n source.location[replace ? \"replace\" : \"assign\"](to);\n }\n }\n\n location = getLocation(source);\n transitioning = true;\n var transition = new Promise(function (res) {\n return resolveTransition = res;\n });\n listeners.forEach(function (listener) {\n return listener({\n location: location,\n action: \"PUSH\"\n });\n });\n return transition;\n }\n };\n}; ////////////////////////////////////////////////////////////////////////////////\n// Stores history entries in memory for testing or other platforms like Native\n\n\nvar createMemorySource = function createMemorySource() {\n var initialPath = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : \"/\";\n var searchIndex = initialPath.indexOf(\"?\");\n var initialLocation = {\n pathname: searchIndex > -1 ? initialPath.substr(0, searchIndex) : initialPath,\n search: searchIndex > -1 ? initialPath.substr(searchIndex) : \"\"\n };\n var index = 0;\n var stack = [initialLocation];\n var states = [null];\n return {\n get location() {\n return stack[index];\n },\n\n addEventListener: function addEventListener(name, fn) {},\n removeEventListener: function removeEventListener(name, fn) {},\n history: {\n get entries() {\n return stack;\n },\n\n get index() {\n return index;\n },\n\n get state() {\n return states[index];\n },\n\n pushState: function pushState(state, _, uri) {\n var _uri$split = uri.split(\"?\"),\n pathname = _uri$split[0],\n _uri$split$ = _uri$split[1],\n search = _uri$split$ === undefined ? \"\" : _uri$split$;\n\n index++;\n stack.push({\n pathname: pathname,\n search: search.length ? \"?\" + search : search\n });\n states.push(state);\n },\n replaceState: function replaceState(state, _, uri) {\n var _uri$split2 = uri.split(\"?\"),\n pathname = _uri$split2[0],\n _uri$split2$ = _uri$split2[1],\n search = _uri$split2$ === undefined ? \"\" : _uri$split2$;\n\n stack[index] = {\n pathname: pathname,\n search: search\n };\n states[index] = state;\n },\n go: function go(to) {\n var newIndex = index + to;\n\n if (newIndex < 0 || newIndex > states.length - 1) {\n return;\n }\n\n index = newIndex;\n }\n }\n };\n}; ////////////////////////////////////////////////////////////////////////////////\n// global history - uses window.history as the source if available, otherwise a\n// memory history\n\n\nvar canUseDOM = !!(typeof window !== \"undefined\" && window.document && window.document.createElement);\n\nvar getSource = function getSource() {\n return canUseDOM ? window : createMemorySource();\n};\n\nvar globalHistory = createHistory(getSource());\nvar navigate = globalHistory.navigate; ////////////////////////////////////////////////////////////////////////////////\n\nexport { globalHistory, navigate, createHistory, createMemorySource };","var _extends = Object.assign || function (target) {\n for (var i = 1; i < arguments.length; i++) {\n var source = arguments[i];\n\n for (var key in source) {\n if (Object.prototype.hasOwnProperty.call(source, key)) {\n target[key] = source[key];\n }\n }\n }\n\n return target;\n};\n\nfunction _objectWithoutProperties(obj, keys) {\n var target = {};\n\n for (var i in obj) {\n if (keys.indexOf(i) >= 0) continue;\n if (!Object.prototype.hasOwnProperty.call(obj, i)) continue;\n target[i] = obj[i];\n }\n\n return target;\n}\n\nfunction _classCallCheck(instance, Constructor) {\n if (!(instance instanceof Constructor)) {\n throw new TypeError(\"Cannot call a class as a function\");\n }\n}\n\nfunction _possibleConstructorReturn(self, call) {\n if (!self) {\n throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\");\n }\n\n return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self;\n}\n\nfunction _inherits(subClass, superClass) {\n if (typeof superClass !== \"function\" && superClass !== null) {\n throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass);\n }\n\n subClass.prototype = Object.create(superClass && superClass.prototype, {\n constructor: {\n value: subClass,\n enumerable: false,\n writable: true,\n configurable: true\n }\n });\n if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;\n}\n/* eslint-disable jsx-a11y/anchor-has-content */\n\n\nimport React, { useContext, createContext } from \"react\";\nimport invariant from \"invariant\";\nimport { polyfill } from \"react-lifecycles-compat\";\nimport { startsWith, pick, resolve, match, insertParams, validateRedirect, shallowCompare } from \"./lib/utils\";\nimport { globalHistory, navigate, createHistory, createMemorySource } from \"./lib/history\"; ////////////////////////////////////////////////////////////////////////////////\n\nvar createNamedContext = function createNamedContext(name, defaultValue) {\n var Ctx = createContext(defaultValue);\n Ctx.displayName = name;\n return Ctx;\n}; ////////////////////////////////////////////////////////////////////////////////\n// Location Context/Provider\n\n\nvar LocationContext = createNamedContext(\"Location\"); // sets up a listener if there isn't one already so apps don't need to be\n// wrapped in some top level provider\n\nvar Location = function Location(_ref) {\n var children = _ref.children;\n return React.createElement(LocationContext.Consumer, null, function (context) {\n return context ? children(context) : React.createElement(LocationProvider, null, children);\n });\n};\n\nvar LocationProvider = function (_React$Component) {\n _inherits(LocationProvider, _React$Component);\n\n function LocationProvider() {\n var _temp, _this, _ret;\n\n _classCallCheck(this, LocationProvider);\n\n for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {\n args[_key] = arguments[_key];\n }\n\n return _ret = (_temp = (_this = _possibleConstructorReturn(this, _React$Component.call.apply(_React$Component, [this].concat(args))), _this), _this.state = {\n context: _this.getContext(),\n refs: {\n unlisten: null\n }\n }, _temp), _possibleConstructorReturn(_this, _ret);\n }\n\n LocationProvider.prototype.getContext = function getContext() {\n var _props$history = this.props.history,\n navigate = _props$history.navigate,\n location = _props$history.location;\n return {\n navigate: navigate,\n location: location\n };\n };\n\n LocationProvider.prototype.componentDidCatch = function componentDidCatch(error, info) {\n if (isRedirect(error)) {\n var _navigate = this.props.history.navigate;\n\n _navigate(error.uri, {\n replace: true\n });\n } else {\n throw error;\n }\n };\n\n LocationProvider.prototype.componentDidUpdate = function componentDidUpdate(prevProps, prevState) {\n if (prevState.context.location !== this.state.context.location) {\n this.props.history._onTransitionComplete();\n }\n };\n\n LocationProvider.prototype.componentDidMount = function componentDidMount() {\n var _this2 = this;\n\n var refs = this.state.refs,\n history = this.props.history;\n\n history._onTransitionComplete();\n\n refs.unlisten = history.listen(function () {\n Promise.resolve().then(function () {\n // TODO: replace rAF with react deferred update API when it's ready https://github.com/facebook/react/issues/13306\n requestAnimationFrame(function () {\n if (!_this2.unmounted) {\n _this2.setState(function () {\n return {\n context: _this2.getContext()\n };\n });\n }\n });\n });\n });\n };\n\n LocationProvider.prototype.componentWillUnmount = function componentWillUnmount() {\n var refs = this.state.refs;\n this.unmounted = true;\n refs.unlisten();\n };\n\n LocationProvider.prototype.render = function render() {\n var context = this.state.context,\n children = this.props.children;\n return React.createElement(LocationContext.Provider, {\n value: context\n }, typeof children === \"function\" ? children(context) : children || null);\n };\n\n return LocationProvider;\n}(React.Component); ////////////////////////////////////////////////////////////////////////////////\n\n\nLocationProvider.defaultProps = {\n history: globalHistory\n};\nprocess.env.NODE_ENV !== \"production\" ? void 0 : void 0;\n\nvar ServerLocation = function ServerLocation(_ref2) {\n var url = _ref2.url,\n children = _ref2.children;\n var searchIndex = url.indexOf(\"?\");\n var searchExists = searchIndex > -1;\n var pathname = void 0;\n var search = \"\";\n var hash = \"\";\n\n if (searchExists) {\n pathname = url.substring(0, searchIndex);\n search = url.substring(searchIndex);\n } else {\n pathname = url;\n }\n\n return React.createElement(LocationContext.Provider, {\n value: {\n location: {\n pathname: pathname,\n search: search,\n hash: hash\n },\n navigate: function navigate() {\n throw new Error(\"You can't call navigate on the server.\");\n }\n }\n }, children);\n}; ////////////////////////////////////////////////////////////////////////////////\n// Sets baseuri and basepath for nested routers and links\n\n\nvar BaseContext = createNamedContext(\"Base\", {\n baseuri: \"/\",\n basepath: \"/\",\n navigate: globalHistory.navigate\n}); ////////////////////////////////////////////////////////////////////////////////\n// The main event, welcome to the show everybody.\n\nvar Router = function Router(props) {\n return React.createElement(BaseContext.Consumer, null, function (baseContext) {\n return React.createElement(Location, null, function (locationContext) {\n return React.createElement(RouterImpl, _extends({}, baseContext, locationContext, props));\n });\n });\n};\n\nvar RouterImpl = function (_React$PureComponent) {\n _inherits(RouterImpl, _React$PureComponent);\n\n function RouterImpl() {\n _classCallCheck(this, RouterImpl);\n\n return _possibleConstructorReturn(this, _React$PureComponent.apply(this, arguments));\n }\n\n RouterImpl.prototype.render = function render() {\n var _props = this.props,\n location = _props.location,\n _navigate2 = _props.navigate,\n basepath = _props.basepath,\n primary = _props.primary,\n children = _props.children,\n baseuri = _props.baseuri,\n _props$component = _props.component,\n component = _props$component === undefined ? \"div\" : _props$component,\n domProps = _objectWithoutProperties(_props, [\"location\", \"navigate\", \"basepath\", \"primary\", \"children\", \"baseuri\", \"component\"]);\n\n var routes = React.Children.toArray(children).reduce(function (array, child) {\n var routes = createRoute(basepath)(child);\n return array.concat(routes);\n }, []);\n var pathname = location.pathname;\n var match = pick(routes, pathname);\n\n if (match) {\n var params = match.params,\n uri = match.uri,\n route = match.route,\n element = match.route.value; // remove the /* from the end for child routes relative paths\n\n basepath = route.default ? basepath : route.path.replace(/\\*$/, \"\");\n\n var props = _extends({}, params, {\n uri: uri,\n location: location,\n navigate: function navigate(to, options) {\n return _navigate2(resolve(to, uri), options);\n }\n });\n\n var clone = React.cloneElement(element, props, element.props.children ? React.createElement(Router, {\n location: location,\n primary: primary\n }, element.props.children) : undefined); // using 'div' for < 16.3 support\n\n var FocusWrapper = primary ? FocusHandler : component; // don't pass any props to 'div'\n\n var wrapperProps = primary ? _extends({\n uri: uri,\n location: location,\n component: component\n }, domProps) : domProps;\n return React.createElement(BaseContext.Provider, {\n value: {\n baseuri: uri,\n basepath: basepath,\n navigate: props.navigate\n }\n }, React.createElement(FocusWrapper, wrapperProps, clone));\n } else {\n // Not sure if we want this, would require index routes at every level\n // warning(\n // false,\n // `\\n\\nNothing matched:\\n\\t${\n // location.pathname\n // }\\n\\nPaths checked: \\n\\t${routes\n // .map(route => route.path)\n // .join(\n // \"\\n\\t\"\n // )}\\n\\nTo get rid of this warning, add a default NotFound component as child of Router:\n // \\n\\tlet NotFound = () =>
Not Found!
\n // \\n\\t\\n\\t \\n\\t {/* ... */}\\n\\t`\n // );\n return null;\n }\n };\n\n return RouterImpl;\n}(React.PureComponent);\n\nRouterImpl.defaultProps = {\n primary: true\n};\nvar FocusContext = createNamedContext(\"Focus\");\n\nvar FocusHandler = function FocusHandler(_ref3) {\n var uri = _ref3.uri,\n location = _ref3.location,\n component = _ref3.component,\n domProps = _objectWithoutProperties(_ref3, [\"uri\", \"location\", \"component\"]);\n\n return React.createElement(FocusContext.Consumer, null, function (requestFocus) {\n return React.createElement(FocusHandlerImpl, _extends({}, domProps, {\n component: component,\n requestFocus: requestFocus,\n uri: uri,\n location: location\n }));\n });\n}; // don't focus on initial render\n\n\nvar initialRender = true;\nvar focusHandlerCount = 0;\n\nvar FocusHandlerImpl = function (_React$Component2) {\n _inherits(FocusHandlerImpl, _React$Component2);\n\n function FocusHandlerImpl() {\n var _temp2, _this4, _ret2;\n\n _classCallCheck(this, FocusHandlerImpl);\n\n for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {\n args[_key2] = arguments[_key2];\n }\n\n return _ret2 = (_temp2 = (_this4 = _possibleConstructorReturn(this, _React$Component2.call.apply(_React$Component2, [this].concat(args))), _this4), _this4.state = {}, _this4.requestFocus = function (node) {\n if (!_this4.state.shouldFocus && node) {\n node.focus();\n }\n }, _temp2), _possibleConstructorReturn(_this4, _ret2);\n }\n\n FocusHandlerImpl.getDerivedStateFromProps = function getDerivedStateFromProps(nextProps, prevState) {\n var initial = prevState.uri == null;\n\n if (initial) {\n return _extends({\n shouldFocus: true\n }, nextProps);\n } else {\n var myURIChanged = nextProps.uri !== prevState.uri;\n var navigatedUpToMe = prevState.location.pathname !== nextProps.location.pathname && nextProps.location.pathname === nextProps.uri;\n return _extends({\n shouldFocus: myURIChanged || navigatedUpToMe\n }, nextProps);\n }\n };\n\n FocusHandlerImpl.prototype.componentDidMount = function componentDidMount() {\n focusHandlerCount++;\n this.focus();\n };\n\n FocusHandlerImpl.prototype.componentWillUnmount = function componentWillUnmount() {\n focusHandlerCount--;\n\n if (focusHandlerCount === 0) {\n initialRender = true;\n }\n };\n\n FocusHandlerImpl.prototype.componentDidUpdate = function componentDidUpdate(prevProps, prevState) {\n if (prevProps.location !== this.props.location && this.state.shouldFocus) {\n this.focus();\n }\n };\n\n FocusHandlerImpl.prototype.focus = function focus() {\n if (process.env.NODE_ENV === \"test\") {\n // getting cannot read property focus of null in the tests\n // and that bit of global `initialRender` state causes problems\n // should probably figure it out!\n return;\n }\n\n var requestFocus = this.props.requestFocus;\n\n if (requestFocus) {\n requestFocus(this.node);\n } else {\n if (initialRender) {\n initialRender = false;\n } else if (this.node) {\n // React polyfills [autofocus] and it fires earlier than cDM,\n // so we were stealing focus away, this line prevents that.\n if (!this.node.contains(document.activeElement)) {\n this.node.focus();\n }\n }\n }\n };\n\n FocusHandlerImpl.prototype.render = function render() {\n var _this5 = this;\n\n var _props2 = this.props,\n children = _props2.children,\n style = _props2.style,\n requestFocus = _props2.requestFocus,\n _props2$component = _props2.component,\n Comp = _props2$component === undefined ? \"div\" : _props2$component,\n uri = _props2.uri,\n location = _props2.location,\n domProps = _objectWithoutProperties(_props2, [\"children\", \"style\", \"requestFocus\", \"component\", \"uri\", \"location\"]);\n\n return React.createElement(Comp, _extends({\n style: _extends({\n outline: \"none\"\n }, style),\n tabIndex: \"-1\",\n ref: function ref(n) {\n return _this5.node = n;\n }\n }, domProps), React.createElement(FocusContext.Provider, {\n value: this.requestFocus\n }, this.props.children));\n };\n\n return FocusHandlerImpl;\n}(React.Component);\n\npolyfill(FocusHandlerImpl);\n\nvar k = function k() {}; ////////////////////////////////////////////////////////////////////////////////\n\n\nvar forwardRef = React.forwardRef;\n\nif (typeof forwardRef === \"undefined\") {\n forwardRef = function forwardRef(C) {\n return C;\n };\n}\n\nvar Link = forwardRef(function (_ref4, ref) {\n var innerRef = _ref4.innerRef,\n props = _objectWithoutProperties(_ref4, [\"innerRef\"]);\n\n return React.createElement(BaseContext.Consumer, null, function (_ref5) {\n var basepath = _ref5.basepath,\n baseuri = _ref5.baseuri;\n return React.createElement(Location, null, function (_ref6) {\n var location = _ref6.location,\n navigate = _ref6.navigate;\n\n var to = props.to,\n state = props.state,\n replace = props.replace,\n _props$getProps = props.getProps,\n getProps = _props$getProps === undefined ? k : _props$getProps,\n anchorProps = _objectWithoutProperties(props, [\"to\", \"state\", \"replace\", \"getProps\"]);\n\n var href = resolve(to, baseuri);\n var encodedHref = encodeURI(href);\n var isCurrent = location.pathname === encodedHref;\n var isPartiallyCurrent = startsWith(location.pathname, encodedHref);\n return React.createElement(\"a\", _extends({\n ref: ref || innerRef,\n \"aria-current\": isCurrent ? \"page\" : undefined\n }, anchorProps, getProps({\n isCurrent: isCurrent,\n isPartiallyCurrent: isPartiallyCurrent,\n href: href,\n location: location\n }), {\n href: href,\n onClick: function onClick(event) {\n if (anchorProps.onClick) anchorProps.onClick(event);\n\n if (shouldNavigate(event)) {\n event.preventDefault();\n var shouldReplace = replace;\n\n if (typeof replace !== \"boolean\" && isCurrent) {\n var _location$state = _extends({}, location.state),\n key = _location$state.key,\n restState = _objectWithoutProperties(_location$state, [\"key\"]);\n\n shouldReplace = shallowCompare(_extends({}, state), restState);\n }\n\n navigate(href, {\n state: state,\n replace: shouldReplace\n });\n }\n }\n }));\n });\n });\n});\nLink.displayName = \"Link\";\nprocess.env.NODE_ENV !== \"production\" ? void 0 : void 0; ////////////////////////////////////////////////////////////////////////////////\n\nfunction RedirectRequest(uri) {\n this.uri = uri;\n}\n\nvar isRedirect = function isRedirect(o) {\n return o instanceof RedirectRequest;\n};\n\nvar redirectTo = function redirectTo(to) {\n throw new RedirectRequest(to);\n};\n\nvar RedirectImpl = function (_React$Component3) {\n _inherits(RedirectImpl, _React$Component3);\n\n function RedirectImpl() {\n _classCallCheck(this, RedirectImpl);\n\n return _possibleConstructorReturn(this, _React$Component3.apply(this, arguments));\n } // Support React < 16 with this hook\n\n\n RedirectImpl.prototype.componentDidMount = function componentDidMount() {\n var _props3 = this.props,\n navigate = _props3.navigate,\n to = _props3.to,\n from = _props3.from,\n _props3$replace = _props3.replace,\n replace = _props3$replace === undefined ? true : _props3$replace,\n state = _props3.state,\n noThrow = _props3.noThrow,\n baseuri = _props3.baseuri,\n props = _objectWithoutProperties(_props3, [\"navigate\", \"to\", \"from\", \"replace\", \"state\", \"noThrow\", \"baseuri\"]);\n\n Promise.resolve().then(function () {\n var resolvedTo = resolve(to, baseuri);\n navigate(insertParams(resolvedTo, props), {\n replace: replace,\n state: state\n });\n });\n };\n\n RedirectImpl.prototype.render = function render() {\n var _props4 = this.props,\n navigate = _props4.navigate,\n to = _props4.to,\n from = _props4.from,\n replace = _props4.replace,\n state = _props4.state,\n noThrow = _props4.noThrow,\n baseuri = _props4.baseuri,\n props = _objectWithoutProperties(_props4, [\"navigate\", \"to\", \"from\", \"replace\", \"state\", \"noThrow\", \"baseuri\"]);\n\n var resolvedTo = resolve(to, baseuri);\n if (!noThrow) redirectTo(insertParams(resolvedTo, props));\n return null;\n };\n\n return RedirectImpl;\n}(React.Component);\n\nvar Redirect = function Redirect(props) {\n return React.createElement(BaseContext.Consumer, null, function (_ref7) {\n var baseuri = _ref7.baseuri;\n return React.createElement(Location, null, function (locationContext) {\n return React.createElement(RedirectImpl, _extends({}, locationContext, {\n baseuri: baseuri\n }, props));\n });\n });\n};\n\nprocess.env.NODE_ENV !== \"production\" ? void 0 : void 0; ////////////////////////////////////////////////////////////////////////////////\n\nvar Match = function Match(_ref8) {\n var path = _ref8.path,\n children = _ref8.children;\n return React.createElement(BaseContext.Consumer, null, function (_ref9) {\n var baseuri = _ref9.baseuri;\n return React.createElement(Location, null, function (_ref10) {\n var navigate = _ref10.navigate,\n location = _ref10.location;\n var resolvedPath = resolve(path, baseuri);\n var result = match(resolvedPath, location.pathname);\n return children({\n navigate: navigate,\n location: location,\n match: result ? _extends({}, result.params, {\n uri: result.uri,\n path: path\n }) : null\n });\n });\n });\n}; ////////////////////////////////////////////////////////////////////////////////\n// Hooks\n\n\nvar useLocation = function useLocation() {\n var context = useContext(LocationContext);\n\n if (!context) {\n throw new Error(\"useLocation hook was used but a LocationContext.Provider was not found in the parent tree. Make sure this is used in a component that is a child of Router\");\n }\n\n return context.location;\n};\n\nvar useNavigate = function useNavigate() {\n var context = useContext(BaseContext);\n\n if (!context) {\n throw new Error(\"useNavigate hook was used but a BaseContext.Provider was not found in the parent tree. Make sure this is used in a component that is a child of Router\");\n }\n\n return context.navigate;\n};\n\nvar useParams = function useParams() {\n var context = useContext(BaseContext);\n\n if (!context) {\n throw new Error(\"useParams hook was used but a LocationContext.Provider was not found in the parent tree. Make sure this is used in a component that is a child of Router\");\n }\n\n var location = useLocation();\n var results = match(context.basepath, location.pathname);\n return results ? results.params : null;\n};\n\nvar useMatch = function useMatch(path) {\n if (!path) {\n throw new Error(\"useMatch(path: string) requires an argument of a string to match against\");\n }\n\n var context = useContext(BaseContext);\n\n if (!context) {\n throw new Error(\"useMatch hook was used but a LocationContext.Provider was not found in the parent tree. Make sure this is used in a component that is a child of Router\");\n }\n\n var location = useLocation();\n var resolvedPath = resolve(path, context.baseuri);\n var result = match(resolvedPath, location.pathname);\n return result ? _extends({}, result.params, {\n uri: result.uri,\n path: path\n }) : null;\n}; ////////////////////////////////////////////////////////////////////////////////\n// Junk\n\n\nvar stripSlashes = function stripSlashes(str) {\n return str.replace(/(^\\/+|\\/+$)/g, \"\");\n};\n\nvar createRoute = function createRoute(basepath) {\n return function (element) {\n if (!element) {\n return null;\n }\n\n if (element.type === React.Fragment && element.props.children) {\n return React.Children.map(element.props.children, createRoute(basepath));\n }\n\n !(element.props.path || element.props.default || element.type === Redirect) ? process.env.NODE_ENV !== \"production\" ? invariant(false, \": Children of must have a `path` or `default` prop, or be a ``. None found on element type `\" + element.type + \"`\") : invariant(false) : void 0;\n !!(element.type === Redirect && (!element.props.from || !element.props.to)) ? process.env.NODE_ENV !== \"production\" ? invariant(false, \" requires both \\\"from\\\" and \\\"to\\\" props when inside a .\") : invariant(false) : void 0;\n !!(element.type === Redirect && !validateRedirect(element.props.from, element.props.to)) ? process.env.NODE_ENV !== \"production\" ? invariant(false, \" has mismatched dynamic segments, ensure both paths have the exact same dynamic segments.\") : invariant(false) : void 0;\n\n if (element.props.default) {\n return {\n value: element,\n default: true\n };\n }\n\n var elementPath = element.type === Redirect ? element.props.from : element.props.path;\n var path = elementPath === \"/\" ? basepath : stripSlashes(basepath) + \"/\" + stripSlashes(elementPath);\n return {\n value: element,\n default: element.props.default,\n path: element.props.children ? stripSlashes(path) + \"/*\" : path\n };\n };\n};\n\nvar shouldNavigate = function shouldNavigate(event) {\n return !event.defaultPrevented && event.button === 0 && !(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey);\n}; ////////////////////////////////////////////////////////////////////////\n\n\nexport { Link, Location, LocationProvider, Match, Redirect, Router, ServerLocation, createHistory, createMemorySource, isRedirect, navigate, redirectTo, globalHistory, match as matchPath, useLocation, useNavigate, useParams, useMatch , BaseContext };","/**\n * Copyright (c) 2013-present, Facebook, Inc.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n'use strict';\n\n/**\n * Use invariant() to assert state which your program assumes to be true.\n *\n * Provide sprintf-style format (only %s is supported) and arguments\n * to provide information about what broke and what you were\n * expecting.\n *\n * The invariant message will be stripped in production, but the invariant\n * will remain to ensure logic does not differ in production.\n */\n\nvar invariant = function(condition, format, a, b, c, d, e, f) {\n if (process.env.NODE_ENV !== 'production') {\n if (format === undefined) {\n throw new Error('invariant requires an error message argument');\n }\n }\n\n if (!condition) {\n var error;\n if (format === undefined) {\n error = new Error(\n 'Minified exception occurred; use the non-minified dev environment ' +\n 'for the full error message and additional helpful warnings.'\n );\n } else {\n var args = [a, b, c, d, e, f];\n var argIndex = 0;\n error = new Error(\n format.replace(/%s/g, function() { return args[argIndex++]; })\n );\n error.name = 'Invariant Violation';\n }\n\n error.framesToPop = 1; // we don't care about invariant's own frame\n throw error;\n }\n};\n\nmodule.exports = invariant;\n","'use strict';\n\nObject.defineProperty(exports, '__esModule', { value: true });\n\nconst PartytownSnippet = \"/* Partytown 0.5.4 - MIT builder.io */\\n!function(t,e,n,i,r,o,a,d,s,c,p,l){function u(){l||(l=1,\\\"/\\\"==(a=(o.lib||\\\"/~partytown/\\\")+(o.debug?\\\"debug/\\\":\\\"\\\"))[0]&&(s=e.querySelectorAll('script[type=\\\"text/partytown\\\"]'),i!=t?i.dispatchEvent(new CustomEvent(\\\"pt1\\\",{detail:t})):(d=setTimeout(w,1e4),e.addEventListener(\\\"pt0\\\",f),r?h(1):n.serviceWorker?n.serviceWorker.register(a+(o.swPath||\\\"partytown-sw.js\\\"),{scope:a}).then((function(t){t.active?h():t.installing&&t.installing.addEventListener(\\\"statechange\\\",(function(t){\\\"activated\\\"==t.target.state&&h()}))}),console.error):w())))}function h(t){c=e.createElement(t?\\\"script\\\":\\\"iframe\\\"),t||(c.setAttribute(\\\"style\\\",\\\"display:block;width:0;height:0;border:0;visibility:hidden\\\"),c.setAttribute(\\\"aria-hidden\\\",!0)),c.src=a+\\\"partytown-\\\"+(t?\\\"atomics.js?v=0.5.4\\\":\\\"sandbox-sw.html?\\\"+Date.now()),e.body.appendChild(c)}function w(t,n){for(f(),t=0;t {\n const { forward = [], ...filteredConfig } = config || {};\n const configStr = JSON.stringify(filteredConfig, (k, v) => {\n if (typeof v === 'function') {\n v = String(v);\n if (v.startsWith(k + '(')) {\n v = 'function ' + v;\n }\n }\n return v;\n });\n return [\n `!(function(w,p,f,c){`,\n Object.keys(filteredConfig).length > 0\n ? `c=w[p]=Object.assign(w[p]||{},${configStr});`\n : `c=w[p]=w[p]||{};`,\n `c[f]=(c[f]||[])`,\n forward.length > 0 ? `.concat(${JSON.stringify(forward)})` : ``,\n `})(window,'partytown','forward');`,\n snippetCode,\n ].join('');\n};\n\n/**\n * The `type` attribute for Partytown scripts, which does two things:\n *\n * 1. Prevents the `Art Diario - The most complicated thing I've built | Jimmy Hooker

Art Diario - The most complicated thing I've built

Download Art Diario!

Download Art Diario!

TL;DR - You should download my simple little app that I have poured an insane amount of effort into making. Please only tell me that you love it, but also report bugs!

About two years ago I started Lambda School part time. While I'd always been a technical kid, I never was able to dedicate the time to fill in the gaps of my programming knowledge. I could hack things together, but I lacked the ability to create cohesive CRUD apps. Anything beyond simple functions was way too much for me. I barely understood what an array was. Lambda School was the first time I could commit to learning programming without quitting my day job, and I jumped at the opportunity.

There's a lot more to this story, and I want to write about it in more depth, but the long and short is that, while brutal, it worked, and I now know how to program in a lot more depth.

A while back I stumbled on an app called ArtPip. It had this core idea of showing everyone who had the app the same piece of art per day. I loved it. I loved being exposed to art that I would have never otherwise seen, I loved how it lasted for 24 hours, and I loved that it was a shared experience with everyone who had the app. Unfortunately, the engineer building it wasn't able to continue, and eventually the app itself switched over to photographs instead of paintings. Photographs are great, but they're an entirely different medium. It felt like the core reason that I wanted to have the app installed was gone.

How much I loved this concept really stuck with me. When the app died originally I really wanted to recreate it right then, but it would be another year until I started Lambda School, a year from then when I would create my first version, and another year after that until I could really polish it into something that other people could use.

+Art Diario - The most complicated thing I've built | Jimmy Hooker

Art Diario - The most complicated thing I've built

Download Art Diario!

Download Art Diario!

TL;DR - You should download my simple little app that I have poured an insane amount of effort into making. Please only tell me that you love it, but also report bugs!

About two years ago I started Lambda School part time. While I'd always been a technical kid, I never was able to dedicate the time to fill in the gaps of my programming knowledge. I could hack things together, but I lacked the ability to create cohesive CRUD apps. Anything beyond simple functions was way too much for me. I barely understood what an array was. Lambda School was the first time I could commit to learning programming without quitting my day job, and I jumped at the opportunity.

There's a lot more to this story, and I want to write about it in more depth, but the long and short is that, while brutal, it worked, and I now know how to program in a lot more depth.

A while back I stumbled on an app called ArtPip. It had this core idea of showing everyone who had the app the same piece of art per day. I loved it. I loved being exposed to art that I would have never otherwise seen, I loved how it lasted for 24 hours, and I loved that it was a shared experience with everyone who had the app. Unfortunately, the engineer building it wasn't able to continue, and eventually the app itself switched over to photographs instead of paintings. Photographs are great, but they're an entirely different medium. It felt like the core reason that I wanted to have the app installed was gone.

How much I loved this concept really stuck with me. When the app died originally I really wanted to recreate it right then, but it would be another year until I started Lambda School, a year from then when I would create my first version, and another year after that until I could really polish it into something that other people could use.

This is an image

This is a very simple app conceptually. One piece of art per day, everyone see's the same art. That's it. It is purely because I love that core experience that I built it. I want everyone to have this same experience.

But, holy crap, was it difficult. There's a huge gap between a proof of concept and something much more polished.

I wanted a beautiful website and an extremely easy to use admin area to upload and manage the art itself. I wanted everything to be fast, I wanted it to be easy to deploy, and I wanted it to be cheap as hell to host. I think, somehow, I've been able to finally manage all of this.

@@ -7,20 +7,14 @@

A beautiful react single-page-app for the website/front-end admin, an express server/api for the backend, and an electron app for the desktop client. Mix in Netlify, AWS S3/Cloudfront, Heroku, Github actions, and Cloudflare caching on images and api calls. I'll have to do a technical deep dive in a future post, but the coordination between all these things was by far the most complex thing I've ever had the opportunity to build.

This is an image -

I think I can honestly say this is the culmination of a life long dream to be able to design, build, and deploy apps.

And, for a first app, I really am glad this is it. It is a pure app: No scheme to get you to buy anything. No agenda. It's purely about the experience of art.

It's hard to convey how much you'll love it too. Download it, and don't do anything. Just let your desktop change each day. Have some friends download it. Suddenly you're talking about art. It's a subtle thing, and it makes me unreasonably happy.

\ No newline at end of file + if(true) { + window.dataLayer = window.dataLayer || []; + function gtag(){window.dataLayer && window.dataLayer.push(arguments);} + gtag('js', new Date()); + + gtag('config', 'G-4HPC5TT803', {"send_page_view":false}); + } + \ No newline at end of file diff --git a/blog/being-curious/index.html b/blog/being-curious/index.html index 9594d84..fdadc70 100644 --- a/blog/being-curious/index.html +++ b/blog/being-curious/index.html @@ -1,17 +1,11 @@ -Being Curious | Jimmy Hooker

Being Curious

Photo by Daniele Franchi

Photo by Daniele Franchi

Be curious, not judgmental - Walt Whitman

The other day I listened to a podcast between Ezra Klein and Judson Brewer talking about anxiety. If you have even mild bouts of anxiety, I highly recommend it: Ezra Klein and Judson Brewer on Anxiety

The core takeaway for me was around meditation, and how a key component of that is curiosity. A ton of meditation centers around focusing on your breath, but what does it mean to focus on your breath?

I did a Vipassana around 7 years ago. The long and short is that it's a 10-day meditation retreat, free of charge, where you meditate silently for approximately 11 hours a day (with breaks for eating/walking/napping in between sessions). It is very intense, which is what attracted me to it in the first place. While I've gotten a few people to do it, I get why most people are not into the idea. It sounds like work, and really, it is. Most would rather go to the beach, and that's valid.

But for me, someone who has pretty consistently struggled to maintain a sense of inner peace, it sounded like a key to unlocking my self. And, in many ways, it has become an incredible tool in my tool belt. I always feel better after doing it. At the same time, I really fight with it. It has always felt like making myself exercise, or do something against my will. Buster Benson talks a bit about this in his post Better than Meditation.

For me, I always feel like I'm enduring meditation. That the goal is just to get to the end and then I will be free. I will have done the chore. However, doing this, I've noticed that I actually feel this way about a lot of things.

If I just get through this project at Lambda School, I will be done with this pain. If I can just save X amount, I can get this Y. If I can just get through the end of this week, I'll be free this weekend. If I just get this job, this apartment, this life.

I'm not the first person to have this realization. We are all on the hedonic treadmill to some degree or another. But do I want to spend my whole life just anticipating things? When do I actually get to enjoy things?

I recently saw a bit of an interview on Twitter with Mads Mikkelson where he mentions his philosophy about life, and it struck me as the most ideal possible way to really live:

This brings us back to curiosity. In the interview, Judson talks about curiosity as being a way of subverting your natural tendency to anticipate or ruminate. By being curious about the present, you take your attention away from the past and future, which don't actually exist. Instead of just trying to get past your current state, you develop an interest in it, even if it feels painful.

How do you stop being anxious about the future? Develop an interest in the present. What is happening presently? You are breathing, you are existing in this world, everything about your circumstances is a miracle. Daniel Kaluuya agrees.

So what's happening right now. What is your breath like. What are the nuances of it. What does it feel like leaving your nose. What's the quality of your breath? How hot is it? How many seconds can you maintain focus on your breath? Can you just observe? Can you witness things? How long can you be curious about the current moment?

What is the current problem you are facing in life? Can you treat it like it's a puzzle? A fun game? Or is it something to be endured? Is life just to be struggled against until you die? Or can you see it as an incredible opportunity, something to be curious about, to enjoy?

I read this article by Richard Feynman the other day around him finding his way back to loving physics. He reframed things through curiosity and play.

I'm not sure why that podcast with Ezra Klein pulled this all together for me, but it hit me hard. Listen to it! In the meantime, I'm going to try and remain curious.

Being Curious | Jimmy Hooker

Being Curious

Photo by Daniele Franchi

Photo by Daniele Franchi

Be curious, not judgmental - Walt Whitman

The other day I listened to a podcast between Ezra Klein and Judson Brewer talking about anxiety. If you have even mild bouts of anxiety, I highly recommend it: Ezra Klein and Judson Brewer on Anxiety

The core takeaway for me was around meditation, and how a key component of that is curiosity. A ton of meditation centers around focusing on your breath, but what does it mean to focus on your breath?

I did a Vipassana around 7 years ago. The long and short is that it's a 10-day meditation retreat, free of charge, where you meditate silently for approximately 11 hours a day (with breaks for eating/walking/napping in between sessions). It is very intense, which is what attracted me to it in the first place. While I've gotten a few people to do it, I get why most people are not into the idea. It sounds like work, and really, it is. Most would rather go to the beach, and that's valid.

But for me, someone who has pretty consistently struggled to maintain a sense of inner peace, it sounded like a key to unlocking my self. And, in many ways, it has become an incredible tool in my tool belt. I always feel better after doing it. At the same time, I really fight with it. It has always felt like making myself exercise, or do something against my will. Buster Benson talks a bit about this in his post Better than Meditation.

For me, I always feel like I'm enduring meditation. That the goal is just to get to the end and then I will be free. I will have done the chore. However, doing this, I've noticed that I actually feel this way about a lot of things.

If I just get through this project at Lambda School, I will be done with this pain. If I can just save X amount, I can get this Y. If I can just get through the end of this week, I'll be free this weekend. If I just get this job, this apartment, this life.

I'm not the first person to have this realization. We are all on the hedonic treadmill to some degree or another. But do I want to spend my whole life just anticipating things? When do I actually get to enjoy things?

I recently saw a bit of an interview on Twitter with Mads Mikkelson where he mentions his philosophy about life, and it struck me as the most ideal possible way to really live:

This brings us back to curiosity. In the interview, Judson talks about curiosity as being a way of subverting your natural tendency to anticipate or ruminate. By being curious about the present, you take your attention away from the past and future, which don't actually exist. Instead of just trying to get past your current state, you develop an interest in it, even if it feels painful.

How do you stop being anxious about the future? Develop an interest in the present. What is happening presently? You are breathing, you are existing in this world, everything about your circumstances is a miracle. Daniel Kaluuya agrees.

So what's happening right now. What is your breath like. What are the nuances of it. What does it feel like leaving your nose. What's the quality of your breath? How hot is it? How many seconds can you maintain focus on your breath? Can you just observe? Can you witness things? How long can you be curious about the current moment?

What is the current problem you are facing in life? Can you treat it like it's a puzzle? A fun game? Or is it something to be endured? Is life just to be struggled against until you die? Or can you see it as an incredible opportunity, something to be curious about, to enjoy?

I read this article by Richard Feynman the other day around him finding his way back to loving physics. He reframed things through curiosity and play.

I'm not sure why that podcast with Ezra Klein pulled this all together for me, but it hit me hard. Listen to it! In the meantime, I'm going to try and remain curious.

\ No newline at end of file + if(true) { + window.dataLayer = window.dataLayer || []; + function gtag(){window.dataLayer && window.dataLayer.push(arguments);} + gtag('js', new Date()); + + gtag('config', 'G-4HPC5TT803', {"send_page_view":false}); + } + \ No newline at end of file diff --git a/blog/being-grateful/index.html b/blog/being-grateful/index.html index 24297af..e8c63fe 100644 --- a/blog/being-grateful/index.html +++ b/blog/being-grateful/index.html @@ -1,17 +1,11 @@ -Being Grateful | Jimmy Hooker

Being Grateful

Art by Albrecht Dürer

Art by Albrecht Dürer

I think I've been trying to figure out myself and my relationship to the world since I was about 17. About 20 years of introspection. That's... maybe too much.

I got into spiritual stuff when I was pretty young because I was deeply anxious and it impeded a lot of what I wanted to do and how I interacted with people. Some of this had to do with my parents, but some was probably built in. I thought if I figured myself out, I could finally live as my true self without anxiety. But it never really worked out, despite throwing a lot of weaponry at it, including therapy, intensive meditation, supplements, exercise, and more. Those things helped, but it usually felt like I was dealing with symptoms rather than the core of the thing.

There are a number of studies that extol the virtues of gratitude. Start a gratitude journal! It will make you notably happier! But that always felt like work to me. It made gratitude into a job, and so, even after trying many times, I tended to avoid it. I didn't disagree with the ideas, whenever I stepped back I realized my life was really pretty great. I just couldn't make it a consistent habit.

But one thing I've done recently has changed this. Pretty dramatically in my experience.

I spend a lot of time being critical. I'd go so far as to say 999 out of a 1000 thoughts are critical. I am constantly noticing things that are out of place, that could be improved, that are bad, that are dumb. This is hugely useful in a working context, it makes you detail oriented and motivated to improve things. But if you can't shut that voice off, it becomes overwhelming and exhausting, coloring your whole experience. If you are almost only noticing the bad things, life seems... pretty bad!

But how do you change the ratio? How do you get to, let's be optimistic, and say, 5% grateful/positive thoughts? 5 thoughts out of 100.

In the past, I would try and convince myself that life was good. But it felt fake. I was already convinced that things were shit, so saying things like "life is amazing" felt false. Like I was a bad salesman trying to pitch an unimpressed customer.

But a recent realization has changed this. You just have to go to the extreme, foundational basics. You have to get down to the sensory experience. Breathing is... cool actually?

Stay with me.

Right now, you are alive. The fact that you are alive is remarkable, actually. Feel your hands, notice the sensations surrounding them. The air as it touches them, their place in space. You are currently operating a body that is an incredible machine. You are the god of a kingdom of specialized cells, waiting to navigate you around earth. Notice your environment. How cool is it that you are here? It is objectively interesting that you exist in this space, right now.

If you can follow this, and legitimately appreciate it, you can re-center yourself in any moment. Any moment you feel overwhelmed or anxious, you can bring it back to the incredible-ness of existence, and build on that. Suddenly, the fact that you are in this stressful meeting with the CEO is really, really cool. It's not a scary thing, it's a remarkable experience.

Any time I catch myself feeling tense or tight, I realize that I'm caught up in some dumb thought process that is highly critical. I come back to how cool it is that I'm here, and then that I'm having whatever experience I'm having. And, man, I am super grateful.

I'm really lucky.

Best of luck changing the ratio, and Happy Thanksgiving!

Being Grateful | Jimmy Hooker

Being Grateful

Art by Albrecht Dürer

Art by Albrecht Dürer

I think I've been trying to figure out myself and my relationship to the world since I was about 17. About 20 years of introspection. That's... maybe too much.

I got into spiritual stuff when I was pretty young because I was deeply anxious and it impeded a lot of what I wanted to do and how I interacted with people. Some of this had to do with my parents, but some was probably built in. I thought if I figured myself out, I could finally live as my true self without anxiety. But it never really worked out, despite throwing a lot of weaponry at it, including therapy, intensive meditation, supplements, exercise, and more. Those things helped, but it usually felt like I was dealing with symptoms rather than the core of the thing.

There are a number of studies that extol the virtues of gratitude. Start a gratitude journal! It will make you notably happier! But that always felt like work to me. It made gratitude into a job, and so, even after trying many times, I tended to avoid it. I didn't disagree with the ideas, whenever I stepped back I realized my life was really pretty great. I just couldn't make it a consistent habit.

But one thing I've done recently has changed this. Pretty dramatically in my experience.

I spend a lot of time being critical. I'd go so far as to say 999 out of a 1000 thoughts are critical. I am constantly noticing things that are out of place, that could be improved, that are bad, that are dumb. This is hugely useful in a working context, it makes you detail oriented and motivated to improve things. But if you can't shut that voice off, it becomes overwhelming and exhausting, coloring your whole experience. If you are almost only noticing the bad things, life seems... pretty bad!

But how do you change the ratio? How do you get to, let's be optimistic, and say, 5% grateful/positive thoughts? 5 thoughts out of 100.

In the past, I would try and convince myself that life was good. But it felt fake. I was already convinced that things were shit, so saying things like "life is amazing" felt false. Like I was a bad salesman trying to pitch an unimpressed customer.

But a recent realization has changed this. You just have to go to the extreme, foundational basics. You have to get down to the sensory experience. Breathing is... cool actually?

Stay with me.

Right now, you are alive. The fact that you are alive is remarkable, actually. Feel your hands, notice the sensations surrounding them. The air as it touches them, their place in space. You are currently operating a body that is an incredible machine. You are the god of a kingdom of specialized cells, waiting to navigate you around earth. Notice your environment. How cool is it that you are here? It is objectively interesting that you exist in this space, right now.

If you can follow this, and legitimately appreciate it, you can re-center yourself in any moment. Any moment you feel overwhelmed or anxious, you can bring it back to the incredible-ness of existence, and build on that. Suddenly, the fact that you are in this stressful meeting with the CEO is really, really cool. It's not a scary thing, it's a remarkable experience.

Any time I catch myself feeling tense or tight, I realize that I'm caught up in some dumb thought process that is highly critical. I come back to how cool it is that I'm here, and then that I'm having whatever experience I'm having. And, man, I am super grateful.

I'm really lucky.

Best of luck changing the ratio, and Happy Thanksgiving!

\ No newline at end of file + if(true) { + window.dataLayer = window.dataLayer || []; + function gtag(){window.dataLayer && window.dataLayer.push(arguments);} + gtag('js', new Date()); + + gtag('config', 'G-4HPC5TT803', {"send_page_view":false}); + } + \ No newline at end of file diff --git a/blog/debugging-tartine-sourdough/index.html b/blog/debugging-tartine-sourdough/index.html index d4fa6e7..0b83af1 100644 --- a/blog/debugging-tartine-sourdough/index.html +++ b/blog/debugging-tartine-sourdough/index.html @@ -1,17 +1,11 @@ -Debugging Tartine Bread | Jimmy Hooker

Debugging Tartine Bread

Recipe for the uninitiated: Tartine Country Loaf. The book is more detailed.

I used to live 2 blocks from Tartine in the Mission of SF. A year ago we moved to the East Bay to seek a more, let's say, conducive atmosphere for our daughter to grow up in. But I missed having easy access to good bread! That is what got me started down this dark path.

I gotta say, this journey has been rough. Many times I would think I'd figured it out, only to then have a bad loaf and not understand what I did wrong. Things just seemed highly unpredictable. I watched tons of YouTube, read this forum, read the Sourdough Journey, the Bread Code, the Tartine book, Flour Water Salt Yeast, you get it. There was a lot of frustration, but I am an obsessive person by nature and I couldn't stop trying to figure it out. At one point I was baking 3 batches a week to speed up my understanding of what was/wasn't working. I've easily baked > 100 batches in the last year.

A lot of advice was really hard to parse. I texted with an award-winning baker in Berkeley and tried to get help on internet forums (including this one), but it was really hard to figure out which variables were off or which piece of advice was right. Was it my starter? Was it my bulk fermentation time? Was the levain just not active enough when I started the dough? Was I just too rough with shaping? Changing one thing seemed to have an effect, but then it would stop for some reason, and I wasn't sure whether to keep the change, revert the change, or change it again.

I've finally been able to consistently produce that bottom loaf. No bad loaves anymore. And these two things nailed it for me:

  • Using way more than the recommended amount of starter in the levain. Like, 30-45 grams of starter. I think this is the true key that unlocked way better loaves. If you remember anything from this, it's to just use more starter in your levain!
  • Starting with 98-100 degree water for bulk fermentation (our house is about 70 degrees). Because the levain was at ~70 degrees, it would bring water temp down a ton when mixed in. Then, fermantalyse inside the mixing bowl would bring the temp down even more.

Just those two things have made it so I get a consistent loaf in 3.5 to 4.5 hours of bulk fermentation.

Things that didn't work:

  • Letting bulk fermentation go on much longer, like 6-8 hours (since I was getting underfermented loaves). For some reason this just produced weird loaves that would exhibit characteristics of under fermentation (caverns), but would also be impossible to work with like an over fermented loaf (ie the gluten had broken down and they were weirdly sticky and wet)
  • Feeding my starter every 12 hours (or more). This just sucked, was a huge hassle, felt wasteful, and didn't meaningfully matter in my experience. Goal was to get it super active, and I guess that worked, but it was so much overhead. Super annoying.
  • Doing it by hand instead of using a KitchenAid mixer. I got some advice that maybe this was breaking down the gluten formation too much after fermantalyse. Didn't seem to matter, that said I got in the habit of doing it by hand after fermantalyse because of that debugging journey.
  • Being super gentle with the dough, especially toward the end of bulk fermentation and shaping. I actually can't say this did nothing, but the loaves were so inconsistent that this was like the equivalent of buying $300 running shoes when you just took up the hobby. It's not what's going to really move the needle at that level.

In hindsight, I think some of the variability I was getting was due to a combination of starter activeness and the amount of starter I was mixing into the leavin. I just had no idea those were the key things. I also, especially in the beginning, just used a 'tablespoon', as described in the book. But, I wasn't careful about measuring that out and I assume would get 'lucky' occasionally when putting in more starter and get a better loaf.

Otherwise, I pretty much stick to the recipe as described. Here is what I do:

Starter Maintenance:

  • House is ~70 degrees Fahrenheit.
  • 50g water, 50g 1:1 bread flour/wheat flour combo every 24 hours, always feed in the morning. Dump everything but 2 or so tablespoons of starter, maybe 30g.

Making Levain:

  • Mix 30-45g of starter (depending on how active it seems to be) with 200g 1:1 bread flour/wheat mixture and 200g of 78-80 degree water.
  • Wait 12 hours

Making dough:

  • Turn the oven light on (this brings the oven temp up to around 83-84 degrees, which is a nice temp to bulk ferment at).
  • Mix levain and flour/water in KitchenAid stand mixer. Use 98-102 degree water. Mix for a couple minutes on setting 2.
  • Wait 30 minutes for fermantalyse to happen.
  • Mix in 20g of Salt with 50g of 90-100 degree water (this seems to matter less, I've used 82 degree water and it was fine, but you're just looking to maintain temp). I now use my hands, but I'm pretty confident you could use a stand mixer with a dough hook with no ill results.
  • At this point the dough is often around 82 degrees, maybe 84. In my experience it will keep dropping.
  • Put the dough in a Cambro container, then put it in the warm oven to maintain temp
  • Do the stretch & folds like normal every 30 minutes
  • After 3.5 to 4.5 hours, it should be doming. On the Cambro, sometimes the dough will show as being at or above the 2 liter line, but not always. As long as it's doming, it seems good in my experience.
  • Shape into rounds, wait 30 min
  • Shape into loaves and drop into bannetons.

Overnight Retard:

I throw a tea towel over top the bannetons and toss in the fridge for what usually ends up being ~17 hours (I start the dough process in the morning and end by around 3pm). I've also used saran wrap to not develop a 'skin', but the effects of this don't seem meaningful to me. I often bake one loaf, then wait another 2 days to bake the other one. It's still great! The fridge seems to do more than slow it down, it seems to basically stop fermentation in my experience. Maybe my fridge is on the colder side.

Baking:

  • Place a cookie sheet in the bottom rack to prevent the bottom of the dutch oven from getting too hot and burning the loaf
  • Set oven to 500 and wait for it to get to temp
  • Pull out the dutch oven, rice flour the bottom and toss in the loaf direct from the fridge. The coldness of the loaf makes no difference in my experience to letting it sit out while the oven pre-heats. It's also easier to cut with a razer when it's cold.
  • Score the loaf at a 45 degree angle to get that nice lip
  • Bring oven temp to 470 and bake for 20 minutes lid on
  • Take lid off dutch oven and bake for another 30 minutes

I hope this helps someone who was as frustrated as I was, and helps them get a bit less lost. Just use more starter in your levain!

Debugging Tartine Bread | Jimmy Hooker

Debugging Tartine Bread

Recipe for the uninitiated: Tartine Country Loaf. The book is more detailed.

I used to live 2 blocks from Tartine in the Mission of SF. A year ago we moved to the East Bay to seek a more, let's say, conducive atmosphere for our daughter to grow up in. But I missed having easy access to good bread! That is what got me started down this dark path.

I gotta say, this journey has been rough. Many times I would think I'd figured it out, only to then have a bad loaf and not understand what I did wrong. Things just seemed highly unpredictable. I watched tons of YouTube, read this forum, read the Sourdough Journey, the Bread Code, the Tartine book, Flour Water Salt Yeast, you get it. There was a lot of frustration, but I am an obsessive person by nature and I couldn't stop trying to figure it out. At one point I was baking 3 batches a week to speed up my understanding of what was/wasn't working. I've easily baked > 100 batches in the last year.

A lot of advice was really hard to parse. I texted with an award-winning baker in Berkeley and tried to get help on internet forums (including this one), but it was really hard to figure out which variables were off or which piece of advice was right. Was it my starter? Was it my bulk fermentation time? Was the levain just not active enough when I started the dough? Was I just too rough with shaping? Changing one thing seemed to have an effect, but then it would stop for some reason, and I wasn't sure whether to keep the change, revert the change, or change it again.

I've finally been able to consistently produce that bottom loaf. No bad loaves anymore. And these two things nailed it for me:

  • Using way more than the recommended amount of starter in the levain. Like, 30-45 grams of starter. I think this is the true key that unlocked way better loaves. If you remember anything from this, it's to just use more starter in your levain!
  • Starting with 98-100 degree water for bulk fermentation (our house is about 70 degrees). Because the levain was at ~70 degrees, it would bring water temp down a ton when mixed in. Then, fermantalyse inside the mixing bowl would bring the temp down even more.

Just those two things have made it so I get a consistent loaf in 3.5 to 4.5 hours of bulk fermentation.

Things that didn't work:

  • Letting bulk fermentation go on much longer, like 6-8 hours (since I was getting underfermented loaves). For some reason this just produced weird loaves that would exhibit characteristics of under fermentation (caverns), but would also be impossible to work with like an over fermented loaf (ie the gluten had broken down and they were weirdly sticky and wet)
  • Feeding my starter every 12 hours (or more). This just sucked, was a huge hassle, felt wasteful, and didn't meaningfully matter in my experience. Goal was to get it super active, and I guess that worked, but it was so much overhead. Super annoying.
  • Doing it by hand instead of using a KitchenAid mixer. I got some advice that maybe this was breaking down the gluten formation too much after fermantalyse. Didn't seem to matter, that said I got in the habit of doing it by hand after fermantalyse because of that debugging journey.
  • Being super gentle with the dough, especially toward the end of bulk fermentation and shaping. I actually can't say this did nothing, but the loaves were so inconsistent that this was like the equivalent of buying $300 running shoes when you just took up the hobby. It's not what's going to really move the needle at that level.

In hindsight, I think some of the variability I was getting was due to a combination of starter activeness and the amount of starter I was mixing into the leavin. I just had no idea those were the key things. I also, especially in the beginning, just used a 'tablespoon', as described in the book. But, I wasn't careful about measuring that out and I assume would get 'lucky' occasionally when putting in more starter and get a better loaf.

Otherwise, I pretty much stick to the recipe as described. Here is what I do:

Starter Maintenance:

  • House is ~70 degrees Fahrenheit.
  • 50g water, 50g 1:1 bread flour/wheat flour combo every 24 hours, always feed in the morning. Dump everything but 2 or so tablespoons of starter, maybe 30g.

Making Levain:

  • Mix 30-45g of starter (depending on how active it seems to be) with 200g 1:1 bread flour/wheat mixture and 200g of 78-80 degree water.
  • Wait 12 hours

Making dough:

  • Turn the oven light on (this brings the oven temp up to around 83-84 degrees, which is a nice temp to bulk ferment at).
  • Mix levain and flour/water in KitchenAid stand mixer. Use 98-102 degree water. Mix for a couple minutes on setting 2.
  • Wait 30 minutes for fermantalyse to happen.
  • Mix in 20g of Salt with 50g of 90-100 degree water (this seems to matter less, I've used 82 degree water and it was fine, but you're just looking to maintain temp). I now use my hands, but I'm pretty confident you could use a stand mixer with a dough hook with no ill results.
  • At this point the dough is often around 82 degrees, maybe 84. In my experience it will keep dropping.
  • Put the dough in a Cambro container, then put it in the warm oven to maintain temp
  • Do the stretch & folds like normal every 30 minutes
  • After 3.5 to 4.5 hours, it should be doming. On the Cambro, sometimes the dough will show as being at or above the 2 liter line, but not always. As long as it's doming, it seems good in my experience.
  • Shape into rounds, wait 30 min
  • Shape into loaves and drop into bannetons.

Overnight Retard:

I throw a tea towel over top the bannetons and toss in the fridge for what usually ends up being ~17 hours (I start the dough process in the morning and end by around 3pm). I've also used saran wrap to not develop a 'skin', but the effects of this don't seem meaningful to me. I often bake one loaf, then wait another 2 days to bake the other one. It's still great! The fridge seems to do more than slow it down, it seems to basically stop fermentation in my experience. Maybe my fridge is on the colder side.

Baking:

  • Place a cookie sheet in the bottom rack to prevent the bottom of the dutch oven from getting too hot and burning the loaf
  • Set oven to 500 and wait for it to get to temp
  • Pull out the dutch oven, rice flour the bottom and toss in the loaf direct from the fridge. The coldness of the loaf makes no difference in my experience to letting it sit out while the oven pre-heats. It's also easier to cut with a razer when it's cold.
  • Score the loaf at a 45 degree angle to get that nice lip
  • Bring oven temp to 470 and bake for 20 minutes lid on
  • Take lid off dutch oven and bake for another 30 minutes

I hope this helps someone who was as frustrated as I was, and helps them get a bit less lost. Just use more starter in your levain!

\ No newline at end of file + if(true) { + window.dataLayer = window.dataLayer || []; + function gtag(){window.dataLayer && window.dataLayer.push(arguments);} + gtag('js', new Date()); + + gtag('config', 'G-4HPC5TT803', {"send_page_view":false}); + } + \ No newline at end of file diff --git a/blog/design-cant-always-be-rationalized/index.html b/blog/design-cant-always-be-rationalized/index.html index 5b85c3c..fa8706a 100644 --- a/blog/design-cant-always-be-rationalized/index.html +++ b/blog/design-cant-always-be-rationalized/index.html @@ -1,17 +1,11 @@ -Rationalizing design | Jimmy Hooker

Rationalizing design

Photo by Soumik Dey

Photo by Soumik Dey

One of the things that got design a seat at the table was data-driven decision making that could be defended. I don't have any evidence to support this, but I really think that design's difficulty in justifying itself to engineering is why products in technology had pretty poor design until the last 10 years.

Apple really changed this around the time of the iPhone. It was as if suddenly everyone awoke from their self-imposed stupor and realized that design is meaningful to people. Further, people could prove it by creating well designed applications for the AppStore and watching them win. While many in engineering wrote off Apple as just being a good marketer to idiots who didn't care what was in their pocket (ask anyone over 30 about this), over time the appreciation for polished, thoughtful design and experience became mainstream among many companies.

So that's awesome right? People finally appreciate designers! However, one big problem I have regarding this nice little seat at the table is defensibility. I know, I know, not a popular thing to say nowadays. Let's explore.

Asana, Twitter, Google, Pinterest, Yelp, Amazon and many more all have retained their early ancestral structure that have been polished and refined over time. Why? If data was so important to design, shouldn't we have realized those early, non-data-driven approaches were wrong? Shouldn't the data suggest that we do something entirely different? Did the original designer or team just, gasp, have a good intuitive sense about things??

One thing that really brought this to the forefront of my mind was an article about how Notion saved itself from death.

Ivan talks about how he spent upwards of 18 hours a day designing. When you are iterating at that rate, you're working off of intuition and judgement, plain and simple. Sure, he spent time talking through his ideas with Simon, but there wasn't a written out plan for the design that he then just executed. Instead he made tons of versions, evaluated them using his and Simon's judgement and intuition, and chose what felt like the best choice.

“If the taste is there, which you can grow by studying typography and the giants of industry, you can churn out versions until you find the best one.” - Ivan

I know. Feelings. Damn.

We, as designers, sometimes just know that something is good. It feels good. We design a ton of versions, and through exposing that design to both our and our teammates judgement we file off the rough edges, massage it, and finally get to the point where it feels 'inevitable'.

How do you defend that? How do you justify or rationalize it?

{% include image.html url="/images/unsplash-artist-description-card.jpg" %}

It reminds me of those little cards that artists put next to their paintings, explaining their intentions. Why is this necessary? Isn't this superfluous? I understand designers are problem solvers, not artists, but can it all be explained? Should it all be explained? Further, if you do explain it, isn't it just hindsight analysis? What's the point? Isn't it just performative?

Another metaphor: Comedy. How do you rationalize or justify a joke? Jokes are forged through judgement and experience. Whether or not they were good is decided by the audience.

Good design, like good art, stands on its own. It needs no defense. It either works or it doesn't.

Rationalizing design | Jimmy Hooker

Rationalizing design

Photo by Soumik Dey

Photo by Soumik Dey

One of the things that got design a seat at the table was data-driven decision making that could be defended. I don't have any evidence to support this, but I really think that design's difficulty in justifying itself to engineering is why products in technology had pretty poor design until the last 10 years.

Apple really changed this around the time of the iPhone. It was as if suddenly everyone awoke from their self-imposed stupor and realized that design is meaningful to people. Further, people could prove it by creating well designed applications for the AppStore and watching them win. While many in engineering wrote off Apple as just being a good marketer to idiots who didn't care what was in their pocket (ask anyone over 30 about this), over time the appreciation for polished, thoughtful design and experience became mainstream among many companies.

So that's awesome right? People finally appreciate designers! However, one big problem I have regarding this nice little seat at the table is defensibility. I know, I know, not a popular thing to say nowadays. Let's explore.

Asana, Twitter, Google, Pinterest, Yelp, Amazon and many more all have retained their early ancestral structure that have been polished and refined over time. Why? If data was so important to design, shouldn't we have realized those early, non-data-driven approaches were wrong? Shouldn't the data suggest that we do something entirely different? Did the original designer or team just, gasp, have a good intuitive sense about things??

One thing that really brought this to the forefront of my mind was an article about how Notion saved itself from death.

Ivan talks about how he spent upwards of 18 hours a day designing. When you are iterating at that rate, you're working off of intuition and judgement, plain and simple. Sure, he spent time talking through his ideas with Simon, but there wasn't a written out plan for the design that he then just executed. Instead he made tons of versions, evaluated them using his and Simon's judgement and intuition, and chose what felt like the best choice.

“If the taste is there, which you can grow by studying typography and the giants of industry, you can churn out versions until you find the best one.” - Ivan

I know. Feelings. Damn.

We, as designers, sometimes just know that something is good. It feels good. We design a ton of versions, and through exposing that design to both our and our teammates judgement we file off the rough edges, massage it, and finally get to the point where it feels 'inevitable'.

How do you defend that? How do you justify or rationalize it?

{% include image.html url="/images/unsplash-artist-description-card.jpg" %}

It reminds me of those little cards that artists put next to their paintings, explaining their intentions. Why is this necessary? Isn't this superfluous? I understand designers are problem solvers, not artists, but can it all be explained? Should it all be explained? Further, if you do explain it, isn't it just hindsight analysis? What's the point? Isn't it just performative?

Another metaphor: Comedy. How do you rationalize or justify a joke? Jokes are forged through judgement and experience. Whether or not they were good is decided by the audience.

Good design, like good art, stands on its own. It needs no defense. It either works or it doesn't.

\ No newline at end of file + if(true) { + window.dataLayer = window.dataLayer || []; + function gtag(){window.dataLayer && window.dataLayer.push(arguments);} + gtag('js', new Date()); + + gtag('config', 'G-4HPC5TT803', {"send_page_view":false}); + } + \ No newline at end of file diff --git a/blog/design-for-repetition/index.html b/blog/design-for-repetition/index.html index 318d612..651e87a 100644 --- a/blog/design-for-repetition/index.html +++ b/blog/design-for-repetition/index.html @@ -1,17 +1,11 @@ -Design for Repetition | Jimmy Hooker

Design for Repetition

Photo by Leonel Fernandez

Photo by Leonel Fernandez

After building multiple applications used by thousands of paying customers I feel like I've converged on a philosophy of design that could be summed up as 'Design for Repetition'.

Is your app used a lot? Or a little? Is your app meant to be engaging? Or are people supposed to get in and out?

A lot of design decisions can be solved by understanding not just the way your user wants to interact with your application, but by how often.

With Badger, our customers interact with the app many times during a workday and perform a few interactions almost constantly. With all this repetition, our goals converged on the following:

  • Get the hell out of the way
  • Make it really easy to undo/redo actions
  • Reduce steps, confirmation, and any explicit actions as much as possible
  • Don't hide buttons (for example, on hover-state)

With enough repetition you barely had to pay attention to what you were doing within our application. Making mistakes was a non-issue, you could quickly undo/redo whatever action had occurred. Did they upload bad data? No big deal, they can undo that. Did they mess up their route? No worries, they can quickly clear and re-add items through multiple points of entry intuitively and in seconds. Did they click the wrong item during a check-in? Just click again and choose the new option.

This creates strong feedback loops where poeple ultimately feel safe in the app. Instead of something that might betray their trust, it becomes an extension of them.

Think about any tool in a woodshop. The tool is designed for constant, repetitive actions, so that you can rely on it, you can trust it. With enough time, you don't have to think about it.

Make software that is designed for repetition.

Design for Repetition | Jimmy Hooker

Design for Repetition

Photo by Leonel Fernandez

Photo by Leonel Fernandez

After building multiple applications used by thousands of paying customers I feel like I've converged on a philosophy of design that could be summed up as 'Design for Repetition'.

Is your app used a lot? Or a little? Is your app meant to be engaging? Or are people supposed to get in and out?

A lot of design decisions can be solved by understanding not just the way your user wants to interact with your application, but by how often.

With Badger, our customers interact with the app many times during a workday and perform a few interactions almost constantly. With all this repetition, our goals converged on the following:

  • Get the hell out of the way
  • Make it really easy to undo/redo actions
  • Reduce steps, confirmation, and any explicit actions as much as possible
  • Don't hide buttons (for example, on hover-state)

With enough repetition you barely had to pay attention to what you were doing within our application. Making mistakes was a non-issue, you could quickly undo/redo whatever action had occurred. Did they upload bad data? No big deal, they can undo that. Did they mess up their route? No worries, they can quickly clear and re-add items through multiple points of entry intuitively and in seconds. Did they click the wrong item during a check-in? Just click again and choose the new option.

This creates strong feedback loops where poeple ultimately feel safe in the app. Instead of something that might betray their trust, it becomes an extension of them.

Think about any tool in a woodshop. The tool is designed for constant, repetitive actions, so that you can rely on it, you can trust it. With enough time, you don't have to think about it.

Make software that is designed for repetition.

\ No newline at end of file + if(true) { + window.dataLayer = window.dataLayer || []; + function gtag(){window.dataLayer && window.dataLayer.push(arguments);} + gtag('js', new Date()); + + gtag('config', 'G-4HPC5TT803', {"send_page_view":false}); + } + \ No newline at end of file diff --git a/blog/feedback-loops-momentum-excitement/index.html b/blog/feedback-loops-momentum-excitement/index.html index c1b7763..5b4cf6a 100644 --- a/blog/feedback-loops-momentum-excitement/index.html +++ b/blog/feedback-loops-momentum-excitement/index.html @@ -1,17 +1,11 @@ -Feedback Loops, Momentum, and Excitement | Jimmy Hooker

Feedback Loops, Momentum, and Excitement

Photo by Marco Mons

Photo by Marco Mons

When building products, there are two things that seem extremely important: scope and speed to feedback.

If you can ruthlessly prioritize, you can focus. If you can tighten your feedback loops, you can iterate faster. Both of these things will allow you to move very fast in the right direction. But more than that, James Somers talks about the perceived weight of work being reduced dramatically by working quickly in general: Speed Matters

In essence, if something takes a long time, you will avoid it. If it happens quickly, you will do more of it.

The same thing is true of the end-user's experience of the software itself. If they can focus and the software feels fast, they will use it more. The pleasure of using it makes you want to use it more. Craig Mod writes about this beautifully here: Fast Software, the Best Software

While reading up on feedback loops I ran across Andrew Certain's explanation of how communication and consensus can cripple the feedback loops that allow software to be developed faster. In order to increase speed at Amazon, Bezos basically said to ditch the idea of avoiding duplication of effort in order to execute faster (read the whole thread!):

The cost of communication, transparency, and consensus was so high that it wasn't worth the effort. It was actually better to get rid of cross-team communication and heavily segment the application in order to ship new products faster. Werner Vogels, Amazon's CTO goes into more detail here: Modern Applications at AWS.

This is an almost heretical idea on its face and goes against a core tenant of software development: Don't repeat yourself!

The ideas of 'fail fast' and 'move fast and break things' are now widely satirized. However, I think they're still deeply true. Moving fast is exciting, just look at any new burgeoning software framework. People love to get involved in projects that really feel fast. They're rewarding and there's the most opportunity to really stand out and feel like your contribution matters.

Anything that slows you down should be looked at with skepticism and there should be insanely high value associated with the trade-off.

Feedback Loops, Momentum, and Excitement | Jimmy Hooker

Feedback Loops, Momentum, and Excitement

Photo by Marco Mons

Photo by Marco Mons

When building products, there are two things that seem extremely important: scope and speed to feedback.

If you can ruthlessly prioritize, you can focus. If you can tighten your feedback loops, you can iterate faster. Both of these things will allow you to move very fast in the right direction. But more than that, James Somers talks about the perceived weight of work being reduced dramatically by working quickly in general: Speed Matters

In essence, if something takes a long time, you will avoid it. If it happens quickly, you will do more of it.

The same thing is true of the end-user's experience of the software itself. If they can focus and the software feels fast, they will use it more. The pleasure of using it makes you want to use it more. Craig Mod writes about this beautifully here: Fast Software, the Best Software

While reading up on feedback loops I ran across Andrew Certain's explanation of how communication and consensus can cripple the feedback loops that allow software to be developed faster. In order to increase speed at Amazon, Bezos basically said to ditch the idea of avoiding duplication of effort in order to execute faster (read the whole thread!):

The cost of communication, transparency, and consensus was so high that it wasn't worth the effort. It was actually better to get rid of cross-team communication and heavily segment the application in order to ship new products faster. Werner Vogels, Amazon's CTO goes into more detail here: Modern Applications at AWS.

This is an almost heretical idea on its face and goes against a core tenant of software development: Don't repeat yourself!

The ideas of 'fail fast' and 'move fast and break things' are now widely satirized. However, I think they're still deeply true. Moving fast is exciting, just look at any new burgeoning software framework. People love to get involved in projects that really feel fast. They're rewarding and there's the most opportunity to really stand out and feel like your contribution matters.

Anything that slows you down should be looked at with skepticism and there should be insanely high value associated with the trade-off.

\ No newline at end of file + if(true) { + window.dataLayer = window.dataLayer || []; + function gtag(){window.dataLayer && window.dataLayer.push(arguments);} + gtag('js', new Date()); + + gtag('config', 'G-4HPC5TT803', {"send_page_view":false}); + } + \ No newline at end of file diff --git a/blog/how-to-deliver-good-presentations/index.html b/blog/how-to-deliver-good-presentations/index.html index 1f5754a..2e13a64 100644 --- a/blog/how-to-deliver-good-presentations/index.html +++ b/blog/how-to-deliver-good-presentations/index.html @@ -1,17 +1,11 @@ -How to Deliver Good Presentations | Jimmy Hooker

How to Deliver Good Presentations

Photo by Andrew Seaman

Photo by Andrew Seaman

This is actually pretty simple to do well, but you really have to put in the practice time. Don't kid yourself, it is humiliating to fuck up a presentation because you didn't practice enough. Unforced Error. Here's how to do it.

  1. Do an ‘off the cuff’ version with no slides, or minimal slides.
  2. Experience the visceral terror of how bad the presentation will be if you do it ‘off the cuff’ or unpracticed.
  3. Do it a couple more times to really fire up the ‘presentation’ neurons
  4. Write down what you want to say like it was a speech, literally word for word what you want to say to the audience.
  5. Practice the written down version a couple times
  6. Edit it heavily
  7. Practice 5-10 more times, making little adjustments, until you can mostly do it without looking at your text

Voila. Your presentation will most likely not suck, and you will have a confident handle on the material so that you can dynamically deliver it in front of an audience with a relative sense of comfort.

How to Deliver Good Presentations | Jimmy Hooker

How to Deliver Good Presentations

Photo by Andrew Seaman

Photo by Andrew Seaman

This is actually pretty simple to do well, but you really have to put in the practice time. Don't kid yourself, it is humiliating to fuck up a presentation because you didn't practice enough. Unforced Error. Here's how to do it.

  1. Do an ‘off the cuff’ version with no slides, or minimal slides.
  2. Experience the visceral terror of how bad the presentation will be if you do it ‘off the cuff’ or unpracticed.
  3. Do it a couple more times to really fire up the ‘presentation’ neurons
  4. Write down what you want to say like it was a speech, literally word for word what you want to say to the audience.
  5. Practice the written down version a couple times
  6. Edit it heavily
  7. Practice 5-10 more times, making little adjustments, until you can mostly do it without looking at your text

Voila. Your presentation will most likely not suck, and you will have a confident handle on the material so that you can dynamically deliver it in front of an audience with a relative sense of comfort.

\ No newline at end of file + if(true) { + window.dataLayer = window.dataLayer || []; + function gtag(){window.dataLayer && window.dataLayer.push(arguments);} + gtag('js', new Date()); + + gtag('config', 'G-4HPC5TT803', {"send_page_view":false}); + } + \ No newline at end of file diff --git a/blog/how-to-not-be-cynical/index.html b/blog/how-to-not-be-cynical/index.html index 992b9d2..62b49cc 100644 --- a/blog/how-to-not-be-cynical/index.html +++ b/blog/how-to-not-be-cynical/index.html @@ -1,17 +1,11 @@ -How to Not Be Cynical | Jimmy Hooker

How to Not Be Cynical

Photo by Joel Holland

Photo by Joel Holland

I was watching YouTube and I ran into this remarkable interview:

Ruby is incredible. It's clear that she is just a force of nature. She exudes life at the age of 87, and as 'limber as a dishrag', she just seems invigorated.

I've often been accused of being overly critical. I like to think I am, on the overall, a pretty optimistic person. I feel like most problems are solvable and that life is getting better and better. This period of time is, imo, one of the most exciting to be alive.

On the one hand, my critical nature has given me an eye for details and a high bar that I set for myself. On the other, I do worry that my critical nature can often bleed into and tinge experiences that could otherwise be really pleasant and enjoyable. It's very much so a double-edged sword.

My wife Sara is much more even keeled. She has a balanced nature, is generally happy, and often brings joy to those who spend time in her company. She balances me, and often makes me realize that I need to chill a little bit. Maybe enjoy the short time we have here.

I want to write about this a bit more, but @visakanv and @nickcammarata have been some of my favorite follows more recently because of their incredible focus on positivity in a way that feels legible to me. Visa often encourages frame changes, while Nick focuses on meditation and qualia.

Can you be critical without being cynical? Without becoming overly judgmental?

It feels like a difficult tight rope to walk.

Life is amazing! It's incredible we get this opportunity!

I hope I don't waste too much of it focusing on the wrong things.

How to Not Be Cynical | Jimmy Hooker

How to Not Be Cynical

Photo by Joel Holland

Photo by Joel Holland

I was watching YouTube and I ran into this remarkable interview:

Ruby is incredible. It's clear that she is just a force of nature. She exudes life at the age of 87, and as 'limber as a dishrag', she just seems invigorated.

I've often been accused of being overly critical. I like to think I am, on the overall, a pretty optimistic person. I feel like most problems are solvable and that life is getting better and better. This period of time is, imo, one of the most exciting to be alive.

On the one hand, my critical nature has given me an eye for details and a high bar that I set for myself. On the other, I do worry that my critical nature can often bleed into and tinge experiences that could otherwise be really pleasant and enjoyable. It's very much so a double-edged sword.

My wife Sara is much more even keeled. She has a balanced nature, is generally happy, and often brings joy to those who spend time in her company. She balances me, and often makes me realize that I need to chill a little bit. Maybe enjoy the short time we have here.

I want to write about this a bit more, but @visakanv and @nickcammarata have been some of my favorite follows more recently because of their incredible focus on positivity in a way that feels legible to me. Visa often encourages frame changes, while Nick focuses on meditation and qualia.

Can you be critical without being cynical? Without becoming overly judgmental?

It feels like a difficult tight rope to walk.

Life is amazing! It's incredible we get this opportunity!

I hope I don't waste too much of it focusing on the wrong things.

\ No newline at end of file + if(true) { + window.dataLayer = window.dataLayer || []; + function gtag(){window.dataLayer && window.dataLayer.push(arguments);} + gtag('js', new Date()); + + gtag('config', 'G-4HPC5TT803', {"send_page_view":false}); + } + \ No newline at end of file diff --git a/blog/how-to-not-be-full-of-shit-as-a-product-manager/index.html b/blog/how-to-not-be-full-of-shit-as-a-product-manager/index.html index 1214b1b..d63f73c 100644 --- a/blog/how-to-not-be-full-of-shit-as-a-product-manager/index.html +++ b/blog/how-to-not-be-full-of-shit-as-a-product-manager/index.html @@ -1,17 +1,11 @@ -How to Not Be Full of Shit as a Product Manager | Jimmy Hooker

How to Not Be Full of Shit as a Product Manager

Uncut Gems

Uncut Gems

So you’ve joined a new team, company, etc and you are experiencing the cold terror that is your new responsibility area as a product manager.

I think you have about 90 days to demonstrate that you are going to be a net positive addition to the team and to build up trust. What do you do?

This assumes:

  • The product already exists
  • There are existing users

My opinion on this is you need to:

  • Get ahold of as many customers as possible within the next 30 days and have them walk you through their product usage and your product feature in particular. Have them share their screen while they do this. Write out your questions loosely beforehand, and adjust rapidly with the first few customers until you get a feel for which questions elicit the best answers. Record everything, and then tag key statements around pain points, etc, with a tool like Dovetail.
  • Insist on involving the engineering manager and designer associated with your product area in the customer discussions. This has multiple benefits, including demonstrating your ability to understand customer pain points and developing shared context between your key stakeholders.
  • Learn where all the bodies are buried in the data. Do not rely on the data team, you need to get familiar with querying raw data. Know the key tables and how to produce good output.
  • Manage your stress. Exercise, take supplements, and sleep enough that you don’t freak out. If you are doing this right, the first 90-days should feel like watching ‘Uncut Gems’ nonstop. Act accordingly.
  • Ignore everything that is not the above. No one will really care that you followed whatever process/ritual properly if your product area doesn’t deliver. Let people chase you down and yell at you for things that aren’t really consequential. You need to hold your responsibility to your product area above all else. That doesn’t mean you need to be unnecessarily rude or obnoxious, but it’s ok to drop balls that don’t matter.

That’s it. I think the last point is worth really worth emphasizing. It’s really easy to get caught up in exercises that feel useful but don’t solve real problems. Do not be the person who has nice narratives but isn’t driving value. Understand your product area through customers, include your key stakeholders, build an aligned plan, and articulate it to leadership. That is all that matters.

Best of luck!

How to Not Be Full of Shit as a Product Manager | Jimmy Hooker

How to Not Be Full of Shit as a Product Manager

Uncut Gems

Uncut Gems

So you’ve joined a new team, company, etc and you are experiencing the cold terror that is your new responsibility area as a product manager.

I think you have about 90 days to demonstrate that you are going to be a net positive addition to the team and to build up trust. What do you do?

This assumes:

  • The product already exists
  • There are existing users

My opinion on this is you need to:

  • Get ahold of as many customers as possible within the next 30 days and have them walk you through their product usage and your product feature in particular. Have them share their screen while they do this. Write out your questions loosely beforehand, and adjust rapidly with the first few customers until you get a feel for which questions elicit the best answers. Record everything, and then tag key statements around pain points, etc, with a tool like Dovetail.
  • Insist on involving the engineering manager and designer associated with your product area in the customer discussions. This has multiple benefits, including demonstrating your ability to understand customer pain points and developing shared context between your key stakeholders.
  • Learn where all the bodies are buried in the data. Do not rely on the data team, you need to get familiar with querying raw data. Know the key tables and how to produce good output.
  • Manage your stress. Exercise, take supplements, and sleep enough that you don’t freak out. If you are doing this right, the first 90-days should feel like watching ‘Uncut Gems’ nonstop. Act accordingly.
  • Ignore everything that is not the above. No one will really care that you followed whatever process/ritual properly if your product area doesn’t deliver. Let people chase you down and yell at you for things that aren’t really consequential. You need to hold your responsibility to your product area above all else. That doesn’t mean you need to be unnecessarily rude or obnoxious, but it’s ok to drop balls that don’t matter.

That’s it. I think the last point is worth really worth emphasizing. It’s really easy to get caught up in exercises that feel useful but don’t solve real problems. Do not be the person who has nice narratives but isn’t driving value. Understand your product area through customers, include your key stakeholders, build an aligned plan, and articulate it to leadership. That is all that matters.

Best of luck!

\ No newline at end of file + if(true) { + window.dataLayer = window.dataLayer || []; + function gtag(){window.dataLayer && window.dataLayer.push(arguments);} + gtag('js', new Date()); + + gtag('config', 'G-4HPC5TT803', {"send_page_view":false}); + } + \ No newline at end of file diff --git a/blog/listening-to-your-inner-voice/index.html b/blog/listening-to-your-inner-voice/index.html index 635f705..852bf6d 100644 --- a/blog/listening-to-your-inner-voice/index.html +++ b/blog/listening-to-your-inner-voice/index.html @@ -1,18 +1,12 @@ -Listening to your Inner Voice | Jimmy Hooker

Listening to your Inner Voice

Pál Szinyei Merse - Meadow with Poppies

Pál Szinyei Merse - Meadow with Poppies

Recently I learned about mimetic desire, originated by Rene Girard, but perhaps popularized in tech by Peter Thiel who saw him as his philosophical mentor.

The TL;DR of mimetic desire is that almost all of our behavior is based on imitation of others, and that it's inescapable. You want that nice car because other people see that nice car as valuable and that the larger culture has built up expectations around this object, not necessarily out of some pure desire on your part to have it. Same with prestigious jobs, schools, excess money, fancy clothes, etc.

If you're in business, as Thiel points out, you could base the customers you go after, and the product decisions you make, on your competitors instead of solving for the deeper problems you might focus on if you made more customer-aware, focused decisions that were divorced from considering your competitors.

I have struggled my whole life with mimetic desire. Instead of understanding the things that I myself want to do, I base a lot of decision making on those around me. What is the smart decision, based on what others would think? What should I be doing with my time? What would impress people?

The key problem with this, and something I've found myself thinking about for many years, is that you end up without any of your own opinions.

It's easy to see why we ended up this way. Human's are tribal creatures with sophisticated social hierarchies that have a dramatic impact on health and survival. In the past, if you were ostracized from the group, it could mean certain death. So it makes sense that our brains evolved to care a great deal about what others think of us.

But, we live in a modern age! For those of us in America, we have more freedom, wealth, and independence than any civilization in history. One of the more difficult things about our age is the sheer quantity of choices we can make about our future, and the impossibility of choosing the 'right' one.

A good plan, violently executed now, is better than a perfect plan next week. -— General George Patton

A difficult truth to accept is that it's often better to make a bad decision than no decision. We want to be able to look back at our thinking and have it be thoughtful, defensible, clear, articulate, and well supported. But, most decisions are reversible! It's often better to 'find out' than to spend endless amounts of time hand-wringing, researching, etc.

I work in product management, but I've also been on the pure creation side as well. I often think of product management as a debug for bad intuition, helping make sure that what you are building is actually something people want vs something you assume people want. You talk to customers, do the research, and build up a sense of what will actually work that is well supported.

However, after a while, you build up tacit knowledge. Knowledge that largely feels like instinct, as it's been built into muscle memory. You can't entirely explain why you know a decision is right, but you know. It's been born through experience.

A few years ago I left a job at a bootstrapped, scrappy startup called Badger Maps to join the professionalized product management world. This threw a lot of my opinions and assumptions into doubt. Did my work at Badger translate? Was the tacit knowledge that I built up over time useful in another context?

I had to revalidate a lot of my thinking and reassess the way I did things. This made trusting my tacit knowledge difficult, and not trusting your gut can lead to bad decisions, plenty of which I made.

As Thiel highlights from Gerards work, imitation is inescapable, but it's possible to be aware of it and double-check yourself.

After considering everyone else's opinions, what do you still want to do? What still feels right? Check in with yourself! Don't do the thing you don't want to do! Ask yourself: "What do I actually want to do?".

\ No newline at end of file +Listening to your Inner Voice | Jimmy Hooker

Listening to your Inner Voice

Pál Szinyei Merse - Meadow with Poppies

Pál Szinyei Merse - Meadow with Poppies

Recently I learned about mimetic desire, originated by Rene Girard, but perhaps popularized in tech by Peter Thiel who saw him as his philosophical mentor.

The TL;DR of mimetic desire is that almost all of our behavior is based on imitation of others, and that it's inescapable. You want that nice car because other people see that nice car as valuable and that the larger culture has built up expectations around this object, not necessarily out of some pure desire on your part to have it. Same with prestigious jobs, schools, excess money, fancy clothes, etc.

If you're in business, as Thiel points out, you could base the customers you go after, and the product decisions you make, on your competitors instead of solving for the deeper problems you might focus on if you made more customer-aware, focused decisions that were divorced from considering your competitors.

I have struggled my whole life with mimetic desire. Instead of understanding the things that I myself want to do, I base a lot of decision making on those around me. What is the smart decision, based on what others would think? What should I be doing with my time? What would impress people?

The key problem with this, and something I've found myself thinking about for many years, is that you end up without any of your own opinions.

It's easy to see why we ended up this way. Human's are tribal creatures with sophisticated social hierarchies that have a dramatic impact on health and survival. In the past, if you were ostracized from the group, it could mean certain death. So it makes sense that our brains evolved to care a great deal about what others think of us.

But, we live in a modern age! For those of us in America, we have more freedom, wealth, and independence than any civilization in history. One of the more difficult things about our age is the sheer quantity of choices we can make about our future, and the impossibility of choosing the 'right' one.

A good plan, violently executed now, is better than a perfect plan next week. +— General George Patton

A difficult truth to accept is that it's often better to make a bad decision than no decision. We want to be able to look back at our thinking and have it be thoughtful, defensible, clear, articulate, and well supported. But, most decisions are reversible! It's often better to 'find out' than to spend endless amounts of time hand-wringing, researching, etc.

I work in product management, but I've also been on the pure creation side as well. I often think of product management as a debug for bad intuition, helping make sure that what you are building is actually something people want vs something you assume people want. You talk to customers, do the research, and build up a sense of what will actually work that is well supported.

However, after a while, you build up tacit knowledge. Knowledge that largely feels like instinct, as it's been built into muscle memory. You can't entirely explain why you know a decision is right, but you know. It's been born through experience.

A few years ago I left a job at a bootstrapped, scrappy startup called Badger Maps to join the professionalized product management world. This threw a lot of my opinions and assumptions into doubt. Did my work at Badger translate? Was the tacit knowledge that I built up over time useful in another context?

I had to revalidate a lot of my thinking and reassess the way I did things. This made trusting my tacit knowledge difficult, and not trusting your gut can lead to bad decisions, plenty of which I made.

As Thiel highlights from Gerards work, imitation is inescapable, but it's possible to be aware of it and double-check yourself.

After considering everyone else's opinions, what do you still want to do? What still feels right? Check in with yourself! Don't do the thing you don't want to do! Ask yourself: "What do I actually want to do?".

\ No newline at end of file diff --git a/blog/strategies-for-dealing-with-anxiety-and-stress/index.html b/blog/strategies-for-dealing-with-anxiety-and-stress/index.html index deb7623..562887d 100644 --- a/blog/strategies-for-dealing-with-anxiety-and-stress/index.html +++ b/blog/strategies-for-dealing-with-anxiety-and-stress/index.html @@ -1,17 +1,11 @@ -Strategies for dealing with Anxiety and Stress | Jimmy Hooker

Strategies for dealing with Anxiety and Stress

Photo by Darius Basher

Photo by Darius Basher

I have experienced anxiety my whole life, at times to degrees that made it really difficult to just have normal, every day interactions. However, I’ve always simultaneously pushed myself really, really hard. Which meant I had to develop coping strategies to survive. Here’s what worked.

Supplements

  • Zembrin: Basically calm, energized focus in a pill. No side effects that I’ve seen. Remarkable supplement. If you have trouble with focus and energy, try this first.
  • L-Theanine: Gits rid of the jitters from caffeine. Naturally occurring in Green Tea, if you are sensitive to the effects of caffeine it basically gives you all the good without the bad. Completely changed coffee for me.
  • Lavender Pills: Noticeably calmer but not so much that it feels like a hindrance or produces side-effects. Pleasantly calm.
  • Magnesium Citrate: Useful to go to sleep, just be careful to not take too much. It is a diarrhetic and will give you wild gas if you over do it.

Medications

  • Beta Blockers: Great if you experience situational anxiety so intense that there’s no real “takeaways” or learning from the experience. Very useful for things like interviews where it’s harder to get “at bats” and the stakes are high. The key feature is it makes it so your anxiety/fear doesn’t produce physical side-effects, like shakey or sweaty hands, rapid heart rate, twitching, etc. It’s non-addictive and isn’t mind altering, but beware that it keeps heart rate down so you can’t exercise on it (your heart rate won’t keep up with the exercise). The cool thing is you still experience the fear, but it is more thrilling because of the lake of physical stuff.

Exercise / Sleep

  • Intense Exercise: I ride my bike up hills so steep they cause me to lose feeling in my fingers, and I feel incredible, almost euphoric afterward. I have related this to others, and almost everyone who does some form of exercise that is so intense they can’t think during parts of it remarks the same thing. You just feel great afterward. Doesn’t need to take a long time, 20 minutes of exercise that includes 5 minutes of super high intensity is great. I like biking because I get to see cool stuff while I’m doing it, and see way more than other exercise, but do whatever you like. If you like it, you’ll do it more often.
  • Minimum strength training: I can do about 50 push-ups in a single set along with 50 squats at the age of 40. I never do more than 1 set of these in a day. I know very few people who can do this at any age, much less 40. The trick is that you never push yourself too hard. Make it easy, and keep it easy. It takes very little of my time and it makes my body feel great.
  • Get an amount of sleep that makes you feel good. This really varies from person to person. I usually only let myself get 7 hours minimum, but I probably could use 9. My wife gets 9 and feels great. I wish I could discipline myself to do this, I think it’s probably the next big unlock for me.

Meditation

  • Ana Pana aka breath meditation/focus: This is what you do for the first 3 days of a 10-day sit when doing a Goenka-style Vipassana. Just focus on the breath leaving/entering your nose. Pay attention to everything about it, go ‘deep’ on your attention, notice the humidity, the sensation, how one nostril is closed, the quality of the breath. See how long you can focus before your mind wanders. 1 minute is a big achievement here. Quick, Goenka-led 15-minute version.
  • Vipassana aka “body scanning”: Start at the top of your head, and slowly move down your body, focusing on the sensation on your skin. If you are doing this right (ie with the right level of focus), you can usually feel your body releasing tension while you’re doing it. This can be hard to do well without getting good at Ana Pana first.

Little Cheats

  • Box Breathing: 4 seconds breathe in, 4 seconds hold, 4 seconds breathe out, 4 seconds hold. Repeat. Has a noticeable calming effect. Do this for a couple minutes and you can quickly center.
  • Cold Showers: Aka Wim Hoff showers. The cheat here is to take a hot shower, and then at the end switch to cold. It’s a great systemic wake up, see how long you can do it!

Psychology

  • Talking to yourself/coaching yourself: You can talk to yourself in a friendly way, as if you are coaching a friend. E.g. “Let’s not think about that right now, we can think about it later. It’s time to go to sleep, let’s go to sleep”. “It’s time to focus, we need to focus on this and get it done. Let’s focus on this task really hard”. “Tonight when we sleep let’s really clear the brain, we don’t need to repair physically as much.” It is kind of insane how well this works.
  • Reframing: The kids probably call this ‘cope’ now, but reframing is a key psychological device. Cope is useful to survive! If you have a shitty experience, there can be a tendency to just focus on the negative, but that’s actually an inaccurate way to view experience. Almost all experience has upside. Focus on it! What was good about it? Articulate it, write it down, feel the goodness of it. A lot of cognitive behavioral therapy (CBT) is oriented around this. It’s basically positive self-talk within a framework.
  • Becoming your own friend: I’m gonna work on a whole post on this, but learning to like yourself is the ultimate unlock and basically solves for most everything else. Just like you root for your friends, love your friends, and give them good advice, you gotta root for yourself, love yourself, and give yourself good advice. This is a consistent practice, and if your default is to be mean to yourself, either emotionally or verbally, it takes practice. I’m no where near done yet, but just the little bit I’ve done has dramatically helped.
  • Stop trying to be other people: You are yourself, that’s really all you can be. Learn to be the best version of that shamelessly. Trust your judgement of what this means. You will never be able to compete against people who are enjoying the experience of being themselves and doing what they’re doing. They will crush you. Just like you will crush others when you learn to do what you like and yourself.

Appendix:

Strategies for dealing with Anxiety and Stress | Jimmy Hooker

Strategies for dealing with Anxiety and Stress

Photo by Darius Basher

Photo by Darius Basher

I have experienced anxiety my whole life, at times to degrees that made it really difficult to just have normal, every day interactions. However, I’ve always simultaneously pushed myself really, really hard. Which meant I had to develop coping strategies to survive. Here’s what worked.

Supplements

  • Zembrin: Basically calm, energized focus in a pill. No side effects that I’ve seen. Remarkable supplement. If you have trouble with focus and energy, try this first.
  • L-Theanine: Gits rid of the jitters from caffeine. Naturally occurring in Green Tea, if you are sensitive to the effects of caffeine it basically gives you all the good without the bad. Completely changed coffee for me.
  • Lavender Pills: Noticeably calmer but not so much that it feels like a hindrance or produces side-effects. Pleasantly calm.
  • Magnesium Citrate: Useful to go to sleep, just be careful to not take too much. It is a diarrhetic and will give you wild gas if you over do it.

Medications

  • Beta Blockers: Great if you experience situational anxiety so intense that there’s no real “takeaways” or learning from the experience. Very useful for things like interviews where it’s harder to get “at bats” and the stakes are high. The key feature is it makes it so your anxiety/fear doesn’t produce physical side-effects, like shakey or sweaty hands, rapid heart rate, twitching, etc. It’s non-addictive and isn’t mind altering, but beware that it keeps heart rate down so you can’t exercise on it (your heart rate won’t keep up with the exercise). The cool thing is you still experience the fear, but it is more thrilling because of the lake of physical stuff.

Exercise / Sleep

  • Intense Exercise: I ride my bike up hills so steep they cause me to lose feeling in my fingers, and I feel incredible, almost euphoric afterward. I have related this to others, and almost everyone who does some form of exercise that is so intense they can’t think during parts of it remarks the same thing. You just feel great afterward. Doesn’t need to take a long time, 20 minutes of exercise that includes 5 minutes of super high intensity is great. I like biking because I get to see cool stuff while I’m doing it, and see way more than other exercise, but do whatever you like. If you like it, you’ll do it more often.
  • Minimum strength training: I can do about 50 push-ups in a single set along with 50 squats at the age of 40. I never do more than 1 set of these in a day. I know very few people who can do this at any age, much less 40. The trick is that you never push yourself too hard. Make it easy, and keep it easy. It takes very little of my time and it makes my body feel great.
  • Get an amount of sleep that makes you feel good. This really varies from person to person. I usually only let myself get 7 hours minimum, but I probably could use 9. My wife gets 9 and feels great. I wish I could discipline myself to do this, I think it’s probably the next big unlock for me.

Meditation

  • Ana Pana aka breath meditation/focus: This is what you do for the first 3 days of a 10-day sit when doing a Goenka-style Vipassana. Just focus on the breath leaving/entering your nose. Pay attention to everything about it, go ‘deep’ on your attention, notice the humidity, the sensation, how one nostril is closed, the quality of the breath. See how long you can focus before your mind wanders. 1 minute is a big achievement here. Quick, Goenka-led 15-minute version.
  • Vipassana aka “body scanning”: Start at the top of your head, and slowly move down your body, focusing on the sensation on your skin. If you are doing this right (ie with the right level of focus), you can usually feel your body releasing tension while you’re doing it. This can be hard to do well without getting good at Ana Pana first.

Little Cheats

  • Box Breathing: 4 seconds breathe in, 4 seconds hold, 4 seconds breathe out, 4 seconds hold. Repeat. Has a noticeable calming effect. Do this for a couple minutes and you can quickly center.
  • Cold Showers: Aka Wim Hoff showers. The cheat here is to take a hot shower, and then at the end switch to cold. It’s a great systemic wake up, see how long you can do it!

Psychology

  • Talking to yourself/coaching yourself: You can talk to yourself in a friendly way, as if you are coaching a friend. E.g. “Let’s not think about that right now, we can think about it later. It’s time to go to sleep, let’s go to sleep”. “It’s time to focus, we need to focus on this and get it done. Let’s focus on this task really hard”. “Tonight when we sleep let’s really clear the brain, we don’t need to repair physically as much.” It is kind of insane how well this works.
  • Reframing: The kids probably call this ‘cope’ now, but reframing is a key psychological device. Cope is useful to survive! If you have a shitty experience, there can be a tendency to just focus on the negative, but that’s actually an inaccurate way to view experience. Almost all experience has upside. Focus on it! What was good about it? Articulate it, write it down, feel the goodness of it. A lot of cognitive behavioral therapy (CBT) is oriented around this. It’s basically positive self-talk within a framework.
  • Becoming your own friend: I’m gonna work on a whole post on this, but learning to like yourself is the ultimate unlock and basically solves for most everything else. Just like you root for your friends, love your friends, and give them good advice, you gotta root for yourself, love yourself, and give yourself good advice. This is a consistent practice, and if your default is to be mean to yourself, either emotionally or verbally, it takes practice. I’m no where near done yet, but just the little bit I’ve done has dramatically helped.
  • Stop trying to be other people: You are yourself, that’s really all you can be. Learn to be the best version of that shamelessly. Trust your judgement of what this means. You will never be able to compete against people who are enjoying the experience of being themselves and doing what they’re doing. They will crush you. Just like you will crush others when you learn to do what you like and yourself.

Appendix:

\ No newline at end of file + if(true) { + window.dataLayer = window.dataLayer || []; + function gtag(){window.dataLayer && window.dataLayer.push(arguments);} + gtag('js', new Date()); + + gtag('config', 'G-4HPC5TT803', {"send_page_view":false}); + } + \ No newline at end of file diff --git a/blog/the-crippling-imaginary-audience/index.html b/blog/the-crippling-imaginary-audience/index.html index 526718c..de8b440 100644 --- a/blog/the-crippling-imaginary-audience/index.html +++ b/blog/the-crippling-imaginary-audience/index.html @@ -1,17 +1,11 @@ -The Crippling, Imaginary Audience | Jimmy Hooker

The Crippling, Imaginary Audience

Photo by Davide Ragusa

Photo by Davide Ragusa

When I was younger I had multiple websites where I posted often. I didn't care what I wrote because I was just writing for my friends. Doing this improved my writing dramatically. It forced me to focus on who my audience was, have a goal in writing (always write to persuade!), and to write for actual consumption. People I knew actually read my site! Having any audience at all was just fun.

But then I got fired for talking shit about my boss. One of my coworkers who didn't like me ratted me out. I was nineteen years old and a complete dumbass. The public nature of the internet was just starting to rear its ugly head and I was one of a number of people who were getting fired for confusing their personal websites for a secluded corner of the internet. This appears to be a difficult lesson to learn, as this still happens constantly to people on Twitter well over a decade later.

The public nature of the internet is a hard thing to wrap your head around. Humans weren't built to appreciate this kind of scale. People constantly post things that are inflammatory, offensive, disrespectful, off-color, wrong-headed, problematic, bad jokes, and more. To their friends, who appreciate the human at the core of it, these posts are just one aspect of a person who contains multitudes. But this melts away once you get to the first person who hasn't met them. Suddenly, you have actor-observer bias running rampant.

Cast a wide enough net, and you are stripped of your humanity. You become infamous, an overnight celebrity with all the scrutiny that comes with it.

It's a terrifying potential.

Recently, a number of thoughtful articles have been written on what is now a big enough problem to be called 'cancel culture':

The mob wants justice and it wants public executions. You're a seventeen year-old kid? Too bad, you are a monster now. The President and you had a tryst? Sorry, you're a slut and the butt of jokes for life. The human desire for blood-lust hasn't abated, it has just changed form.

So why post at all? Why voice any opinions in such a dangerous environment?

I still have a lot of difficulty with this. For now at least, the lesson might be that you shouldn't touch anything that is controversial unless you have some very well defined thoughts on the subject. If you haven't done your research and you haven't debated someone knowledgeable, leave it alone. As soon as you put words to paper, you are inviting people to pick it apart. This is not you and your friends around a beer.

Perhaps this isn't a bad lesson. We are not all journalists and comedians. We are not prepared for the responsibility and weight of our words. For many of us, perhaps only managers, teachers, and our parents have read our writing. And now you want the whole internet to respond?

Unless you really want to enter the arena, maybe best to leave it to someone else.

The Crippling, Imaginary Audience | Jimmy Hooker

The Crippling, Imaginary Audience

Photo by Davide Ragusa

Photo by Davide Ragusa

When I was younger I had multiple websites where I posted often. I didn't care what I wrote because I was just writing for my friends. Doing this improved my writing dramatically. It forced me to focus on who my audience was, have a goal in writing (always write to persuade!), and to write for actual consumption. People I knew actually read my site! Having any audience at all was just fun.

But then I got fired for talking shit about my boss. One of my coworkers who didn't like me ratted me out. I was nineteen years old and a complete dumbass. The public nature of the internet was just starting to rear its ugly head and I was one of a number of people who were getting fired for confusing their personal websites for a secluded corner of the internet. This appears to be a difficult lesson to learn, as this still happens constantly to people on Twitter well over a decade later.

The public nature of the internet is a hard thing to wrap your head around. Humans weren't built to appreciate this kind of scale. People constantly post things that are inflammatory, offensive, disrespectful, off-color, wrong-headed, problematic, bad jokes, and more. To their friends, who appreciate the human at the core of it, these posts are just one aspect of a person who contains multitudes. But this melts away once you get to the first person who hasn't met them. Suddenly, you have actor-observer bias running rampant.

Cast a wide enough net, and you are stripped of your humanity. You become infamous, an overnight celebrity with all the scrutiny that comes with it.

It's a terrifying potential.

Recently, a number of thoughtful articles have been written on what is now a big enough problem to be called 'cancel culture':

The mob wants justice and it wants public executions. You're a seventeen year-old kid? Too bad, you are a monster now. The President and you had a tryst? Sorry, you're a slut and the butt of jokes for life. The human desire for blood-lust hasn't abated, it has just changed form.

So why post at all? Why voice any opinions in such a dangerous environment?

I still have a lot of difficulty with this. For now at least, the lesson might be that you shouldn't touch anything that is controversial unless you have some very well defined thoughts on the subject. If you haven't done your research and you haven't debated someone knowledgeable, leave it alone. As soon as you put words to paper, you are inviting people to pick it apart. This is not you and your friends around a beer.

Perhaps this isn't a bad lesson. We are not all journalists and comedians. We are not prepared for the responsibility and weight of our words. For many of us, perhaps only managers, teachers, and our parents have read our writing. And now you want the whole internet to respond?

Unless you really want to enter the arena, maybe best to leave it to someone else.

\ No newline at end of file + if(true) { + window.dataLayer = window.dataLayer || []; + function gtag(){window.dataLayer && window.dataLayer.push(arguments);} + gtag('js', new Date()); + + gtag('config', 'G-4HPC5TT803', {"send_page_view":false}); + } + \ No newline at end of file diff --git a/chunk-map.json b/chunk-map.json index 04d1b2d..9cf9dd0 100644 --- a/chunk-map.json +++ b/chunk-map.json @@ -1 +1 @@ -{"polyfill":["/polyfill-67c31b63031754035b52.js"],"app":["/app-2db289bf6a57778cd61e.js"],"component---src-pages-404-js":["/component---src-pages-404-js-27842d7ed8830e74c3aa.js"],"component---src-pages-about-js":["/component---src-pages-about-js-c9d82eeefc0c25a33d16.js"],"component---src-pages-donate-js":["/component---src-pages-donate-js-5899d8c8b95030f5a1bb.js"],"component---src-pages-index-js":["/component---src-pages-index-js-2f7a1945b6aa37a50672.js"],"component---src-pages-mdx-frontmatter-post-type-mdx-slug-js":["/component---src-pages-mdx-frontmatter-post-type-mdx-slug-js-48b0a058daaca5b189f1.js"],"component---src-pages-portfolio-js":["/component---src-pages-portfolio-js-3ad369d50ef16a5829cb.js"]} \ No newline at end of file +{"polyfill":["/polyfill-67c31b63031754035b52.js"],"app":["/app-5f75a45e93580195f195.js"],"component---src-pages-404-js":["/component---src-pages-404-js-27842d7ed8830e74c3aa.js"],"component---src-pages-about-js":["/component---src-pages-about-js-c9d82eeefc0c25a33d16.js"],"component---src-pages-donate-js":["/component---src-pages-donate-js-5899d8c8b95030f5a1bb.js"],"component---src-pages-index-js":["/component---src-pages-index-js-2f7a1945b6aa37a50672.js"],"component---src-pages-mdx-frontmatter-post-type-mdx-slug-js":["/component---src-pages-mdx-frontmatter-post-type-mdx-slug-js-48b0a058daaca5b189f1.js"],"component---src-pages-portfolio-js":["/component---src-pages-portfolio-js-3ad369d50ef16a5829cb.js"]} \ No newline at end of file diff --git a/donate/index.html b/donate/index.html index 689edd2..8d62c42 100644 --- a/donate/index.html +++ b/donate/index.html @@ -1,17 +1,11 @@ -Donate to Grow SF | Jimmy Hooker

Donate

Grow SF works through donations

Grow SF PAC is comprised of a team of volunteers. All proceeds will be used to operate the organization including tech infrastructure costs, advertising, and legal fees.

If your contribution is from a business, please enter the business type in the donor info form (corporation, LLC, sole proprietor, etc). If your company is an LLC or LLP, please also enter in the responsible officer. The responsible Officer is the individual (or one of the individuals) primarily responsible for the political contribution decisions, whether one of the LLC's members, a project manager or other employee.

Contributions are not deductible as charitable contributions for federal income tax purposes.

Donate to Grow SF | Jimmy Hooker

Donate

Grow SF works through donations

Grow SF PAC is comprised of a team of volunteers. All proceeds will be used to operate the organization including tech infrastructure costs, advertising, and legal fees.

If your contribution is from a business, please enter the business type in the donor info form (corporation, LLC, sole proprietor, etc). If your company is an LLC or LLP, please also enter in the responsible officer. The responsible Officer is the individual (or one of the individuals) primarily responsible for the political contribution decisions, whether one of the LLC's members, a project manager or other employee.

Contributions are not deductible as charitable contributions for federal income tax purposes.

\ No newline at end of file + if(true) { + window.dataLayer = window.dataLayer || []; + function gtag(){window.dataLayer && window.dataLayer.push(arguments);} + gtag('js', new Date()); + + gtag('config', 'G-4HPC5TT803', {"send_page_view":false}); + } + \ No newline at end of file diff --git a/hidden/dont-leave-meetings-confused/index.html b/hidden/dont-leave-meetings-confused/index.html index 8f386e0..16c5a14 100644 --- a/hidden/dont-leave-meetings-confused/index.html +++ b/hidden/dont-leave-meetings-confused/index.html @@ -1,17 +1,11 @@ -Don't leave meetings confused | Jimmy Hooker

Don't leave meetings confused

Art by Edvard Munch

Art by Edvard Munch

One of the worst ways to leave a meeting is confused. What am I supposed to be doing? What was that one person talking about? What is the next step here? It's a waste of everyone's time. Meetings are a necessary evil, but they are pure evil when they don't have clear outcomes.

There are two ways this happens.

  1. You created the meeting and you just kinda jumped in to what you wanted to talk about without giving everyone time to wrap their head around the subject matter. You didn't do intros, set context, or check in and let people ask questions. If you ask people if they have questions at the end and people just kinda silently look at you, the meeting is probably a failure. Silence is dissent.
  2. You attended a meeting, and you didn't ask questions, either because you were afraid to look stupid, or you didn't understand enough to even ask the questions. There's a lot of responsibility on the meeting runner to make sure the meeting makes sense, but you as an attendee also have a responsibility to get clarity.

So, how can you solve for this? I've done this the wrong way more times than I can count, so one of the ways to get good is to just brute-force it until you learn through pain to get better. But there are shortcuts that will help!

As a meeting organizer:

  1. Set context: What are we here to talk about? What's the goal? Why? Setting up the problem you are looking to solve in the meeting is by far the most important thing. Usually everything else flows from this. DO NOT skimp here. Err on the side of over-explaining the problem/context.
  2. Check in regularly: You should only talk for maybe a few minutes before pausing to get the temperature. Is everyone getting it? Look out for those 'deer in headlights' eyes. Call on people in particular if they seem lost. Be nice, but direct.
  3. Review what was discussed and what the action items are at least 4 minutes before the end of the meeting. What outcomes did we get to? Does everyone agree on your interpretation?
  4. Summarize the meeting and send notes to everyone at the end. Send it in slack, create meeting notes on the calendar entry. What was discussed, what are the action items, and when can we expect an update?
  5. Set a reminder for yourself or the meeting attendees to follow-up when everyone expects right at the end or even during the meeting. Do not wait. Do it in your calendar or slack.

As a meeting attendee:

  1. Ask questions if you are confused. Either wait for them to check-in, or if they don't, interrupt them. Do not let the organizer keep going if you are confused. You are very likely not the only one, and even if you are who cares. Presumably you are in the meeting because you are important, it is essential you don't leave confused. I can't emphasize this enough.
  2. Take notes: If the meeting organizer doesn't summarize the meeting, you will forget the details of the meeting in about 48 hours. Take notes, and if the organizer doesn't send a summary, you should and you should check-in to make sure they align with the organizer's expectations.
Don't leave meetings confused | Jimmy Hooker

Don't leave meetings confused

Art by Edvard Munch

Art by Edvard Munch

One of the worst ways to leave a meeting is confused. What am I supposed to be doing? What was that one person talking about? What is the next step here? It's a waste of everyone's time. Meetings are a necessary evil, but they are pure evil when they don't have clear outcomes.

There are two ways this happens.

  1. You created the meeting and you just kinda jumped in to what you wanted to talk about without giving everyone time to wrap their head around the subject matter. You didn't do intros, set context, or check in and let people ask questions. If you ask people if they have questions at the end and people just kinda silently look at you, the meeting is probably a failure. Silence is dissent.
  2. You attended a meeting, and you didn't ask questions, either because you were afraid to look stupid, or you didn't understand enough to even ask the questions. There's a lot of responsibility on the meeting runner to make sure the meeting makes sense, but you as an attendee also have a responsibility to get clarity.

So, how can you solve for this? I've done this the wrong way more times than I can count, so one of the ways to get good is to just brute-force it until you learn through pain to get better. But there are shortcuts that will help!

As a meeting organizer:

  1. Set context: What are we here to talk about? What's the goal? Why? Setting up the problem you are looking to solve in the meeting is by far the most important thing. Usually everything else flows from this. DO NOT skimp here. Err on the side of over-explaining the problem/context.
  2. Check in regularly: You should only talk for maybe a few minutes before pausing to get the temperature. Is everyone getting it? Look out for those 'deer in headlights' eyes. Call on people in particular if they seem lost. Be nice, but direct.
  3. Review what was discussed and what the action items are at least 4 minutes before the end of the meeting. What outcomes did we get to? Does everyone agree on your interpretation?
  4. Summarize the meeting and send notes to everyone at the end. Send it in slack, create meeting notes on the calendar entry. What was discussed, what are the action items, and when can we expect an update?
  5. Set a reminder for yourself or the meeting attendees to follow-up when everyone expects right at the end or even during the meeting. Do not wait. Do it in your calendar or slack.

As a meeting attendee:

  1. Ask questions if you are confused. Either wait for them to check-in, or if they don't, interrupt them. Do not let the organizer keep going if you are confused. You are very likely not the only one, and even if you are who cares. Presumably you are in the meeting because you are important, it is essential you don't leave confused. I can't emphasize this enough.
  2. Take notes: If the meeting organizer doesn't summarize the meeting, you will forget the details of the meeting in about 48 hours. Take notes, and if the organizer doesn't send a summary, you should and you should check-in to make sure they align with the organizer's expectations.
\ No newline at end of file + if(true) { + window.dataLayer = window.dataLayer || []; + function gtag(){window.dataLayer && window.dataLayer.push(arguments);} + gtag('js', new Date()); + + gtag('config', 'G-4HPC5TT803', {"send_page_view":false}); + } + \ No newline at end of file diff --git a/index.html b/index.html index d1e4f7e..fc6189b 100644 --- a/index.html +++ b/index.html @@ -1,17 +1,11 @@ -Jimmy Hooker's Website | Jimmy Hooker
Jimmy Hooker's Website | Jimmy Hooker
\ No newline at end of file + if(true) { + window.dataLayer = window.dataLayer || []; + function gtag(){window.dataLayer && window.dataLayer.push(arguments);} + gtag('js', new Date()); + + gtag('config', 'G-4HPC5TT803', {"send_page_view":false}); + } + \ No newline at end of file diff --git a/page-data/app-data.json b/page-data/app-data.json index c63ef3c..a7fddde 100644 --- a/page-data/app-data.json +++ b/page-data/app-data.json @@ -1 +1 @@ -{"webpackCompilationHash":"b1172a36afc08558aa8a"} +{"webpackCompilationHash":"655a454a8691656a4ce4"} diff --git a/portfolio/associates-dein/index.html b/portfolio/associates-dein/index.html index 9e66da2..ec468ab 100644 --- a/portfolio/associates-dein/index.html +++ b/portfolio/associates-dein/index.html @@ -1,4 +1,4 @@ -Associates Dein | Jimmy Hooker

Associates Dein

Created a website and map based real-estate product that allowed people to easily find where they'd like to locate their new home in Arizona.

+Associates Dein | Jimmy Hooker

Associates Dein

Created a website and map based real-estate product that allowed people to easily find where they'd like to locate their new home in Arizona.

Associates Dein

@@ -7,20 +7,14 @@

Map of Phoenix -

\ No newline at end of file + if(true) { + window.dataLayer = window.dataLayer || []; + function gtag(){window.dataLayer && window.dataLayer.push(arguments);} + gtag('js', new Date()); + + gtag('config', 'G-4HPC5TT803', {"send_page_view":false}); + } + \ No newline at end of file diff --git a/portfolio/badger-maps/index.html b/portfolio/badger-maps/index.html index 9fde8ee..83793a5 100644 --- a/portfolio/badger-maps/index.html +++ b/portfolio/badger-maps/index.html @@ -1,4 +1,4 @@ -Badger Maps | Jimmy Hooker

Badger Maps

While at Badger, I've had the opportunity to do a little bit of everything. However, my earliest and most tangible contributions have been on design. I'm incredibly proud of how easy to use our product is and how tight the feedback loops are to take action.

Badger is a unique application in that its primary interface is a map. At a high-level Badger works as a light weight CRM for outside sales professionals, so being able to spatially reason with your customers is very important. There are few applications out there that are able to layer data on a map while still making it feel easy to use. Our customers were sales people and had zero patience for an application that felt difficult.

Here I'll breakdown our approach on a platform-by-platform basis.

Responsive Web Application

Designing responsive web applications is absurdly difficult. Do you fluidly adapt? Do you use certain breakpoints? What things do you throw away, and which do you keep? Lets say the core of your experience is a map. How do you keep that map available/surfaceable easily by the user?

I managed to come up with a solution that kept almost all of our templates/components intact while adapting them for mobile at a single breakpoint. Once implemented, it immediately offered a mobile solution across all devices that, while not quite at the level of native, was pretty dang good.

This design has held strong for 4 years, remaining intuitive and allowing us to add features easily.

Let's dig in.

The Metaphor

I wanted a simple structure on desktop that I could easily adapt down to the mobile level.

I decided that there were four things I really had to worry about:

  • The Menu
  • The Map
  • The Menu Details (ie what is exposed when you click on a menu button)
  • The Account Details

On desktop, I had the room to have a beefier menu, but on mobile I didn't. So I decided that I'd likely have to toss the menu and recreate it for mobile since the differences would be so stark. However, the rest of it I could re-use if I could wrap my head around it.

I decided to use a 'pane' metaphor, where each pane would be more or less the width of a cell phone. The menu details would be a pane, and the account details would be a pane. This would mean I could re-use the panes on mobile very easily by setting all their element widths to 100% (plus a few other tweaks)

Here's what it looks like on Desktop:

+Badger Maps | Jimmy Hooker

Badger Maps

While at Badger, I've had the opportunity to do a little bit of everything. However, my earliest and most tangible contributions have been on design. I'm incredibly proud of how easy to use our product is and how tight the feedback loops are to take action.

Badger is a unique application in that its primary interface is a map. At a high-level Badger works as a light weight CRM for outside sales professionals, so being able to spatially reason with your customers is very important. There are few applications out there that are able to layer data on a map while still making it feel easy to use. Our customers were sales people and had zero patience for an application that felt difficult.

Here I'll breakdown our approach on a platform-by-platform basis.

Responsive Web Application

Designing responsive web applications is absurdly difficult. Do you fluidly adapt? Do you use certain breakpoints? What things do you throw away, and which do you keep? Lets say the core of your experience is a map. How do you keep that map available/surfaceable easily by the user?

I managed to come up with a solution that kept almost all of our templates/components intact while adapting them for mobile at a single breakpoint. Once implemented, it immediately offered a mobile solution across all devices that, while not quite at the level of native, was pretty dang good.

This design has held strong for 4 years, remaining intuitive and allowing us to add features easily.

Let's dig in.

The Metaphor

I wanted a simple structure on desktop that I could easily adapt down to the mobile level.

I decided that there were four things I really had to worry about:

  • The Menu
  • The Map
  • The Menu Details (ie what is exposed when you click on a menu button)
  • The Account Details

On desktop, I had the room to have a beefier menu, but on mobile I didn't. So I decided that I'd likely have to toss the menu and recreate it for mobile since the differences would be so stark. However, the rest of it I could re-use if I could wrap my head around it.

I decided to use a 'pane' metaphor, where each pane would be more or less the width of a cell phone. The menu details would be a pane, and the account details would be a pane. This would mean I could re-use the panes on mobile very easily by setting all their element widths to 100% (plus a few other tweaks)

Here's what it looks like on Desktop:

Here is the desktop view broken down into it's main structural components.

@@ -64,20 +64,14 @@ Original versions highlighting problems when going black and white

badger logo three styles after - Improvements in being able to go black and white

The resulting logo, to me at least, is a massive difference. It reduces FAR better. it feels more confident, it flows. it feels like it knows what it's doing.

\ No newline at end of file + if(true) { + window.dataLayer = window.dataLayer || []; + function gtag(){window.dataLayer && window.dataLayer.push(arguments);} + gtag('js', new Date()); + + gtag('config', 'G-4HPC5TT803', {"send_page_view":false}); + } + \ No newline at end of file diff --git a/portfolio/cloudhike/index.html b/portfolio/cloudhike/index.html index 6b9098e..b9f41d5 100644 --- a/portfolio/cloudhike/index.html +++ b/portfolio/cloudhike/index.html @@ -1,23 +1,17 @@ -Cloudhike | Jimmy Hooker

Cloudhike

Cloud hosting for a Wordpress product me and a good friend worked on when we were trying to make company after company. We could have been WPEngine!

+Cloudhike | Jimmy Hooker

Cloudhike

Cloud hosting for a Wordpress product me and a good friend worked on when we were trying to make company after company. We could have been WPEngine!

Cloudhike Home Page

Cloudhike Pricing Page -

\ No newline at end of file + if(true) { + window.dataLayer = window.dataLayer || []; + function gtag(){window.dataLayer && window.dataLayer.push(arguments);} + gtag('js', new Date()); + + gtag('config', 'G-4HPC5TT803', {"send_page_view":false}); + } + \ No newline at end of file diff --git a/portfolio/fahrenheit-labs/index.html b/portfolio/fahrenheit-labs/index.html index e62efde..73f6107 100644 --- a/portfolio/fahrenheit-labs/index.html +++ b/portfolio/fahrenheit-labs/index.html @@ -1,4 +1,4 @@ -Fahrenheit Labs | Jimmy Hooker

Fahrenheit Labs

Created a design/brand/website design company with a friend when younger. I'm still proud of this branding.

+Fahrenheit Labs | Jimmy Hooker

Fahrenheit Labs

Created a design/brand/website design company with a friend when younger. I'm still proud of this branding.

Fahrenheit home page

@@ -7,20 +7,14 @@

Fahrenheit contact page -

\ No newline at end of file + if(true) { + window.dataLayer = window.dataLayer || []; + function gtag(){window.dataLayer && window.dataLayer.push(arguments);} + gtag('js', new Date()); + + gtag('config', 'G-4HPC5TT803', {"send_page_view":false}); + } + \ No newline at end of file diff --git a/portfolio/hank/index.html b/portfolio/hank/index.html index b2b8fff..994442c 100644 --- a/portfolio/hank/index.html +++ b/portfolio/hank/index.html @@ -1,4 +1,4 @@ -Hank | Jimmy Hooker

Hank

+Hank | Jimmy Hooker

Hank

Logo ideation

Built a logo and identiy around Hank, an AI powered HVAC controller.

@@ -7,20 +7,14 @@

Website -

\ No newline at end of file + if(true) { + window.dataLayer = window.dataLayer || []; + function gtag(){window.dataLayer && window.dataLayer.push(arguments);} + gtag('js', new Date()); + + gtag('config', 'G-4HPC5TT803', {"send_page_view":false}); + } + \ No newline at end of file diff --git a/portfolio/index.html b/portfolio/index.html index 50517c0..b35b1a9 100644 --- a/portfolio/index.html +++ b/portfolio/index.html @@ -1,17 +1,11 @@ -Jimmy Hooker's Portfolio | Jimmy Hooker
Jimmy Hooker's Portfolio | Jimmy Hooker
\ No newline at end of file + if(true) { + window.dataLayer = window.dataLayer || []; + function gtag(){window.dataLayer && window.dataLayer.push(arguments);} + gtag('js', new Date()); + + gtag('config', 'G-4HPC5TT803', {"send_page_view":false}); + } + \ No newline at end of file diff --git a/portfolio/pocket-bodhi/index.html b/portfolio/pocket-bodhi/index.html index 104cd4d..496394b 100644 --- a/portfolio/pocket-bodhi/index.html +++ b/portfolio/pocket-bodhi/index.html @@ -1,20 +1,14 @@ -Pocket Bodhi | Jimmy Hooker

Pocket Bodhi

Had fun building a concept meditation app for a health conscious hackathon in Palm Springs in 2014.

+Pocket Bodhi | Jimmy Hooker

Pocket Bodhi

Had fun building a concept meditation app for a health conscious hackathon in Palm Springs in 2014.

App design for Pocket Bodhi -

\ No newline at end of file + if(true) { + window.dataLayer = window.dataLayer || []; + function gtag(){window.dataLayer && window.dataLayer.push(arguments);} + gtag('js', new Date()); + + gtag('config', 'G-4HPC5TT803', {"send_page_view":false}); + } + \ No newline at end of file diff --git a/portfolio/rocky-mountain-ruby/index.html b/portfolio/rocky-mountain-ruby/index.html index c5dc66b..e98bfac 100644 --- a/portfolio/rocky-mountain-ruby/index.html +++ b/portfolio/rocky-mountain-ruby/index.html @@ -1,20 +1,14 @@ -Rocky Mountain Ruby Conference | Jimmy Hooker

Rocky Mountain Ruby Conference

+Rocky Mountain Ruby Conference | Jimmy Hooker

Rocky Mountain Ruby Conference

Final logos for rocky mountain ruby -

\ No newline at end of file + if(true) { + window.dataLayer = window.dataLayer || []; + function gtag(){window.dataLayer && window.dataLayer.push(arguments);} + gtag('js', new Date()); + + gtag('config', 'G-4HPC5TT803', {"send_page_view":false}); + } + \ No newline at end of file diff --git a/rss.xml b/rss.xml index fe061f4..037d40f 100644 --- a/rss.xml +++ b/rss.xml @@ -1,4 +1,4 @@ -<![CDATA[Jimmy Hooker's RSS Feed]]>https://www.jimmyhooker.comhttps://www.jimmyhooker.com/icon.pngJimmy Hooker's RSS Feedhttps://www.jimmyhooker.comGatsbyJSFri, 03 May 2024 19:34:56 GMT<![CDATA[How to Not Be Full of Shit as a Product Manager]]>https://www.jimmyhooker.com/blog/how-to-not-be-full-of-shit-as-a-product-manager/https://www.jimmyhooker.com/blog/how-to-not-be-full-of-shit-as-a-product-manager/Fri, 03 May 2024 00:00:00 GMT<p>So you’ve joined a new team, company, etc and you are experiencing the cold terror that is your new responsibility area as a product manager.</p><p>I think you have about 90 days to demonstrate that you are going to be a net positive addition to the team and to build up trust. What do you do?</p><p>This assumes:</p><ul><li>The product already exists</li><li>There are existing users</li></ul><p>My opinion on this is you need to:</p><ul><li><strong>Get ahold of as many customers as possible within the next 30 days and have them walk you through their product usage and your product feature in particular</strong>. Have them share their screen while they do this. Write out your questions loosely beforehand, and adjust rapidly with the first few customers until you get a feel for which questions elicit the best answers. Record everything, and then tag key statements around pain points, etc, with a tool like Dovetail.</li><li><strong>Insist on involving the engineering manager and designer associated with your product area in the customer discussions</strong>. This has multiple benefits, including demonstrating your ability to understand customer pain points and developing shared context between your key stakeholders.</li><li><strong>Learn where all the bodies are buried in the data</strong>. Do not rely on the data team, you need to get familiar with querying raw data. Know the key tables and how to produce good output.</li><li><strong>Manage your stress</strong>. Exercise, take supplements, and sleep enough that you don’t freak out. If you are doing this right, the first 90-days should feel like watching ‘Uncut Gems’ nonstop. <a href="https://www.jimmyhooker.com/blog/strategies-for-dealing-with-anxiety-and-stress/">Act accordingly</a>.</li><li><strong>Ignore everything that is not the above</strong>. No one will really care that you followed whatever process/ritual properly if your product area doesn’t deliver. Let people chase you down and yell at you for things that aren’t really consequential. You need to hold your responsibility to your product area above all else. That doesn’t mean you need to be unnecessarily rude or obnoxious, but it’s ok to drop balls that don’t matter.</li></ul><p>That’s it. I think the last point is worth really worth emphasizing. It’s really easy to get caught up in exercises that feel useful but don’t solve real problems. Do not be the person who has nice narratives but isn’t driving value. Understand your product area through customers, include your key stakeholders, build an aligned plan, and articulate it to leadership. That is all that matters.</p><p>Best of luck!</p><![CDATA[How to Deliver Good Presentations]]>https://www.jimmyhooker.com/blog/how-to-deliver-good-presentations/https://www.jimmyhooker.com/blog/how-to-deliver-good-presentations/Tue, 26 Mar 2024 00:00:00 GMT<p>This is actually pretty simple to do well, but you really have to put in the practice time. Don&#x27;t kid yourself, it is humiliating to fuck up a presentation because you didn&#x27;t practice enough. Unforced Error. Here&#x27;s how to do it.</p><ol><li>Do an ‘off the cuff’ version with no slides, or minimal slides.</li><li>Experience the visceral terror of how bad the presentation will be if you do it ‘off the cuff’ or unpracticed.</li><li>Do it a couple more times to really fire up the ‘presentation’ neurons</li><li>Write down what you want to say like it was a speech, literally word for word what you want to say to the audience. </li><li>Practice the written down version a couple times</li><li>Edit it heavily</li><li>Practice 5-10 more times, making little adjustments, until you can mostly do it without looking at your text</li></ol><p>Voila. Your presentation will most likely not suck, and you will have a confident handle on the material so that you can dynamically deliver it in front of an audience with a relative sense of comfort. </p><![CDATA[Strategies for dealing with Anxiety and Stress]]>https://www.jimmyhooker.com/blog/strategies-for-dealing-with-anxiety-and-stress/https://www.jimmyhooker.com/blog/strategies-for-dealing-with-anxiety-and-stress/Sat, 23 Mar 2024 00:00:00 GMT<p>I have experienced anxiety my whole life, at times to degrees that made it really difficult to just have normal, every day interactions. However, I’ve always simultaneously pushed myself really, really hard. Which meant I had to develop coping strategies to survive. Here’s what worked. </p><h2>Supplements</h2><ul><li>Zembrin: Basically calm, energized focus in a pill. No side effects that I’ve seen. Remarkable supplement. If you have trouble with focus and energy, try this first. </li><li>L-Theanine: Gits rid of the jitters from caffeine. Naturally occurring in Green Tea, if you are sensitive to the effects of caffeine it basically gives you all the good without the bad. Completely changed coffee for me. </li><li>Lavender Pills: Noticeably calmer but not so much that it feels like a hindrance or produces side-effects. Pleasantly calm. </li><li>Magnesium Citrate: Useful to go to sleep, just be careful to not take too much. It is a diarrhetic and will give you wild gas if you over do it. </li></ul><h2>Medications</h2><ul><li>Beta Blockers: Great if you experience situational anxiety so intense that there’s no real “takeaways” or learning from the experience. Very useful for things like interviews where it’s harder to get “at bats” and the stakes are high. The key feature is it makes it so your anxiety/fear doesn’t produce physical side-effects, like shakey or sweaty hands, rapid heart rate, twitching, etc. It’s non-addictive and isn’t mind altering, but beware that it keeps heart rate down so you can’t exercise on it (your heart rate won’t keep up with the exercise). The cool thing is you still experience the fear, but it is more thrilling because of the lake of physical stuff. </li></ul><h2>Exercise / Sleep</h2><ul><li>Intense Exercise: I ride my bike up hills so steep they cause me to lose feeling in my fingers, and I feel incredible, almost euphoric afterward. I have related this to others, and almost everyone who does some form of exercise that is so intense they can’t think during parts of it remarks the same thing. You just feel great afterward. Doesn’t need to take a long time, 20 minutes of exercise that includes 5 minutes of super high intensity is great. I like biking because I get to see cool stuff while I’m doing it, and see way more than other exercise, but do whatever you like. If you like it, you’ll do it more often. </li><li>Minimum strength training: I can do about 50 push-ups in a single set along with 50 squats at the age of 40. I never do more than 1 set of these in a day. I know very few people who can do this at any age, much less 40. The trick is that you never push yourself too hard. Make it easy, and keep it easy. It takes very little of my time and it makes my body feel great. </li><li>Get an amount of sleep that makes you feel good. This really varies from person to person. I usually only let myself get 7 hours minimum, but I probably could use 9. My wife gets 9 and feels great. I wish I could discipline myself to do this, I think it’s probably the next big unlock for me. </li></ul><h2>Meditation</h2><ul><li>Ana Pana aka breath meditation/focus: This is what you do for the first 3 days of a 10-day sit when doing a Goenka-style Vipassana. Just focus on the breath leaving/entering your nose. Pay attention to everything about it, go ‘deep’ on your attention, notice the humidity, the sensation, how one nostril is closed, the quality of the breath. See how long you can focus before your mind wanders. 1 minute is a big achievement here. Quick, <a href="https://www.youtube.com/watch?v=eyM_c3wSh8s">Goenka-led 15-minute version</a>. </li><li>Vipassana aka “body scanning”: Start at the top of your head, and slowly move down your body, focusing on the sensation on your skin. If you are doing this right (ie with the right level of focus), you can usually feel your body releasing tension while you’re doing it. This can be hard to do well without getting good at Ana Pana first. </li></ul><h2>Little Cheats</h2><ul><li><a href="https://www.healthline.com/health/box-breathing#slowly-exhale">Box Breathing</a>: 4 seconds breathe in, 4 seconds hold, 4 seconds breathe out, 4 seconds hold. Repeat. Has a noticeable calming effect. Do this for a couple minutes and you can quickly center. </li><li>Cold Showers: Aka <a href="https://www.wimhofmethod.com/benefits-of-cold-showers">Wim Hoff showers</a>. The cheat here is to take a hot shower, and then at the end switch to cold. It’s a great systemic wake up, see how long you can do it!</li></ul><h2>Psychology</h2><ul><li>Talking to yourself/coaching yourself: You can <a href="https://x.com/devonzuegel/status/1770480691416834299?s=20">talk to yourself in a friendly way</a>, as if you are coaching a friend. E.g. “Let’s not think about that right now, we can think about it later. It’s time to go to sleep, let’s go to sleep”. “It’s time to focus, we need to focus on this and get it done. Let’s focus on this task really hard”. “Tonight when we sleep let’s really clear the brain, we don’t need to repair physically as much.” It is kind of insane how well this works. </li><li>Reframing: The kids probably call this ‘cope’ now, but reframing is a key psychological device. Cope is useful to survive! If you have a shitty experience, there can be a tendency to just focus on the negative, but that’s actually an inaccurate way to view experience. Almost all experience has upside. Focus on it! What was good about it? Articulate it, write it down, feel the goodness of it. A lot of cognitive behavioral therapy (CBT) is oriented around this. It’s basically positive self-talk within a framework. </li><li>Becoming your own friend: I’m gonna work on a whole post on this, but learning to like yourself is the ultimate unlock and basically solves for most everything else. Just like you root for your friends, love your friends, and give them good advice, you gotta root for yourself, love yourself, and give yourself good advice. This is a consistent practice, and if your default is to be mean to yourself, either emotionally or verbally, it takes practice. I’m no where near done yet, but just the little bit I’ve done has dramatically helped. </li><li>Stop trying to be other people: You are yourself, that’s really all you can be. Learn to be the best version of that shamelessly. Trust your judgement of what this means. You will never be able to compete against people who are enjoying the experience of being themselves and doing what they’re doing. They will crush you. Just like you will crush others when you learn to do what you like and yourself. </li></ul><p>Appendix:</p><ul><li><a href="https://x.com/visakanv/status/1326456124422324225?s=20">https://x.com/visakanv/status/1326456124422324225?s=20</a></li><li><a href="https://x.com/devonzuegel/status/1770480691416834299?s=20">https://x.com/devonzuegel/status/1770480691416834299?s=20</a></li><li><a href="https://x.com/corymuscara/status/1597242735865503756?s=20">https://x.com/corymuscara/status/1597242735865503756?s=20</a></li></ul><![CDATA[Debugging Tartine Bread]]>https://www.jimmyhooker.com/blog/debugging-tartine-sourdough/https://www.jimmyhooker.com/blog/debugging-tartine-sourdough/Wed, 13 Mar 2024 00:00:00 GMT<p>Recipe for the uninitiated: <a href="https://tartinebakery.com/stories/country-bread">Tartine Country Loaf</a>. The <a href="https://a.co/d/1o5HBrA">book</a> is more detailed. </p><p>I used to live 2 blocks from Tartine in the Mission of SF. A year ago we moved to the East Bay to seek a more, let&#x27;s say, conducive atmosphere for our daughter to grow up in. But I missed having easy access to good bread! That is what got me started down this dark path.</p><p>I gotta say, this journey has been rough. Many times I would think I&#x27;d figured it out, only to then have a bad loaf and not understand what I did wrong. Things just seemed highly unpredictable. I watched tons of YouTube, read this forum, read the Sourdough Journey, the Bread Code, the Tartine book, Flour Water Salt Yeast, you get it. There was a lot of frustration, but I am an obsessive person by nature and I couldn&#x27;t stop trying to figure it out. At one point I was baking 3 batches a week to speed up my understanding of what was/wasn&#x27;t working. I&#x27;ve easily baked &gt; 100 batches in the last year.</p><p>A lot of advice was really hard to parse. I texted with an award-winning baker in Berkeley and tried to get help on internet forums (including this one), but it was really hard to figure out which variables were off or which piece of advice was right. Was it my starter? Was it my bulk fermentation time? Was the levain just not active enough when I started the dough? Was I just too rough with shaping? Changing one thing seemed to have an effect, but then it would stop for some reason, and I wasn&#x27;t sure whether to keep the change, revert the change, or change it again.</p><p>I&#x27;ve finally been able to consistently produce that bottom loaf. No bad loaves anymore. And these two things nailed it for me:</p><ul><li>Using way more than the recommended amount of starter in the levain. Like, 30-45 grams of starter. I think this is the true key that unlocked way better loaves. If you remember anything from this, it&#x27;s to just use more starter in your levain!</li><li>Starting with 98-100 degree water for bulk fermentation (our house is about 70 degrees). Because the levain was at ~70 degrees, it would bring water temp down a ton when mixed in. Then, fermantalyse inside the mixing bowl would bring the temp down even more.</li></ul><p>Just those two things have made it so I get a consistent loaf in 3.5 to 4.5 hours of bulk fermentation.</p><p>Things that didn&#x27;t work:</p><ul><li>Letting bulk fermentation go on much longer, like 6-8 hours (since I was getting underfermented loaves). For some reason this just produced weird loaves that would exhibit characteristics of under fermentation (caverns), but would also be impossible to work with like an over fermented loaf (ie the gluten had broken down and they were weirdly sticky and wet)</li><li>Feeding my starter every 12 hours (or more). This just sucked, was a huge hassle, felt wasteful, and didn&#x27;t meaningfully matter in my experience. Goal was to get it super active, and I guess that worked, but it was so much overhead. Super annoying.</li><li>Doing it by hand instead of using a KitchenAid mixer. I got some advice that maybe this was breaking down the gluten formation too much after fermantalyse. Didn&#x27;t seem to matter, that said I got in the habit of doing it by hand after fermantalyse because of that debugging journey.</li><li>Being super gentle with the dough, especially toward the end of bulk fermentation and shaping. I actually can&#x27;t say this did nothing, but the loaves were so inconsistent that this was like the equivalent of buying $300 running shoes when you just took up the hobby. It&#x27;s not what&#x27;s going to really move the needle at that level.</li></ul><p>In hindsight, I think some of the variability I was getting was due to a combination of starter activeness and the amount of starter I was mixing into the leavin. I just had no idea those were the key things. I also, especially in the beginning, just used a &#x27;tablespoon&#x27;, as described in the book. But, I wasn&#x27;t careful about measuring that out and I assume would get &#x27;lucky&#x27; occasionally when putting in more starter and get a better loaf.</p><p>Otherwise, I pretty much stick to the recipe as described. Here is what I do:</p><p><strong>Starter Maintenance</strong>:</p><ul><li>House is ~70 degrees Fahrenheit.</li><li>50g water, 50g 1:1 bread flour/wheat flour combo every 24 hours, always feed in the morning. Dump everything but 2 or so tablespoons of starter, maybe 30g.</li></ul><p><strong>Making Levain</strong>:</p><ul><li>Mix 30-45g of starter (depending on how active it seems to be) with 200g 1:1 bread flour/wheat mixture and 200g of 78-80 degree water.</li><li>Wait 12 hours</li></ul><p><strong>Making dough</strong>:</p><ul><li>Turn the oven light on (this brings the oven temp up to around 83-84 degrees, which is a nice temp to bulk ferment at).</li><li>Mix levain and flour/water in KitchenAid stand mixer. Use 98-102 degree water. Mix for a couple minutes on setting 2.</li><li>Wait 30 minutes for fermantalyse to happen.</li><li>Mix in 20g of Salt with 50g of 90-100 degree water (this seems to matter less, I&#x27;ve used 82 degree water and it was fine, but you&#x27;re just looking to maintain temp). I now use my hands, but I&#x27;m pretty confident you could use a stand mixer with a dough hook with no ill results.</li><li>At this point the dough is often around 82 degrees, maybe 84. In my experience it will keep dropping.</li><li>Put the dough in a <a href="https://www.amazon.com/gp/product/B07FXW61K1/ref=ppx_yo_dt_b_search_asin_title?ie=UTF8&amp;psc=1">Cambro container</a>, then put it in the warm oven to maintain temp</li><li>Do the stretch &amp; folds like normal every 30 minutes</li><li>After 3.5 to 4.5 hours, it should be doming. On the Cambro, sometimes the dough will show as being at or above the 2 liter line, but not always. As long as it&#x27;s doming, it seems good in my experience.</li><li>Shape into rounds, wait 30 min</li><li>Shape into loaves and drop into bannetons.</li></ul><p><strong>Overnight Retard</strong>:</p><p>I throw a tea towel over top the bannetons and toss in the fridge for what usually ends up being ~17 hours (I start the dough process in the morning and end by around 3pm). I&#x27;ve also used saran wrap to not develop a &#x27;skin&#x27;, but the effects of this don&#x27;t seem meaningful to me. I often bake one loaf, then wait another 2 days to bake the other one. It&#x27;s still great! The fridge seems to do more than slow it down, it seems to basically stop fermentation in my experience. Maybe my fridge is on the colder side.</p><p><strong>Baking:</strong></p><ul><li>Place a cookie sheet in the bottom rack to prevent the bottom of the dutch oven from getting too hot and burning the loaf</li><li>Set oven to 500 and wait for it to get to temp</li><li>Pull out the dutch oven, rice flour the bottom and toss in the loaf direct from the fridge. The coldness of the loaf makes no difference in my experience to letting it sit out while the oven pre-heats. It&#x27;s also easier to cut with a razer when it&#x27;s cold.</li><li>Score the loaf at a 45 degree angle to get that nice lip</li><li>Bring oven temp to 470 and bake for 20 minutes lid on</li><li>Take lid off dutch oven and bake for another 30 minutes</li></ul><p>I hope this helps someone who was as frustrated as I was, and helps them get a bit less lost. Just use more starter in your levain!</p><![CDATA[Being Grateful]]>https://www.jimmyhooker.com/blog/being-grateful/https://www.jimmyhooker.com/blog/being-grateful/Thu, 24 Nov 2022 00:00:00 GMT<p>I think I&#x27;ve been trying to figure out myself and my relationship to the world since I was about 17. About 20 years of introspection. That&#x27;s... maybe too much.</p><blockquote class="twitter-tweet"><p lang="en" dir="ltr">I think some people are more or less addicted to &quot;therapy&quot; and &quot;healing&quot;<br/><br/>developing real goals, even very simple goals, probably helps people move on better than many systematized approaches bc those systems (therapy) are more interested in perpetuating themselves <a href="https://t.co/CYMOsFuE9e">https://t.co/CYMOsFuE9e</a></p>— Simon Sarris (@simonsarris) <a href="https://twitter.com/simonsarris/status/1508550534269186055?ref_src=twsrc%5Etfw">March 28, 2022</a></blockquote> <script async="" src="https://platform.twitter.com/widgets.js" charSet="utf-8"></script><p>I got into spiritual stuff when I was pretty young because I was deeply anxious and it impeded a lot of what I wanted to do and how I interacted with people. Some of this had to do with my parents, but some was probably built in. I thought if I figured myself out, I could finally live as my true self without anxiety. But it never really worked out, despite throwing a lot of weaponry at it, including therapy, intensive meditation, supplements, exercise, and more. Those things helped, but it usually felt like I was dealing with symptoms rather than the core of the thing.</p><p>There are a <a href="https://www.health.harvard.edu/healthbeat/giving-thanks-can-make-you-happier">number of studies</a> that extol the virtues of gratitude. Start a gratitude journal! It will make you notably happier! But that always felt like work to me. It made gratitude into a job, and so, even after trying many times, I tended to avoid it. I didn&#x27;t disagree with the ideas, <a href="https://ava.substack.com/p/its-beautiful-i-just-dont-always">whenever I stepped back I realized my life was really pretty great</a>. I just couldn&#x27;t make it a consistent habit.</p><p>But one thing I&#x27;ve done recently has changed this. Pretty dramatically in my experience.</p><p>I spend a lot of time being critical. I&#x27;d go so far as to say 999 out of a 1000 thoughts are critical. I am constantly noticing things that are out of place, that could be improved, that are bad, that are dumb. This is hugely useful in a working context, it makes you detail oriented and motivated to improve things. But if you can&#x27;t shut that voice off, it becomes overwhelming and exhausting, coloring your whole experience. If you are almost only noticing the bad things, life seems... pretty bad!</p><p>But how do you change the ratio? How do you get to, let&#x27;s be optimistic, and say, 5% grateful/positive thoughts? 5 thoughts out of 100.</p><p>In the past, I would try and <em>convince</em> myself that life was good. But it felt fake. I was already convinced that things were shit, so saying things like &quot;life is amazing&quot; felt false. Like I was a bad salesman trying to pitch an unimpressed customer.</p><p>But a recent realization has changed this. You just have to go to the extreme, foundational basics. You have to get down to the sensory experience. Breathing is... cool actually?</p><p>Stay with me.</p><p>Right now, you are alive. The fact that you are alive is remarkable, actually. Feel your hands, notice the sensations surrounding them. The air as it touches them, their place in space. You are currently operating a body that is an incredible machine. You are the god of a kingdom of specialized cells, waiting to navigate you around earth. Notice your environment. How cool is it that you are here? It is objectively interesting that you exist in this space, right now.</p><p>If you can follow this, and legitimately appreciate it, you can re-center yourself in any moment. Any moment you feel overwhelmed or anxious, you can bring it back to the incredible-ness of existence, and build on that. Suddenly, the fact that you are in this stressful meeting with the CEO is really, really cool. It&#x27;s not a scary thing, it&#x27;s a remarkable experience.</p><p>Any time I catch myself feeling tense or tight, I realize that I&#x27;m caught up in some dumb thought process that is highly critical. I come back to how cool it is that I&#x27;m here, and then that I&#x27;m having whatever experience I&#x27;m having. And, man, I am super grateful.</p><p>I&#x27;m really lucky.</p><p>Best of luck changing the ratio, and Happy Thanksgiving!</p><![CDATA[Listening to your Inner Voice]]>https://www.jimmyhooker.com/blog/listening-to-your-inner-voice/https://www.jimmyhooker.com/blog/listening-to-your-inner-voice/Fri, 24 Jun 2022 00:00:00 GMT<p>Recently I learned about mimetic desire, originated by <a href="https://en.wikipedia.org/wiki/Mimetic_theory">Rene Girard</a>, but perhaps popularized in tech by <a href="https://www.businessinsider.com/peter-thiel-on-rene-girards-influence-2014-11#:~:text=Girard&#x27;s%20main%20concept%20is%20%22mimetic,scapegoat%20to%20return%20to%20balance.">Peter Thiel</a> who saw him as his philosophical mentor.</p><p>The TL;DR of mimetic desire is that almost all of our behavior is based on imitation of others, and that it&#x27;s inescapable. You want that nice car because other people see that nice car as valuable and that the larger culture has built up expectations around this object, not necessarily out of some pure desire on your part to have it. Same with prestigious jobs, schools, excess money, fancy clothes, etc.</p><p>If you&#x27;re in business, as Thiel points out, you could base the customers you go after, and the product decisions you make, on your competitors instead of solving for the deeper problems you might focus on if you made more customer-aware, focused decisions that were divorced from considering your competitors.</p><p>I have struggled my whole life with mimetic desire. Instead of understanding the things that I myself want to do, I base a lot of decision making on those around me. What is the smart decision, based on what others would think? What <em>should</em> I be doing with my time? What would impress people?</p><p>The key problem with this, and something I&#x27;ve found myself thinking about for many years, is that you end up without any of your own opinions.</p><p>It&#x27;s easy to see why we ended up this way. Human&#x27;s are tribal creatures with sophisticated social hierarchies that have a <a href="https://www.scientificamerican.com/article/whos-top-monkey-how-social-status-affects-immune-health/">dramatic impact on health and survival</a>. In the past, if you were ostracized from the group, it could mean certain death. So it makes sense that our brains evolved to care a great deal about what others think of us.</p><p>But, we live in a modern age! For those of us in America, we have more freedom, wealth, and independence than any civilization in history. One of the more difficult things about our age is the sheer quantity of choices we can make about our future, and the <a href="https://bschwartz.domains.swarthmore.edu/Sci.Amer.pdf">impossibility of choosing the &#x27;right&#x27; one</a>.</p><blockquote><p>A good plan, violently executed now, is better than a perfect plan next week. +<![CDATA[Jimmy Hooker's RSS Feed]]>https://www.jimmyhooker.comhttps://www.jimmyhooker.com/icon.pngJimmy Hooker's RSS Feedhttps://www.jimmyhooker.comGatsbyJSMon, 06 May 2024 17:17:56 GMT<![CDATA[How to Not Be Full of Shit as a Product Manager]]>https://www.jimmyhooker.com/blog/how-to-not-be-full-of-shit-as-a-product-manager/https://www.jimmyhooker.com/blog/how-to-not-be-full-of-shit-as-a-product-manager/Fri, 03 May 2024 00:00:00 GMT<p>So you’ve joined a new team, company, etc and you are experiencing the cold terror that is your new responsibility area as a product manager.</p><p>I think you have about 90 days to demonstrate that you are going to be a net positive addition to the team and to build up trust. What do you do?</p><p>This assumes:</p><ul><li>The product already exists</li><li>There are existing users</li></ul><p>My opinion on this is you need to:</p><ul><li><strong>Get ahold of as many customers as possible within the next 30 days and have them walk you through their product usage and your product feature in particular</strong>. Have them share their screen while they do this. Write out your questions loosely beforehand, and adjust rapidly with the first few customers until you get a feel for which questions elicit the best answers. Record everything, and then tag key statements around pain points, etc, with a tool like Dovetail.</li><li><strong>Insist on involving the engineering manager and designer associated with your product area in the customer discussions</strong>. This has multiple benefits, including demonstrating your ability to understand customer pain points and developing shared context between your key stakeholders.</li><li><strong>Learn where all the bodies are buried in the data</strong>. Do not rely on the data team, you need to get familiar with querying raw data. Know the key tables and how to produce good output.</li><li><strong>Manage your stress</strong>. Exercise, take supplements, and sleep enough that you don’t freak out. If you are doing this right, the first 90-days should feel like watching ‘Uncut Gems’ nonstop. <a href="https://www.jimmyhooker.com/blog/strategies-for-dealing-with-anxiety-and-stress/">Act accordingly</a>.</li><li><strong>Ignore everything that is not the above</strong>. No one will really care that you followed whatever process/ritual properly if your product area doesn’t deliver. Let people chase you down and yell at you for things that aren’t really consequential. You need to hold your responsibility to your product area above all else. That doesn’t mean you need to be unnecessarily rude or obnoxious, but it’s ok to drop balls that don’t matter.</li></ul><p>That’s it. I think the last point is worth really worth emphasizing. It’s really easy to get caught up in exercises that feel useful but don’t solve real problems. Do not be the person who has nice narratives but isn’t driving value. Understand your product area through customers, include your key stakeholders, build an aligned plan, and articulate it to leadership. That is all that matters.</p><p>Best of luck!</p><![CDATA[How to Deliver Good Presentations]]>https://www.jimmyhooker.com/blog/how-to-deliver-good-presentations/https://www.jimmyhooker.com/blog/how-to-deliver-good-presentations/Tue, 26 Mar 2024 00:00:00 GMT<p>This is actually pretty simple to do well, but you really have to put in the practice time. Don&#x27;t kid yourself, it is humiliating to fuck up a presentation because you didn&#x27;t practice enough. Unforced Error. Here&#x27;s how to do it.</p><ol><li>Do an ‘off the cuff’ version with no slides, or minimal slides.</li><li>Experience the visceral terror of how bad the presentation will be if you do it ‘off the cuff’ or unpracticed.</li><li>Do it a couple more times to really fire up the ‘presentation’ neurons</li><li>Write down what you want to say like it was a speech, literally word for word what you want to say to the audience. </li><li>Practice the written down version a couple times</li><li>Edit it heavily</li><li>Practice 5-10 more times, making little adjustments, until you can mostly do it without looking at your text</li></ol><p>Voila. Your presentation will most likely not suck, and you will have a confident handle on the material so that you can dynamically deliver it in front of an audience with a relative sense of comfort. </p><![CDATA[Strategies for dealing with Anxiety and Stress]]>https://www.jimmyhooker.com/blog/strategies-for-dealing-with-anxiety-and-stress/https://www.jimmyhooker.com/blog/strategies-for-dealing-with-anxiety-and-stress/Sat, 23 Mar 2024 00:00:00 GMT<p>I have experienced anxiety my whole life, at times to degrees that made it really difficult to just have normal, every day interactions. However, I’ve always simultaneously pushed myself really, really hard. Which meant I had to develop coping strategies to survive. Here’s what worked. </p><h2>Supplements</h2><ul><li>Zembrin: Basically calm, energized focus in a pill. No side effects that I’ve seen. Remarkable supplement. If you have trouble with focus and energy, try this first. </li><li>L-Theanine: Gits rid of the jitters from caffeine. Naturally occurring in Green Tea, if you are sensitive to the effects of caffeine it basically gives you all the good without the bad. Completely changed coffee for me. </li><li>Lavender Pills: Noticeably calmer but not so much that it feels like a hindrance or produces side-effects. Pleasantly calm. </li><li>Magnesium Citrate: Useful to go to sleep, just be careful to not take too much. It is a diarrhetic and will give you wild gas if you over do it. </li></ul><h2>Medications</h2><ul><li>Beta Blockers: Great if you experience situational anxiety so intense that there’s no real “takeaways” or learning from the experience. Very useful for things like interviews where it’s harder to get “at bats” and the stakes are high. The key feature is it makes it so your anxiety/fear doesn’t produce physical side-effects, like shakey or sweaty hands, rapid heart rate, twitching, etc. It’s non-addictive and isn’t mind altering, but beware that it keeps heart rate down so you can’t exercise on it (your heart rate won’t keep up with the exercise). The cool thing is you still experience the fear, but it is more thrilling because of the lake of physical stuff. </li></ul><h2>Exercise / Sleep</h2><ul><li>Intense Exercise: I ride my bike up hills so steep they cause me to lose feeling in my fingers, and I feel incredible, almost euphoric afterward. I have related this to others, and almost everyone who does some form of exercise that is so intense they can’t think during parts of it remarks the same thing. You just feel great afterward. Doesn’t need to take a long time, 20 minutes of exercise that includes 5 minutes of super high intensity is great. I like biking because I get to see cool stuff while I’m doing it, and see way more than other exercise, but do whatever you like. If you like it, you’ll do it more often. </li><li>Minimum strength training: I can do about 50 push-ups in a single set along with 50 squats at the age of 40. I never do more than 1 set of these in a day. I know very few people who can do this at any age, much less 40. The trick is that you never push yourself too hard. Make it easy, and keep it easy. It takes very little of my time and it makes my body feel great. </li><li>Get an amount of sleep that makes you feel good. This really varies from person to person. I usually only let myself get 7 hours minimum, but I probably could use 9. My wife gets 9 and feels great. I wish I could discipline myself to do this, I think it’s probably the next big unlock for me. </li></ul><h2>Meditation</h2><ul><li>Ana Pana aka breath meditation/focus: This is what you do for the first 3 days of a 10-day sit when doing a Goenka-style Vipassana. Just focus on the breath leaving/entering your nose. Pay attention to everything about it, go ‘deep’ on your attention, notice the humidity, the sensation, how one nostril is closed, the quality of the breath. See how long you can focus before your mind wanders. 1 minute is a big achievement here. Quick, <a href="https://www.youtube.com/watch?v=eyM_c3wSh8s">Goenka-led 15-minute version</a>. </li><li>Vipassana aka “body scanning”: Start at the top of your head, and slowly move down your body, focusing on the sensation on your skin. If you are doing this right (ie with the right level of focus), you can usually feel your body releasing tension while you’re doing it. This can be hard to do well without getting good at Ana Pana first. </li></ul><h2>Little Cheats</h2><ul><li><a href="https://www.healthline.com/health/box-breathing#slowly-exhale">Box Breathing</a>: 4 seconds breathe in, 4 seconds hold, 4 seconds breathe out, 4 seconds hold. Repeat. Has a noticeable calming effect. Do this for a couple minutes and you can quickly center. </li><li>Cold Showers: Aka <a href="https://www.wimhofmethod.com/benefits-of-cold-showers">Wim Hoff showers</a>. The cheat here is to take a hot shower, and then at the end switch to cold. It’s a great systemic wake up, see how long you can do it!</li></ul><h2>Psychology</h2><ul><li>Talking to yourself/coaching yourself: You can <a href="https://x.com/devonzuegel/status/1770480691416834299?s=20">talk to yourself in a friendly way</a>, as if you are coaching a friend. E.g. “Let’s not think about that right now, we can think about it later. It’s time to go to sleep, let’s go to sleep”. “It’s time to focus, we need to focus on this and get it done. Let’s focus on this task really hard”. “Tonight when we sleep let’s really clear the brain, we don’t need to repair physically as much.” It is kind of insane how well this works. </li><li>Reframing: The kids probably call this ‘cope’ now, but reframing is a key psychological device. Cope is useful to survive! If you have a shitty experience, there can be a tendency to just focus on the negative, but that’s actually an inaccurate way to view experience. Almost all experience has upside. Focus on it! What was good about it? Articulate it, write it down, feel the goodness of it. A lot of cognitive behavioral therapy (CBT) is oriented around this. It’s basically positive self-talk within a framework. </li><li>Becoming your own friend: I’m gonna work on a whole post on this, but learning to like yourself is the ultimate unlock and basically solves for most everything else. Just like you root for your friends, love your friends, and give them good advice, you gotta root for yourself, love yourself, and give yourself good advice. This is a consistent practice, and if your default is to be mean to yourself, either emotionally or verbally, it takes practice. I’m no where near done yet, but just the little bit I’ve done has dramatically helped. </li><li>Stop trying to be other people: You are yourself, that’s really all you can be. Learn to be the best version of that shamelessly. Trust your judgement of what this means. You will never be able to compete against people who are enjoying the experience of being themselves and doing what they’re doing. They will crush you. Just like you will crush others when you learn to do what you like and yourself. </li></ul><p>Appendix:</p><ul><li><a href="https://x.com/visakanv/status/1326456124422324225?s=20">https://x.com/visakanv/status/1326456124422324225?s=20</a></li><li><a href="https://x.com/devonzuegel/status/1770480691416834299?s=20">https://x.com/devonzuegel/status/1770480691416834299?s=20</a></li><li><a href="https://x.com/corymuscara/status/1597242735865503756?s=20">https://x.com/corymuscara/status/1597242735865503756?s=20</a></li></ul><![CDATA[Debugging Tartine Bread]]>https://www.jimmyhooker.com/blog/debugging-tartine-sourdough/https://www.jimmyhooker.com/blog/debugging-tartine-sourdough/Wed, 13 Mar 2024 00:00:00 GMT<p>Recipe for the uninitiated: <a href="https://tartinebakery.com/stories/country-bread">Tartine Country Loaf</a>. The <a href="https://a.co/d/1o5HBrA">book</a> is more detailed. </p><p>I used to live 2 blocks from Tartine in the Mission of SF. A year ago we moved to the East Bay to seek a more, let&#x27;s say, conducive atmosphere for our daughter to grow up in. But I missed having easy access to good bread! That is what got me started down this dark path.</p><p>I gotta say, this journey has been rough. Many times I would think I&#x27;d figured it out, only to then have a bad loaf and not understand what I did wrong. Things just seemed highly unpredictable. I watched tons of YouTube, read this forum, read the Sourdough Journey, the Bread Code, the Tartine book, Flour Water Salt Yeast, you get it. There was a lot of frustration, but I am an obsessive person by nature and I couldn&#x27;t stop trying to figure it out. At one point I was baking 3 batches a week to speed up my understanding of what was/wasn&#x27;t working. I&#x27;ve easily baked &gt; 100 batches in the last year.</p><p>A lot of advice was really hard to parse. I texted with an award-winning baker in Berkeley and tried to get help on internet forums (including this one), but it was really hard to figure out which variables were off or which piece of advice was right. Was it my starter? Was it my bulk fermentation time? Was the levain just not active enough when I started the dough? Was I just too rough with shaping? Changing one thing seemed to have an effect, but then it would stop for some reason, and I wasn&#x27;t sure whether to keep the change, revert the change, or change it again.</p><p>I&#x27;ve finally been able to consistently produce that bottom loaf. No bad loaves anymore. And these two things nailed it for me:</p><ul><li>Using way more than the recommended amount of starter in the levain. Like, 30-45 grams of starter. I think this is the true key that unlocked way better loaves. If you remember anything from this, it&#x27;s to just use more starter in your levain!</li><li>Starting with 98-100 degree water for bulk fermentation (our house is about 70 degrees). Because the levain was at ~70 degrees, it would bring water temp down a ton when mixed in. Then, fermantalyse inside the mixing bowl would bring the temp down even more.</li></ul><p>Just those two things have made it so I get a consistent loaf in 3.5 to 4.5 hours of bulk fermentation.</p><p>Things that didn&#x27;t work:</p><ul><li>Letting bulk fermentation go on much longer, like 6-8 hours (since I was getting underfermented loaves). For some reason this just produced weird loaves that would exhibit characteristics of under fermentation (caverns), but would also be impossible to work with like an over fermented loaf (ie the gluten had broken down and they were weirdly sticky and wet)</li><li>Feeding my starter every 12 hours (or more). This just sucked, was a huge hassle, felt wasteful, and didn&#x27;t meaningfully matter in my experience. Goal was to get it super active, and I guess that worked, but it was so much overhead. Super annoying.</li><li>Doing it by hand instead of using a KitchenAid mixer. I got some advice that maybe this was breaking down the gluten formation too much after fermantalyse. Didn&#x27;t seem to matter, that said I got in the habit of doing it by hand after fermantalyse because of that debugging journey.</li><li>Being super gentle with the dough, especially toward the end of bulk fermentation and shaping. I actually can&#x27;t say this did nothing, but the loaves were so inconsistent that this was like the equivalent of buying $300 running shoes when you just took up the hobby. It&#x27;s not what&#x27;s going to really move the needle at that level.</li></ul><p>In hindsight, I think some of the variability I was getting was due to a combination of starter activeness and the amount of starter I was mixing into the leavin. I just had no idea those were the key things. I also, especially in the beginning, just used a &#x27;tablespoon&#x27;, as described in the book. But, I wasn&#x27;t careful about measuring that out and I assume would get &#x27;lucky&#x27; occasionally when putting in more starter and get a better loaf.</p><p>Otherwise, I pretty much stick to the recipe as described. Here is what I do:</p><p><strong>Starter Maintenance</strong>:</p><ul><li>House is ~70 degrees Fahrenheit.</li><li>50g water, 50g 1:1 bread flour/wheat flour combo every 24 hours, always feed in the morning. Dump everything but 2 or so tablespoons of starter, maybe 30g.</li></ul><p><strong>Making Levain</strong>:</p><ul><li>Mix 30-45g of starter (depending on how active it seems to be) with 200g 1:1 bread flour/wheat mixture and 200g of 78-80 degree water.</li><li>Wait 12 hours</li></ul><p><strong>Making dough</strong>:</p><ul><li>Turn the oven light on (this brings the oven temp up to around 83-84 degrees, which is a nice temp to bulk ferment at).</li><li>Mix levain and flour/water in KitchenAid stand mixer. Use 98-102 degree water. Mix for a couple minutes on setting 2.</li><li>Wait 30 minutes for fermantalyse to happen.</li><li>Mix in 20g of Salt with 50g of 90-100 degree water (this seems to matter less, I&#x27;ve used 82 degree water and it was fine, but you&#x27;re just looking to maintain temp). I now use my hands, but I&#x27;m pretty confident you could use a stand mixer with a dough hook with no ill results.</li><li>At this point the dough is often around 82 degrees, maybe 84. In my experience it will keep dropping.</li><li>Put the dough in a <a href="https://www.amazon.com/gp/product/B07FXW61K1/ref=ppx_yo_dt_b_search_asin_title?ie=UTF8&amp;psc=1">Cambro container</a>, then put it in the warm oven to maintain temp</li><li>Do the stretch &amp; folds like normal every 30 minutes</li><li>After 3.5 to 4.5 hours, it should be doming. On the Cambro, sometimes the dough will show as being at or above the 2 liter line, but not always. As long as it&#x27;s doming, it seems good in my experience.</li><li>Shape into rounds, wait 30 min</li><li>Shape into loaves and drop into bannetons.</li></ul><p><strong>Overnight Retard</strong>:</p><p>I throw a tea towel over top the bannetons and toss in the fridge for what usually ends up being ~17 hours (I start the dough process in the morning and end by around 3pm). I&#x27;ve also used saran wrap to not develop a &#x27;skin&#x27;, but the effects of this don&#x27;t seem meaningful to me. I often bake one loaf, then wait another 2 days to bake the other one. It&#x27;s still great! The fridge seems to do more than slow it down, it seems to basically stop fermentation in my experience. Maybe my fridge is on the colder side.</p><p><strong>Baking:</strong></p><ul><li>Place a cookie sheet in the bottom rack to prevent the bottom of the dutch oven from getting too hot and burning the loaf</li><li>Set oven to 500 and wait for it to get to temp</li><li>Pull out the dutch oven, rice flour the bottom and toss in the loaf direct from the fridge. The coldness of the loaf makes no difference in my experience to letting it sit out while the oven pre-heats. It&#x27;s also easier to cut with a razer when it&#x27;s cold.</li><li>Score the loaf at a 45 degree angle to get that nice lip</li><li>Bring oven temp to 470 and bake for 20 minutes lid on</li><li>Take lid off dutch oven and bake for another 30 minutes</li></ul><p>I hope this helps someone who was as frustrated as I was, and helps them get a bit less lost. Just use more starter in your levain!</p><![CDATA[Being Grateful]]>https://www.jimmyhooker.com/blog/being-grateful/https://www.jimmyhooker.com/blog/being-grateful/Thu, 24 Nov 2022 00:00:00 GMT<p>I think I&#x27;ve been trying to figure out myself and my relationship to the world since I was about 17. About 20 years of introspection. That&#x27;s... maybe too much.</p><blockquote class="twitter-tweet"><p lang="en" dir="ltr">I think some people are more or less addicted to &quot;therapy&quot; and &quot;healing&quot;<br/><br/>developing real goals, even very simple goals, probably helps people move on better than many systematized approaches bc those systems (therapy) are more interested in perpetuating themselves <a href="https://t.co/CYMOsFuE9e">https://t.co/CYMOsFuE9e</a></p>— Simon Sarris (@simonsarris) <a href="https://twitter.com/simonsarris/status/1508550534269186055?ref_src=twsrc%5Etfw">March 28, 2022</a></blockquote> <script async="" src="https://platform.twitter.com/widgets.js" charSet="utf-8"></script><p>I got into spiritual stuff when I was pretty young because I was deeply anxious and it impeded a lot of what I wanted to do and how I interacted with people. Some of this had to do with my parents, but some was probably built in. I thought if I figured myself out, I could finally live as my true self without anxiety. But it never really worked out, despite throwing a lot of weaponry at it, including therapy, intensive meditation, supplements, exercise, and more. Those things helped, but it usually felt like I was dealing with symptoms rather than the core of the thing.</p><p>There are a <a href="https://www.health.harvard.edu/healthbeat/giving-thanks-can-make-you-happier">number of studies</a> that extol the virtues of gratitude. Start a gratitude journal! It will make you notably happier! But that always felt like work to me. It made gratitude into a job, and so, even after trying many times, I tended to avoid it. I didn&#x27;t disagree with the ideas, <a href="https://ava.substack.com/p/its-beautiful-i-just-dont-always">whenever I stepped back I realized my life was really pretty great</a>. I just couldn&#x27;t make it a consistent habit.</p><p>But one thing I&#x27;ve done recently has changed this. Pretty dramatically in my experience.</p><p>I spend a lot of time being critical. I&#x27;d go so far as to say 999 out of a 1000 thoughts are critical. I am constantly noticing things that are out of place, that could be improved, that are bad, that are dumb. This is hugely useful in a working context, it makes you detail oriented and motivated to improve things. But if you can&#x27;t shut that voice off, it becomes overwhelming and exhausting, coloring your whole experience. If you are almost only noticing the bad things, life seems... pretty bad!</p><p>But how do you change the ratio? How do you get to, let&#x27;s be optimistic, and say, 5% grateful/positive thoughts? 5 thoughts out of 100.</p><p>In the past, I would try and <em>convince</em> myself that life was good. But it felt fake. I was already convinced that things were shit, so saying things like &quot;life is amazing&quot; felt false. Like I was a bad salesman trying to pitch an unimpressed customer.</p><p>But a recent realization has changed this. You just have to go to the extreme, foundational basics. You have to get down to the sensory experience. Breathing is... cool actually?</p><p>Stay with me.</p><p>Right now, you are alive. The fact that you are alive is remarkable, actually. Feel your hands, notice the sensations surrounding them. The air as it touches them, their place in space. You are currently operating a body that is an incredible machine. You are the god of a kingdom of specialized cells, waiting to navigate you around earth. Notice your environment. How cool is it that you are here? It is objectively interesting that you exist in this space, right now.</p><p>If you can follow this, and legitimately appreciate it, you can re-center yourself in any moment. Any moment you feel overwhelmed or anxious, you can bring it back to the incredible-ness of existence, and build on that. Suddenly, the fact that you are in this stressful meeting with the CEO is really, really cool. It&#x27;s not a scary thing, it&#x27;s a remarkable experience.</p><p>Any time I catch myself feeling tense or tight, I realize that I&#x27;m caught up in some dumb thought process that is highly critical. I come back to how cool it is that I&#x27;m here, and then that I&#x27;m having whatever experience I&#x27;m having. And, man, I am super grateful.</p><p>I&#x27;m really lucky.</p><p>Best of luck changing the ratio, and Happy Thanksgiving!</p><![CDATA[Listening to your Inner Voice]]>https://www.jimmyhooker.com/blog/listening-to-your-inner-voice/https://www.jimmyhooker.com/blog/listening-to-your-inner-voice/Fri, 24 Jun 2022 00:00:00 GMT<p>Recently I learned about mimetic desire, originated by <a href="https://en.wikipedia.org/wiki/Mimetic_theory">Rene Girard</a>, but perhaps popularized in tech by <a href="https://www.businessinsider.com/peter-thiel-on-rene-girards-influence-2014-11#:~:text=Girard&#x27;s%20main%20concept%20is%20%22mimetic,scapegoat%20to%20return%20to%20balance.">Peter Thiel</a> who saw him as his philosophical mentor.</p><p>The TL;DR of mimetic desire is that almost all of our behavior is based on imitation of others, and that it&#x27;s inescapable. You want that nice car because other people see that nice car as valuable and that the larger culture has built up expectations around this object, not necessarily out of some pure desire on your part to have it. Same with prestigious jobs, schools, excess money, fancy clothes, etc.</p><p>If you&#x27;re in business, as Thiel points out, you could base the customers you go after, and the product decisions you make, on your competitors instead of solving for the deeper problems you might focus on if you made more customer-aware, focused decisions that were divorced from considering your competitors.</p><p>I have struggled my whole life with mimetic desire. Instead of understanding the things that I myself want to do, I base a lot of decision making on those around me. What is the smart decision, based on what others would think? What <em>should</em> I be doing with my time? What would impress people?</p><p>The key problem with this, and something I&#x27;ve found myself thinking about for many years, is that you end up without any of your own opinions.</p><p>It&#x27;s easy to see why we ended up this way. Human&#x27;s are tribal creatures with sophisticated social hierarchies that have a <a href="https://www.scientificamerican.com/article/whos-top-monkey-how-social-status-affects-immune-health/">dramatic impact on health and survival</a>. In the past, if you were ostracized from the group, it could mean certain death. So it makes sense that our brains evolved to care a great deal about what others think of us.</p><p>But, we live in a modern age! For those of us in America, we have more freedom, wealth, and independence than any civilization in history. One of the more difficult things about our age is the sheer quantity of choices we can make about our future, and the <a href="https://bschwartz.domains.swarthmore.edu/Sci.Amer.pdf">impossibility of choosing the &#x27;right&#x27; one</a>.</p><blockquote><p>A good plan, violently executed now, is better than a perfect plan next week. — General George Patton</p></blockquote><p>A difficult truth to accept is that it&#x27;s often better to make a bad decision than no decision. We want to be able to look back at our thinking and have it be thoughtful, defensible, clear, articulate, and well supported. But, <a href="https://fs.blog/reversible-irreversible-decisions/">most decisions are reversible</a>! It&#x27;s often better to &#x27;find out&#x27; than to spend endless amounts of time hand-wringing, researching, etc.</p><p>I work in product management, but I&#x27;ve also been on the pure creation side as well. I often think of product management as a debug for bad intuition, helping make sure that what you are building is actually something people want vs something you assume people want. You talk to customers, do the research, and build up a sense of what will actually work that is well supported.</p><p>However, after a while, you build up tacit knowledge. Knowledge that largely feels like instinct, as it&#x27;s been built into muscle memory. You can&#x27;t entirely explain why you know a decision is right, but you know. It&#x27;s been <a href="https://commoncog.com/blog/tacit-knowledge-is-a-real-thing/">born through experience</a>.</p><p>A few years ago I left a job at a bootstrapped, scrappy startup called <a href="https://www.badgermapping.com">Badger Maps</a> to join the professionalized product management world. This threw a lot of my opinions and assumptions into doubt. Did my work at Badger translate? Was the tacit knowledge that I built up over time useful in another context?</p><p>I had to revalidate a lot of my thinking and reassess the way I did things. This made trusting my tacit knowledge difficult, and not trusting your gut can lead to bad decisions, plenty of which I made.</p><p>As Thiel highlights from Gerards work, imitation is inescapable, but it&#x27;s possible to be aware of it and double-check yourself.</p><p>After considering everyone else&#x27;s opinions, what do you still want to do? What still feels right? Check in with yourself! Don&#x27;t do the thing you don&#x27;t want to do! Ask yourself: &quot;What do I actually want to do?&quot;.</p><![CDATA[How to Not Be Cynical]]>https://www.jimmyhooker.com/blog/how-to-not-be-cynical/https://www.jimmyhooker.com/blog/how-to-not-be-cynical/Fri, 10 Jun 2022 00:00:00 GMT<p>I was watching YouTube and I ran into this remarkable interview:</p><div url="https://www.youtube.com/embed/JEz8IXWOdi8" title="Ruby Interview"></div><p>Ruby is incredible. It&#x27;s clear that she is just a force of nature. She exudes life at the age of 87, and as &#x27;limber as a dishrag&#x27;, she just seems invigorated.</p><p>I&#x27;ve often been accused of being overly critical. I like to think I am, on the overall, a pretty optimistic person. I feel like most problems are solvable and that life is getting better and better. This period of time is, imo, one of the most exciting to be alive.</p><p>On the one hand, my critical nature has given me an eye for details and a high bar that I set for myself. On the other, I do worry that my critical nature can often bleed into and tinge experiences that could otherwise be really pleasant and enjoyable. It&#x27;s very much so a double-edged sword.</p><p>My wife Sara is much more even keeled. She has a balanced nature, is generally happy, and often brings joy to those who spend time in her company. She balances me, and often makes me realize that I need to chill a little bit. Maybe enjoy the short time we have here.</p><blockquote class="twitter-tweet"><p lang="en" dir="ltr">You only get to be alive for a brief interval. Maybe 4000 weeks if you&#x27;re lucky.<br/><br/>Try to enjoy it!</p>— Nat Friedman (@natfriedman) <a href="https://twitter.com/natfriedman/status/1454981538672611331?ref_src=twsrc%5Etfw">November 1, 2021</a></blockquote> <script async="" src="https://platform.twitter.com/widgets.js" charSet="utf-8"></script><p>I want to write about this a bit more, but @visakanv and @nickcammarata have been some of my favorite follows more recently because of their incredible focus on positivity in a way that feels legible to me. Visa often encourages frame changes, while Nick focuses on meditation and qualia.</p><p>Can you be critical without being cynical? Without becoming overly judgmental?</p><div url="https://www.youtube.com/embed/KFupT9flepY" title="Conan O&#x27;Brian don&#x27;t be cynical"></div><p>It feels like a difficult tight rope to walk.</p><p>Life is amazing! It&#x27;s incredible we get this opportunity!</p><p>I hope I don&#x27;t waste too much of it focusing on the wrong things.</p><![CDATA[Art Diario - The most complicated thing I've built]]>https://www.jimmyhooker.com/blog/art-diario-the-most-complicated-thing-ive-built/https://www.jimmyhooker.com/blog/art-diario-the-most-complicated-thing-ive-built/Sun, 12 Sep 2021 00:00:00 GMT<p>TL;DR - You should <a href="https://www.artdiario.com/">download my simple little app</a> that I have poured an insane amount of effort into making. Please only tell me that you love it, but also report bugs!</p><p>About two years ago I started <a href="https://lambdaschool.com">Lambda School</a> part time. While I&#x27;d always been a technical kid, I never was able to dedicate the time to fill in the gaps of my programming knowledge. I could hack things together, but I lacked the ability to create cohesive CRUD apps. Anything beyond simple functions was way too much for me. I barely understood what an array was. Lambda School was the first time I could commit to learning programming without quitting my day job, and I jumped at the opportunity.</p><p>There&#x27;s a lot more to this story, and I want to write about it in more depth, but the long and short is that, while brutal, it worked, and I now know how to program in a lot more depth.</p><p>A while back I stumbled on an app called <a href="https://artpip.com/">ArtPip</a>. It had this core idea of showing everyone who had the app the same piece of art per day. I loved it. I loved being exposed to art that I would have never otherwise seen, I loved how it lasted for 24 hours, and I loved that it was a shared experience with everyone who had the app. Unfortunately, the engineer building it wasn&#x27;t able to continue, and eventually the app itself switched over to photographs instead of paintings. Photographs are great, but they&#x27;re an entirely different medium. It felt like the core reason that I wanted to have the app installed was gone.</p><p>How much I loved this concept really stuck with me. When the app died originally I really wanted to recreate it right then, but it would be another year until I started Lambda School, a year from then when I would create my first version, and another year after that until I could really polish it into something that other people could use.</p><p><span class="gatsby-resp-image-wrapper" style="position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1158px"> <span class="gatsby-resp-image-background-image" style="padding-bottom:37.333333333333336%;position:relative;bottom:0;left:0;background-image:url(&#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAAsTAAALEwEAmpwYAAAA2klEQVQoz41Ry26DQAzkr3uIqh5zye+FVJHyA7x3WfAuC0w0Vh0lVVXF0miMscdjKG7Xb1wvZ4hEdF2HZVkgIkgpIYSAaZoQY8S+78rzPP9AEFPSGmeWnLGuK4rT1wHHzw/0zqMsL3DOP0SccxjHUQUYZC4JYVLm4hjlIbptGwrRraIvm6ZRl2RiGAZ9rqoKbdvinSgs4UYO0xWFjJ9zurWcvX3fw3uvzJo65Lch2EgndMYGgkMGq/9exiVWo44KMjhU1/WLCMFTWSeY55z/P9kE7U++E3bVX7gDSpsbxp1l5gYAAAAASUVORK5CYII=&#x27;);background-size:cover;display:block"></span> <img width="1200" class="gatsby-resp-image-image" alt="This is an image" title="This is an image" src="https://www.jimmyhooker.com/static/af0d1d51b2f08a348d7050243388fb50/1132d/artdiario-artpip-email.png" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0" loading="lazy" decoding="async"/> diff --git a/sitemap/sitemap-0.xml b/sitemap/sitemap-0.xml index d49e8ef..af362fc 100644 --- a/sitemap/sitemap-0.xml +++ b/sitemap/sitemap-0.xml @@ -1 +1 @@ -https://www.jimmyhooker.com/about/daily0.7https://www.jimmyhooker.com/donate/daily0.7https://www.jimmyhooker.com/daily0.7https://www.jimmyhooker.com/portfolio/daily0.7https://www.jimmyhooker.com/blog/art-diario-the-most-complicated-thing-ive-built/daily0.7https://www.jimmyhooker.com/blog/being-curious/daily0.7https://www.jimmyhooker.com/blog/being-grateful/daily0.7https://www.jimmyhooker.com/blog/design-cant-always-be-rationalized/daily0.7https://www.jimmyhooker.com/blog/debugging-tartine-sourdough/daily0.7https://www.jimmyhooker.com/blog/design-for-repetition/daily0.7https://www.jimmyhooker.com/hidden/dont-leave-meetings-confused/daily0.7https://www.jimmyhooker.com/blog/feedback-loops-momentum-excitement/daily0.7https://www.jimmyhooker.com/blog/how-to-deliver-good-presentations/daily0.7https://www.jimmyhooker.com/blog/how-to-not-be-cynical/daily0.7https://www.jimmyhooker.com/blog/how-to-not-be-full-of-shit-as-a-product-manager/daily0.7https://www.jimmyhooker.com/blog/listening-to-your-inner-voice/daily0.7https://www.jimmyhooker.com/blog/strategies-for-dealing-with-anxiety-and-stress/daily0.7https://www.jimmyhooker.com/blog/the-crippling-imaginary-audience/daily0.7https://www.jimmyhooker.com/portfolio/associates-dein/daily0.7https://www.jimmyhooker.com/portfolio/badger-maps/daily0.7https://www.jimmyhooker.com/portfolio/cloudhike/daily0.7https://www.jimmyhooker.com/portfolio/fahrenheit-labs/daily0.7https://www.jimmyhooker.com/portfolio/hank/daily0.7https://www.jimmyhooker.com/portfolio/pocket-bodhi/daily0.7https://www.jimmyhooker.com/portfolio/rocky-mountain-ruby/daily0.7 \ No newline at end of file +https://www.jimmyhooker.com/about/daily0.7https://www.jimmyhooker.com/donate/daily0.7https://www.jimmyhooker.com/daily0.7https://www.jimmyhooker.com/portfolio/daily0.7https://www.jimmyhooker.com/blog/art-diario-the-most-complicated-thing-ive-built/daily0.7https://www.jimmyhooker.com/blog/being-curious/daily0.7https://www.jimmyhooker.com/blog/being-grateful/daily0.7https://www.jimmyhooker.com/blog/debugging-tartine-sourdough/daily0.7https://www.jimmyhooker.com/blog/design-cant-always-be-rationalized/daily0.7https://www.jimmyhooker.com/blog/design-for-repetition/daily0.7https://www.jimmyhooker.com/hidden/dont-leave-meetings-confused/daily0.7https://www.jimmyhooker.com/blog/feedback-loops-momentum-excitement/daily0.7https://www.jimmyhooker.com/blog/how-to-deliver-good-presentations/daily0.7https://www.jimmyhooker.com/blog/how-to-not-be-cynical/daily0.7https://www.jimmyhooker.com/blog/how-to-not-be-full-of-shit-as-a-product-manager/daily0.7https://www.jimmyhooker.com/blog/listening-to-your-inner-voice/daily0.7https://www.jimmyhooker.com/blog/strategies-for-dealing-with-anxiety-and-stress/daily0.7https://www.jimmyhooker.com/blog/the-crippling-imaginary-audience/daily0.7https://www.jimmyhooker.com/portfolio/associates-dein/daily0.7https://www.jimmyhooker.com/portfolio/badger-maps/daily0.7https://www.jimmyhooker.com/portfolio/cloudhike/daily0.7https://www.jimmyhooker.com/portfolio/fahrenheit-labs/daily0.7https://www.jimmyhooker.com/portfolio/hank/daily0.7https://www.jimmyhooker.com/portfolio/pocket-bodhi/daily0.7https://www.jimmyhooker.com/portfolio/rocky-mountain-ruby/daily0.7 \ No newline at end of file diff --git a/webpack-runtime-56bc60d9a2f6029973eb.js b/webpack-runtime-56bc60d9a2f6029973eb.js deleted file mode 100644 index 99f87fd..0000000 --- a/webpack-runtime-56bc60d9a2f6029973eb.js +++ /dev/null @@ -1,2 +0,0 @@ -!function(){"use strict";var e,t,n,r,o,c={},u={};function i(e){var t=u[e];if(void 0!==t)return t.exports;var n=u[e]={exports:{}};return c[e].call(n.exports,n,n.exports,i),n.exports}i.m=c,e=[],i.O=function(t,n,r,o){if(!n){var c=1/0;for(s=0;s=o)&&Object.keys(i.O).every((function(e){return i.O[e](n[f])}))?n.splice(f--,1):(u=!1,o0&&e[s-1][2]>o;s--)e[s]=e[s-1];e[s]=[n,r,o]},i.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(t,{a:t}),t},n=Object.getPrototypeOf?function(e){return Object.getPrototypeOf(e)}:function(e){return e.__proto__},i.t=function(e,r){if(1&r&&(e=this(e)),8&r)return e;if("object"==typeof e&&e){if(4&r&&e.__esModule)return e;if(16&r&&"function"==typeof e.then)return e}var o=Object.create(null);i.r(o);var c={};t=t||[null,n({}),n([]),n(n)];for(var u=2&r&&e;"object"==typeof u&&!~t.indexOf(u);u=n(u))Object.getOwnPropertyNames(u).forEach((function(t){c[t]=function(){return e[t]}}));return c.default=function(){return e},i.d(o,c),o},i.d=function(e,t){for(var n in t)i.o(t,n)&&!i.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},i.f={},i.e=function(e){return Promise.all(Object.keys(i.f).reduce((function(t,n){return i.f[n](e,t),t}),[]))},i.u=function(e){return({25:"component---src-pages-portfolio-js",166:"component---src-pages-donate-js",256:"component---src-pages-mdx-frontmatter-post-type-mdx-slug-js",351:"commons",597:"d352c97fe2d9dac170c373a6e8eddfefac95254b",678:"component---src-pages-index-js",682:"component---src-pages-about-js",774:"framework",883:"component---src-pages-404-js"}[e]||e)+"-"+{25:"3ad369d50ef16a5829cb",166:"5899d8c8b95030f5a1bb",231:"839266e8d1c4768a8263",256:"48b0a058daaca5b189f1",351:"3aa4d10633ba503a27de",589:"5b6bba0449f4a6fcacd1",597:"62480750a44dee691769",678:"2f7a1945b6aa37a50672",680:"bf4a6373360ebefebb72",682:"c9d82eeefc0c25a33d16",774:"f851dd4c68ba35ea457f",883:"27842d7ed8830e74c3aa"}[e]+".js"},i.miniCssF=function(e){return"styles.ed9ad152a5c75a471d98.css"},i.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),i.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r={},o="jimmy-hooker-com:",i.l=function(e,t,n,c){if(r[e])r[e].push(t);else{var u,f;if(void 0!==n)for(var a=document.getElementsByTagName("script"),s=0;s 0 && deferred[i - 1][2] > priority; i--) deferred[i] = deferred[i - 1];\n\t\tdeferred[i] = [chunkIds, fn, priority];\n\t\treturn;\n\t}\n\tvar notFulfilled = Infinity;\n\tfor (var i = 0; i < deferred.length; i++) {\n\t\tvar chunkIds = deferred[i][0];\n\t\tvar fn = deferred[i][1];\n\t\tvar priority = deferred[i][2];\n\t\tvar fulfilled = true;\n\t\tfor (var j = 0; j < chunkIds.length; j++) {\n\t\t\tif ((priority & 1 === 0 || notFulfilled >= priority) && Object.keys(__webpack_require__.O).every(function(key) { return __webpack_require__.O[key](chunkIds[j]); })) {\n\t\t\t\tchunkIds.splice(j--, 1);\n\t\t\t} else {\n\t\t\t\tfulfilled = false;\n\t\t\t\tif(priority < notFulfilled) notFulfilled = priority;\n\t\t\t}\n\t\t}\n\t\tif(fulfilled) {\n\t\t\tdeferred.splice(i--, 1)\n\t\t\tvar r = fn();\n\t\t\tif (r !== undefined) result = r;\n\t\t}\n\t}\n\treturn result;\n};","var getProto = Object.getPrototypeOf ? function(obj) { return Object.getPrototypeOf(obj); } : function(obj) { return obj.__proto__; };\nvar leafPrototypes;\n// create a fake namespace object\n// mode & 1: value is a module id, require it\n// mode & 2: merge all properties of value into the ns\n// mode & 4: return value when already ns object\n// mode & 16: return value when it's Promise-like\n// mode & 8|1: behave like require\n__webpack_require__.t = function(value, mode) {\n\tif(mode & 1) value = this(value);\n\tif(mode & 8) return value;\n\tif(typeof value === 'object' && value) {\n\t\tif((mode & 4) && value.__esModule) return value;\n\t\tif((mode & 16) && typeof value.then === 'function') return value;\n\t}\n\tvar ns = Object.create(null);\n\t__webpack_require__.r(ns);\n\tvar def = {};\n\tleafPrototypes = leafPrototypes || [null, getProto({}), getProto([]), getProto(getProto)];\n\tfor(var current = mode & 2 && value; typeof current == 'object' && !~leafPrototypes.indexOf(current); current = getProto(current)) {\n\t\tObject.getOwnPropertyNames(current).forEach(function(key) { def[key] = function() { return value[key]; }; });\n\t}\n\tdef['default'] = function() { return value; };\n\t__webpack_require__.d(ns, def);\n\treturn ns;\n};","var inProgress = {};\nvar dataWebpackPrefix = \"jimmy-hooker-com:\";\n// loadScript function to load a script via script tag\n__webpack_require__.l = function(url, done, key, chunkId) {\n\tif(inProgress[url]) { inProgress[url].push(done); return; }\n\tvar script, needAttach;\n\tif(key !== undefined) {\n\t\tvar scripts = document.getElementsByTagName(\"script\");\n\t\tfor(var i = 0; i < scripts.length; i++) {\n\t\t\tvar s = scripts[i];\n\t\t\tif(s.getAttribute(\"src\") == url || s.getAttribute(\"data-webpack\") == dataWebpackPrefix + key) { script = s; break; }\n\t\t}\n\t}\n\tif(!script) {\n\t\tneedAttach = true;\n\t\tscript = document.createElement('script');\n\n\t\tscript.charset = 'utf-8';\n\t\tscript.timeout = 120;\n\t\tif (__webpack_require__.nc) {\n\t\t\tscript.setAttribute(\"nonce\", __webpack_require__.nc);\n\t\t}\n\t\tscript.setAttribute(\"data-webpack\", dataWebpackPrefix + key);\n\t\tscript.src = url;\n\t}\n\tinProgress[url] = [done];\n\tvar onScriptComplete = function(prev, event) {\n\t\t// avoid mem leaks in IE.\n\t\tscript.onerror = script.onload = null;\n\t\tclearTimeout(timeout);\n\t\tvar doneFns = inProgress[url];\n\t\tdelete inProgress[url];\n\t\tscript.parentNode && script.parentNode.removeChild(script);\n\t\tdoneFns && doneFns.forEach(function(fn) { return fn(event); });\n\t\tif(prev) return prev(event);\n\t}\n\t;\n\tvar timeout = setTimeout(onScriptComplete.bind(null, undefined, { type: 'timeout', target: script }), 120000);\n\tscript.onerror = onScriptComplete.bind(null, script.onerror);\n\tscript.onload = onScriptComplete.bind(null, script.onload);\n\tneedAttach && document.head.appendChild(script);\n};","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n// expose the modules object (__webpack_modules__)\n__webpack_require__.m = __webpack_modules__;\n\n","// getDefaultExport function for compatibility with non-harmony modules\n__webpack_require__.n = function(module) {\n\tvar getter = module && module.__esModule ?\n\t\tfunction() { return module['default']; } :\n\t\tfunction() { return module; };\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};","// define getter functions for harmony exports\n__webpack_require__.d = function(exports, definition) {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.f = {};\n// This file contains only the entry chunk.\n// The chunk loading function for additional chunks\n__webpack_require__.e = function(chunkId) {\n\treturn Promise.all(Object.keys(__webpack_require__.f).reduce(function(promises, key) {\n\t\t__webpack_require__.f[key](chunkId, promises);\n\t\treturn promises;\n\t}, []));\n};","// This function allow to reference async chunks\n__webpack_require__.u = function(chunkId) {\n\t// return url for filenames based on template\n\treturn \"\" + ({\"25\":\"component---src-pages-portfolio-js\",\"166\":\"component---src-pages-donate-js\",\"256\":\"component---src-pages-mdx-frontmatter-post-type-mdx-slug-js\",\"351\":\"commons\",\"597\":\"d352c97fe2d9dac170c373a6e8eddfefac95254b\",\"678\":\"component---src-pages-index-js\",\"682\":\"component---src-pages-about-js\",\"774\":\"framework\",\"883\":\"component---src-pages-404-js\"}[chunkId] || chunkId) + \"-\" + {\"25\":\"3ad369d50ef16a5829cb\",\"166\":\"5899d8c8b95030f5a1bb\",\"231\":\"839266e8d1c4768a8263\",\"256\":\"48b0a058daaca5b189f1\",\"351\":\"3aa4d10633ba503a27de\",\"589\":\"5b6bba0449f4a6fcacd1\",\"597\":\"62480750a44dee691769\",\"678\":\"2f7a1945b6aa37a50672\",\"680\":\"bf4a6373360ebefebb72\",\"682\":\"c9d82eeefc0c25a33d16\",\"774\":\"f851dd4c68ba35ea457f\",\"883\":\"27842d7ed8830e74c3aa\"}[chunkId] + \".js\";\n};","// This function allow to reference all chunks\n__webpack_require__.miniCssF = function(chunkId) {\n\t// return url for filenames based on template\n\treturn \"\" + \"styles\" + \".\" + \"ed9ad152a5c75a471d98\" + \".css\";\n};","__webpack_require__.g = (function() {\n\tif (typeof globalThis === 'object') return globalThis;\n\ttry {\n\t\treturn this || new Function('return this')();\n\t} catch (e) {\n\t\tif (typeof window === 'object') return window;\n\t}\n})();","__webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }","// define __esModule on exports\n__webpack_require__.r = function(exports) {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","__webpack_require__.p = \"/\";","// no baseURI\n\n// object to store loaded and loading chunks\n// undefined = chunk not loaded, null = chunk preloaded/prefetched\n// [resolve, reject, Promise] = chunk loading, 0 = chunk loaded\nvar installedChunks = {\n\t658: 0,\n\t532: 0\n};\n\n__webpack_require__.f.j = function(chunkId, promises) {\n\t\t// JSONP chunk loading for javascript\n\t\tvar installedChunkData = __webpack_require__.o(installedChunks, chunkId) ? installedChunks[chunkId] : undefined;\n\t\tif(installedChunkData !== 0) { // 0 means \"already installed\".\n\n\t\t\t// a Promise means \"currently loading\".\n\t\t\tif(installedChunkData) {\n\t\t\t\tpromises.push(installedChunkData[2]);\n\t\t\t} else {\n\t\t\t\tif(!/^(532|658)$/.test(chunkId)) {\n\t\t\t\t\t// setup Promise in chunk cache\n\t\t\t\t\tvar promise = new Promise(function(resolve, reject) { installedChunkData = installedChunks[chunkId] = [resolve, reject]; });\n\t\t\t\t\tpromises.push(installedChunkData[2] = promise);\n\n\t\t\t\t\t// start chunk loading\n\t\t\t\t\tvar url = __webpack_require__.p + __webpack_require__.u(chunkId);\n\t\t\t\t\t// create error before stack unwound to get useful stacktrace later\n\t\t\t\t\tvar error = new Error();\n\t\t\t\t\tvar loadingEnded = function(event) {\n\t\t\t\t\t\tif(__webpack_require__.o(installedChunks, chunkId)) {\n\t\t\t\t\t\t\tinstalledChunkData = installedChunks[chunkId];\n\t\t\t\t\t\t\tif(installedChunkData !== 0) installedChunks[chunkId] = undefined;\n\t\t\t\t\t\t\tif(installedChunkData) {\n\t\t\t\t\t\t\t\tvar errorType = event && (event.type === 'load' ? 'missing' : event.type);\n\t\t\t\t\t\t\t\tvar realSrc = event && event.target && event.target.src;\n\t\t\t\t\t\t\t\terror.message = 'Loading chunk ' + chunkId + ' failed.\\n(' + errorType + ': ' + realSrc + ')';\n\t\t\t\t\t\t\t\terror.name = 'ChunkLoadError';\n\t\t\t\t\t\t\t\terror.type = errorType;\n\t\t\t\t\t\t\t\terror.request = realSrc;\n\t\t\t\t\t\t\t\tinstalledChunkData[1](error);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\t\t\t\t\t__webpack_require__.l(url, loadingEnded, \"chunk-\" + chunkId, chunkId);\n\t\t\t\t} else installedChunks[chunkId] = 0;\n\t\t\t}\n\t\t}\n};\n\n// no prefetching\n\n// no preloaded\n\n// no HMR\n\n// no HMR manifest\n\n__webpack_require__.O.j = function(chunkId) { return installedChunks[chunkId] === 0; };\n\n// install a JSONP callback for chunk loading\nvar webpackJsonpCallback = function(parentChunkLoadingFunction, data) {\n\tvar chunkIds = data[0];\n\tvar moreModules = data[1];\n\tvar runtime = data[2];\n\t// add \"moreModules\" to the modules object,\n\t// then flag all \"chunkIds\" as loaded and fire callback\n\tvar moduleId, chunkId, i = 0;\n\tif(chunkIds.some(function(id) { return installedChunks[id] !== 0; })) {\n\t\tfor(moduleId in moreModules) {\n\t\t\tif(__webpack_require__.o(moreModules, moduleId)) {\n\t\t\t\t__webpack_require__.m[moduleId] = moreModules[moduleId];\n\t\t\t}\n\t\t}\n\t\tif(runtime) var result = runtime(__webpack_require__);\n\t}\n\tif(parentChunkLoadingFunction) parentChunkLoadingFunction(data);\n\tfor(;i < chunkIds.length; i++) {\n\t\tchunkId = chunkIds[i];\n\t\tif(__webpack_require__.o(installedChunks, chunkId) && installedChunks[chunkId]) {\n\t\t\tinstalledChunks[chunkId][0]();\n\t\t}\n\t\tinstalledChunks[chunkId] = 0;\n\t}\n\treturn __webpack_require__.O(result);\n}\n\nvar chunkLoadingGlobal = self[\"webpackChunkjimmy_hooker_com\"] = self[\"webpackChunkjimmy_hooker_com\"] || [];\nchunkLoadingGlobal.forEach(webpackJsonpCallback.bind(null, 0));\nchunkLoadingGlobal.push = webpackJsonpCallback.bind(null, chunkLoadingGlobal.push.bind(chunkLoadingGlobal));"],"names":["deferred","leafPrototypes","getProto","inProgress","dataWebpackPrefix","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","undefined","exports","module","__webpack_modules__","call","m","O","result","chunkIds","fn","priority","notFulfilled","Infinity","i","length","fulfilled","j","Object","keys","every","key","splice","r","n","getter","__esModule","d","a","getPrototypeOf","obj","__proto__","t","value","mode","this","then","ns","create","def","current","indexOf","getOwnPropertyNames","forEach","definition","o","defineProperty","enumerable","get","f","e","chunkId","Promise","all","reduce","promises","u","miniCssF","g","globalThis","Function","window","prop","prototype","hasOwnProperty","l","url","done","push","script","needAttach","scripts","document","getElementsByTagName","s","getAttribute","createElement","charset","timeout","nc","setAttribute","src","onScriptComplete","prev","event","onerror","onload","clearTimeout","doneFns","parentNode","removeChild","setTimeout","bind","type","target","head","appendChild","Symbol","toStringTag","p","installedChunks","installedChunkData","test","promise","resolve","reject","error","Error","errorType","realSrc","message","name","request","webpackJsonpCallback","parentChunkLoadingFunction","data","moreModules","runtime","some","id","chunkLoadingGlobal","self"],"sourceRoot":""} \ No newline at end of file diff --git a/webpack-runtime-f36eab2c0da52c9b15f6.js b/webpack-runtime-f36eab2c0da52c9b15f6.js new file mode 100644 index 0000000..32f66b5 --- /dev/null +++ b/webpack-runtime-f36eab2c0da52c9b15f6.js @@ -0,0 +1,2 @@ +!function(){"use strict";var e,t,n,r,o,c={},u={};function i(e){var t=u[e];if(void 0!==t)return t.exports;var n=u[e]={exports:{}};return c[e].call(n.exports,n,n.exports,i),n.exports}i.m=c,e=[],i.O=function(t,n,r,o){if(!n){var c=1/0;for(s=0;s=o)&&Object.keys(i.O).every((function(e){return i.O[e](n[f])}))?n.splice(f--,1):(u=!1,o0&&e[s-1][2]>o;s--)e[s]=e[s-1];e[s]=[n,r,o]},i.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(t,{a:t}),t},n=Object.getPrototypeOf?function(e){return Object.getPrototypeOf(e)}:function(e){return e.__proto__},i.t=function(e,r){if(1&r&&(e=this(e)),8&r)return e;if("object"==typeof e&&e){if(4&r&&e.__esModule)return e;if(16&r&&"function"==typeof e.then)return e}var o=Object.create(null);i.r(o);var c={};t=t||[null,n({}),n([]),n(n)];for(var u=2&r&&e;"object"==typeof u&&!~t.indexOf(u);u=n(u))Object.getOwnPropertyNames(u).forEach((function(t){c[t]=function(){return e[t]}}));return c.default=function(){return e},i.d(o,c),o},i.d=function(e,t){for(var n in t)i.o(t,n)&&!i.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},i.f={},i.e=function(e){return Promise.all(Object.keys(i.f).reduce((function(t,n){return i.f[n](e,t),t}),[]))},i.u=function(e){return({25:"component---src-pages-portfolio-js",166:"component---src-pages-donate-js",256:"component---src-pages-mdx-frontmatter-post-type-mdx-slug-js",351:"commons",597:"d352c97fe2d9dac170c373a6e8eddfefac95254b",678:"component---src-pages-index-js",682:"component---src-pages-about-js",774:"framework",883:"component---src-pages-404-js"}[e]||e)+"-"+{25:"3ad369d50ef16a5829cb",166:"5899d8c8b95030f5a1bb",231:"839266e8d1c4768a8263",256:"48b0a058daaca5b189f1",351:"3aa4d10633ba503a27de",597:"62480750a44dee691769",678:"2f7a1945b6aa37a50672",680:"bf4a6373360ebefebb72",682:"c9d82eeefc0c25a33d16",774:"f851dd4c68ba35ea457f",883:"27842d7ed8830e74c3aa"}[e]+".js"},i.miniCssF=function(e){return"styles.ed9ad152a5c75a471d98.css"},i.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),i.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r={},o="jimmy-hooker-com:",i.l=function(e,t,n,c){if(r[e])r[e].push(t);else{var u,f;if(void 0!==n)for(var a=document.getElementsByTagName("script"),s=0;s 0 && deferred[i - 1][2] > priority; i--) deferred[i] = deferred[i - 1];\n\t\tdeferred[i] = [chunkIds, fn, priority];\n\t\treturn;\n\t}\n\tvar notFulfilled = Infinity;\n\tfor (var i = 0; i < deferred.length; i++) {\n\t\tvar chunkIds = deferred[i][0];\n\t\tvar fn = deferred[i][1];\n\t\tvar priority = deferred[i][2];\n\t\tvar fulfilled = true;\n\t\tfor (var j = 0; j < chunkIds.length; j++) {\n\t\t\tif ((priority & 1 === 0 || notFulfilled >= priority) && Object.keys(__webpack_require__.O).every(function(key) { return __webpack_require__.O[key](chunkIds[j]); })) {\n\t\t\t\tchunkIds.splice(j--, 1);\n\t\t\t} else {\n\t\t\t\tfulfilled = false;\n\t\t\t\tif(priority < notFulfilled) notFulfilled = priority;\n\t\t\t}\n\t\t}\n\t\tif(fulfilled) {\n\t\t\tdeferred.splice(i--, 1)\n\t\t\tvar r = fn();\n\t\t\tif (r !== undefined) result = r;\n\t\t}\n\t}\n\treturn result;\n};","var getProto = Object.getPrototypeOf ? function(obj) { return Object.getPrototypeOf(obj); } : function(obj) { return obj.__proto__; };\nvar leafPrototypes;\n// create a fake namespace object\n// mode & 1: value is a module id, require it\n// mode & 2: merge all properties of value into the ns\n// mode & 4: return value when already ns object\n// mode & 16: return value when it's Promise-like\n// mode & 8|1: behave like require\n__webpack_require__.t = function(value, mode) {\n\tif(mode & 1) value = this(value);\n\tif(mode & 8) return value;\n\tif(typeof value === 'object' && value) {\n\t\tif((mode & 4) && value.__esModule) return value;\n\t\tif((mode & 16) && typeof value.then === 'function') return value;\n\t}\n\tvar ns = Object.create(null);\n\t__webpack_require__.r(ns);\n\tvar def = {};\n\tleafPrototypes = leafPrototypes || [null, getProto({}), getProto([]), getProto(getProto)];\n\tfor(var current = mode & 2 && value; typeof current == 'object' && !~leafPrototypes.indexOf(current); current = getProto(current)) {\n\t\tObject.getOwnPropertyNames(current).forEach(function(key) { def[key] = function() { return value[key]; }; });\n\t}\n\tdef['default'] = function() { return value; };\n\t__webpack_require__.d(ns, def);\n\treturn ns;\n};","var inProgress = {};\nvar dataWebpackPrefix = \"jimmy-hooker-com:\";\n// loadScript function to load a script via script tag\n__webpack_require__.l = function(url, done, key, chunkId) {\n\tif(inProgress[url]) { inProgress[url].push(done); return; }\n\tvar script, needAttach;\n\tif(key !== undefined) {\n\t\tvar scripts = document.getElementsByTagName(\"script\");\n\t\tfor(var i = 0; i < scripts.length; i++) {\n\t\t\tvar s = scripts[i];\n\t\t\tif(s.getAttribute(\"src\") == url || s.getAttribute(\"data-webpack\") == dataWebpackPrefix + key) { script = s; break; }\n\t\t}\n\t}\n\tif(!script) {\n\t\tneedAttach = true;\n\t\tscript = document.createElement('script');\n\n\t\tscript.charset = 'utf-8';\n\t\tscript.timeout = 120;\n\t\tif (__webpack_require__.nc) {\n\t\t\tscript.setAttribute(\"nonce\", __webpack_require__.nc);\n\t\t}\n\t\tscript.setAttribute(\"data-webpack\", dataWebpackPrefix + key);\n\t\tscript.src = url;\n\t}\n\tinProgress[url] = [done];\n\tvar onScriptComplete = function(prev, event) {\n\t\t// avoid mem leaks in IE.\n\t\tscript.onerror = script.onload = null;\n\t\tclearTimeout(timeout);\n\t\tvar doneFns = inProgress[url];\n\t\tdelete inProgress[url];\n\t\tscript.parentNode && script.parentNode.removeChild(script);\n\t\tdoneFns && doneFns.forEach(function(fn) { return fn(event); });\n\t\tif(prev) return prev(event);\n\t}\n\t;\n\tvar timeout = setTimeout(onScriptComplete.bind(null, undefined, { type: 'timeout', target: script }), 120000);\n\tscript.onerror = onScriptComplete.bind(null, script.onerror);\n\tscript.onload = onScriptComplete.bind(null, script.onload);\n\tneedAttach && document.head.appendChild(script);\n};","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n// expose the modules object (__webpack_modules__)\n__webpack_require__.m = __webpack_modules__;\n\n","// getDefaultExport function for compatibility with non-harmony modules\n__webpack_require__.n = function(module) {\n\tvar getter = module && module.__esModule ?\n\t\tfunction() { return module['default']; } :\n\t\tfunction() { return module; };\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};","// define getter functions for harmony exports\n__webpack_require__.d = function(exports, definition) {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.f = {};\n// This file contains only the entry chunk.\n// The chunk loading function for additional chunks\n__webpack_require__.e = function(chunkId) {\n\treturn Promise.all(Object.keys(__webpack_require__.f).reduce(function(promises, key) {\n\t\t__webpack_require__.f[key](chunkId, promises);\n\t\treturn promises;\n\t}, []));\n};","// This function allow to reference async chunks\n__webpack_require__.u = function(chunkId) {\n\t// return url for filenames based on template\n\treturn \"\" + ({\"25\":\"component---src-pages-portfolio-js\",\"166\":\"component---src-pages-donate-js\",\"256\":\"component---src-pages-mdx-frontmatter-post-type-mdx-slug-js\",\"351\":\"commons\",\"597\":\"d352c97fe2d9dac170c373a6e8eddfefac95254b\",\"678\":\"component---src-pages-index-js\",\"682\":\"component---src-pages-about-js\",\"774\":\"framework\",\"883\":\"component---src-pages-404-js\"}[chunkId] || chunkId) + \"-\" + {\"25\":\"3ad369d50ef16a5829cb\",\"166\":\"5899d8c8b95030f5a1bb\",\"231\":\"839266e8d1c4768a8263\",\"256\":\"48b0a058daaca5b189f1\",\"351\":\"3aa4d10633ba503a27de\",\"597\":\"62480750a44dee691769\",\"678\":\"2f7a1945b6aa37a50672\",\"680\":\"bf4a6373360ebefebb72\",\"682\":\"c9d82eeefc0c25a33d16\",\"774\":\"f851dd4c68ba35ea457f\",\"883\":\"27842d7ed8830e74c3aa\"}[chunkId] + \".js\";\n};","// This function allow to reference all chunks\n__webpack_require__.miniCssF = function(chunkId) {\n\t// return url for filenames based on template\n\treturn \"\" + \"styles\" + \".\" + \"ed9ad152a5c75a471d98\" + \".css\";\n};","__webpack_require__.g = (function() {\n\tif (typeof globalThis === 'object') return globalThis;\n\ttry {\n\t\treturn this || new Function('return this')();\n\t} catch (e) {\n\t\tif (typeof window === 'object') return window;\n\t}\n})();","__webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }","// define __esModule on exports\n__webpack_require__.r = function(exports) {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","__webpack_require__.p = \"/\";","// no baseURI\n\n// object to store loaded and loading chunks\n// undefined = chunk not loaded, null = chunk preloaded/prefetched\n// [resolve, reject, Promise] = chunk loading, 0 = chunk loaded\nvar installedChunks = {\n\t658: 0,\n\t532: 0\n};\n\n__webpack_require__.f.j = function(chunkId, promises) {\n\t\t// JSONP chunk loading for javascript\n\t\tvar installedChunkData = __webpack_require__.o(installedChunks, chunkId) ? installedChunks[chunkId] : undefined;\n\t\tif(installedChunkData !== 0) { // 0 means \"already installed\".\n\n\t\t\t// a Promise means \"currently loading\".\n\t\t\tif(installedChunkData) {\n\t\t\t\tpromises.push(installedChunkData[2]);\n\t\t\t} else {\n\t\t\t\tif(!/^(532|658)$/.test(chunkId)) {\n\t\t\t\t\t// setup Promise in chunk cache\n\t\t\t\t\tvar promise = new Promise(function(resolve, reject) { installedChunkData = installedChunks[chunkId] = [resolve, reject]; });\n\t\t\t\t\tpromises.push(installedChunkData[2] = promise);\n\n\t\t\t\t\t// start chunk loading\n\t\t\t\t\tvar url = __webpack_require__.p + __webpack_require__.u(chunkId);\n\t\t\t\t\t// create error before stack unwound to get useful stacktrace later\n\t\t\t\t\tvar error = new Error();\n\t\t\t\t\tvar loadingEnded = function(event) {\n\t\t\t\t\t\tif(__webpack_require__.o(installedChunks, chunkId)) {\n\t\t\t\t\t\t\tinstalledChunkData = installedChunks[chunkId];\n\t\t\t\t\t\t\tif(installedChunkData !== 0) installedChunks[chunkId] = undefined;\n\t\t\t\t\t\t\tif(installedChunkData) {\n\t\t\t\t\t\t\t\tvar errorType = event && (event.type === 'load' ? 'missing' : event.type);\n\t\t\t\t\t\t\t\tvar realSrc = event && event.target && event.target.src;\n\t\t\t\t\t\t\t\terror.message = 'Loading chunk ' + chunkId + ' failed.\\n(' + errorType + ': ' + realSrc + ')';\n\t\t\t\t\t\t\t\terror.name = 'ChunkLoadError';\n\t\t\t\t\t\t\t\terror.type = errorType;\n\t\t\t\t\t\t\t\terror.request = realSrc;\n\t\t\t\t\t\t\t\tinstalledChunkData[1](error);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\t\t\t\t\t__webpack_require__.l(url, loadingEnded, \"chunk-\" + chunkId, chunkId);\n\t\t\t\t} else installedChunks[chunkId] = 0;\n\t\t\t}\n\t\t}\n};\n\n// no prefetching\n\n// no preloaded\n\n// no HMR\n\n// no HMR manifest\n\n__webpack_require__.O.j = function(chunkId) { return installedChunks[chunkId] === 0; };\n\n// install a JSONP callback for chunk loading\nvar webpackJsonpCallback = function(parentChunkLoadingFunction, data) {\n\tvar chunkIds = data[0];\n\tvar moreModules = data[1];\n\tvar runtime = data[2];\n\t// add \"moreModules\" to the modules object,\n\t// then flag all \"chunkIds\" as loaded and fire callback\n\tvar moduleId, chunkId, i = 0;\n\tif(chunkIds.some(function(id) { return installedChunks[id] !== 0; })) {\n\t\tfor(moduleId in moreModules) {\n\t\t\tif(__webpack_require__.o(moreModules, moduleId)) {\n\t\t\t\t__webpack_require__.m[moduleId] = moreModules[moduleId];\n\t\t\t}\n\t\t}\n\t\tif(runtime) var result = runtime(__webpack_require__);\n\t}\n\tif(parentChunkLoadingFunction) parentChunkLoadingFunction(data);\n\tfor(;i < chunkIds.length; i++) {\n\t\tchunkId = chunkIds[i];\n\t\tif(__webpack_require__.o(installedChunks, chunkId) && installedChunks[chunkId]) {\n\t\t\tinstalledChunks[chunkId][0]();\n\t\t}\n\t\tinstalledChunks[chunkId] = 0;\n\t}\n\treturn __webpack_require__.O(result);\n}\n\nvar chunkLoadingGlobal = self[\"webpackChunkjimmy_hooker_com\"] = self[\"webpackChunkjimmy_hooker_com\"] || [];\nchunkLoadingGlobal.forEach(webpackJsonpCallback.bind(null, 0));\nchunkLoadingGlobal.push = webpackJsonpCallback.bind(null, chunkLoadingGlobal.push.bind(chunkLoadingGlobal));"],"names":["deferred","leafPrototypes","getProto","inProgress","dataWebpackPrefix","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","undefined","exports","module","__webpack_modules__","call","m","O","result","chunkIds","fn","priority","notFulfilled","Infinity","i","length","fulfilled","j","Object","keys","every","key","splice","r","n","getter","__esModule","d","a","getPrototypeOf","obj","__proto__","t","value","mode","this","then","ns","create","def","current","indexOf","getOwnPropertyNames","forEach","definition","o","defineProperty","enumerable","get","f","e","chunkId","Promise","all","reduce","promises","u","miniCssF","g","globalThis","Function","window","prop","prototype","hasOwnProperty","l","url","done","push","script","needAttach","scripts","document","getElementsByTagName","s","getAttribute","createElement","charset","timeout","nc","setAttribute","src","onScriptComplete","prev","event","onerror","onload","clearTimeout","doneFns","parentNode","removeChild","setTimeout","bind","type","target","head","appendChild","Symbol","toStringTag","p","installedChunks","installedChunkData","test","promise","resolve","reject","error","Error","errorType","realSrc","message","name","request","webpackJsonpCallback","parentChunkLoadingFunction","data","moreModules","runtime","some","id","chunkLoadingGlobal","self"],"sourceRoot":""} \ No newline at end of file diff --git a/webpack.stats.json b/webpack.stats.json index 612ec90..067f2ac 100644 --- a/webpack.stats.json +++ b/webpack.stats.json @@ -1 +1 @@ -{"name":"build-javascript","namedChunkGroups":{"polyfill":{"name":"polyfill","assets":[{"name":"webpack-runtime-56bc60d9a2f6029973eb.js","size":4322},{"name":"polyfill-67c31b63031754035b52.js","size":85850}],"filteredAssets":0,"assetsSize":90172,"filteredAuxiliaryAssets":2,"auxiliaryAssetsSize":224353},"app":{"name":"app","assets":[{"name":"webpack-runtime-56bc60d9a2f6029973eb.js","size":4322},{"name":"framework-f851dd4c68ba35ea457f.js","size":149097},{"name":"styles.ed9ad152a5c75a471d98.css","size":96692},{"name":"app-2db289bf6a57778cd61e.js","size":99506}],"filteredAssets":0,"assetsSize":349617,"filteredAuxiliaryAssets":21,"auxiliaryAssetsSize":1172429},"component---src-pages-404-js":{"name":"component---src-pages-404-js","assets":[{"name":"commons-3aa4d10633ba503a27de.js","size":124166},{"name":"component---src-pages-404-js-27842d7ed8830e74c3aa.js","size":722}],"filteredAssets":0,"assetsSize":124888,"filteredAuxiliaryAssets":2,"auxiliaryAssetsSize":621901},"component---src-pages-about-js":{"name":"component---src-pages-about-js","assets":[{"name":"commons-3aa4d10633ba503a27de.js","size":124166},{"name":"component---src-pages-about-js-c9d82eeefc0c25a33d16.js","size":1881}],"filteredAssets":0,"assetsSize":126047,"filteredAuxiliaryAssets":2,"auxiliaryAssetsSize":622986},"component---src-pages-donate-js":{"name":"component---src-pages-donate-js","assets":[{"name":"commons-3aa4d10633ba503a27de.js","size":124166},{"name":"component---src-pages-donate-js-5899d8c8b95030f5a1bb.js","size":2500}],"filteredAssets":0,"assetsSize":126666,"filteredAuxiliaryAssets":2,"auxiliaryAssetsSize":624769},"component---src-pages-index-js":{"name":"component---src-pages-index-js","assets":[{"name":"commons-3aa4d10633ba503a27de.js","size":124166},{"name":"d352c97fe2d9dac170c373a6e8eddfefac95254b-62480750a44dee691769.js","size":9412},{"name":"component---src-pages-index-js-2f7a1945b6aa37a50672.js","size":1671}],"filteredAssets":0,"assetsSize":135249,"filteredAuxiliaryAssets":3,"auxiliaryAssetsSize":689875},"component---src-pages-mdx-frontmatter-post-type-mdx-slug-js":{"name":"component---src-pages-mdx-frontmatter-post-type-mdx-slug-js","assets":[{"name":"commons-3aa4d10633ba503a27de.js","size":124166},{"name":"d352c97fe2d9dac170c373a6e8eddfefac95254b-62480750a44dee691769.js","size":9412},{"name":"component---src-pages-mdx-frontmatter-post-type-mdx-slug-js-48b0a058daaca5b189f1.js","size":14974}],"filteredAssets":0,"assetsSize":148552,"filteredAuxiliaryAssets":3,"auxiliaryAssetsSize":734444},"component---src-pages-portfolio-js":{"name":"component---src-pages-portfolio-js","assets":[{"name":"commons-3aa4d10633ba503a27de.js","size":124166},{"name":"d352c97fe2d9dac170c373a6e8eddfefac95254b-62480750a44dee691769.js","size":9412},{"name":"component---src-pages-portfolio-js-3ad369d50ef16a5829cb.js","size":1682}],"filteredAssets":0,"assetsSize":135260,"filteredAuxiliaryAssets":3,"auxiliaryAssetsSize":689787}},"assetsByChunkName":{"polyfill":["webpack-runtime-56bc60d9a2f6029973eb.js","polyfill-67c31b63031754035b52.js"],"app":["webpack-runtime-56bc60d9a2f6029973eb.js","framework-f851dd4c68ba35ea457f.js","styles.ed9ad152a5c75a471d98.css","app-2db289bf6a57778cd61e.js"],"component---src-pages-404-js":["commons-3aa4d10633ba503a27de.js","component---src-pages-404-js-27842d7ed8830e74c3aa.js"],"component---src-pages-about-js":["commons-3aa4d10633ba503a27de.js","component---src-pages-about-js-c9d82eeefc0c25a33d16.js"],"component---src-pages-donate-js":["commons-3aa4d10633ba503a27de.js","component---src-pages-donate-js-5899d8c8b95030f5a1bb.js"],"component---src-pages-index-js":["commons-3aa4d10633ba503a27de.js","d352c97fe2d9dac170c373a6e8eddfefac95254b-62480750a44dee691769.js","component---src-pages-index-js-2f7a1945b6aa37a50672.js"],"component---src-pages-mdx-frontmatter-post-type-mdx-slug-js":["commons-3aa4d10633ba503a27de.js","d352c97fe2d9dac170c373a6e8eddfefac95254b-62480750a44dee691769.js","component---src-pages-mdx-frontmatter-post-type-mdx-slug-js-48b0a058daaca5b189f1.js"],"component---src-pages-portfolio-js":["commons-3aa4d10633ba503a27de.js","d352c97fe2d9dac170c373a6e8eddfefac95254b-62480750a44dee691769.js","component---src-pages-portfolio-js-3ad369d50ef16a5829cb.js"]},"childAssetsByChunkName":{}} \ No newline at end of file +{"name":"build-javascript","namedChunkGroups":{"polyfill":{"name":"polyfill","assets":[{"name":"webpack-runtime-f36eab2c0da52c9b15f6.js","size":4295},{"name":"polyfill-67c31b63031754035b52.js","size":85850}],"filteredAssets":0,"assetsSize":90145,"filteredAuxiliaryAssets":2,"auxiliaryAssetsSize":224308},"app":{"name":"app","assets":[{"name":"webpack-runtime-f36eab2c0da52c9b15f6.js","size":4295},{"name":"framework-f851dd4c68ba35ea457f.js","size":149097},{"name":"styles.ed9ad152a5c75a471d98.css","size":96692},{"name":"app-5f75a45e93580195f195.js","size":98695}],"filteredAssets":0,"assetsSize":348779,"filteredAuxiliaryAssets":21,"auxiliaryAssetsSize":1168896},"component---src-pages-404-js":{"name":"component---src-pages-404-js","assets":[{"name":"commons-3aa4d10633ba503a27de.js","size":124166},{"name":"component---src-pages-404-js-27842d7ed8830e74c3aa.js","size":722}],"filteredAssets":0,"assetsSize":124888,"filteredAuxiliaryAssets":2,"auxiliaryAssetsSize":621901},"component---src-pages-about-js":{"name":"component---src-pages-about-js","assets":[{"name":"commons-3aa4d10633ba503a27de.js","size":124166},{"name":"component---src-pages-about-js-c9d82eeefc0c25a33d16.js","size":1881}],"filteredAssets":0,"assetsSize":126047,"filteredAuxiliaryAssets":2,"auxiliaryAssetsSize":622986},"component---src-pages-donate-js":{"name":"component---src-pages-donate-js","assets":[{"name":"commons-3aa4d10633ba503a27de.js","size":124166},{"name":"component---src-pages-donate-js-5899d8c8b95030f5a1bb.js","size":2500}],"filteredAssets":0,"assetsSize":126666,"filteredAuxiliaryAssets":2,"auxiliaryAssetsSize":624769},"component---src-pages-index-js":{"name":"component---src-pages-index-js","assets":[{"name":"commons-3aa4d10633ba503a27de.js","size":124166},{"name":"d352c97fe2d9dac170c373a6e8eddfefac95254b-62480750a44dee691769.js","size":9412},{"name":"component---src-pages-index-js-2f7a1945b6aa37a50672.js","size":1671}],"filteredAssets":0,"assetsSize":135249,"filteredAuxiliaryAssets":3,"auxiliaryAssetsSize":689875},"component---src-pages-mdx-frontmatter-post-type-mdx-slug-js":{"name":"component---src-pages-mdx-frontmatter-post-type-mdx-slug-js","assets":[{"name":"commons-3aa4d10633ba503a27de.js","size":124166},{"name":"d352c97fe2d9dac170c373a6e8eddfefac95254b-62480750a44dee691769.js","size":9412},{"name":"component---src-pages-mdx-frontmatter-post-type-mdx-slug-js-48b0a058daaca5b189f1.js","size":14974}],"filteredAssets":0,"assetsSize":148552,"filteredAuxiliaryAssets":3,"auxiliaryAssetsSize":734444},"component---src-pages-portfolio-js":{"name":"component---src-pages-portfolio-js","assets":[{"name":"commons-3aa4d10633ba503a27de.js","size":124166},{"name":"d352c97fe2d9dac170c373a6e8eddfefac95254b-62480750a44dee691769.js","size":9412},{"name":"component---src-pages-portfolio-js-3ad369d50ef16a5829cb.js","size":1682}],"filteredAssets":0,"assetsSize":135260,"filteredAuxiliaryAssets":3,"auxiliaryAssetsSize":689787}},"assetsByChunkName":{"polyfill":["webpack-runtime-f36eab2c0da52c9b15f6.js","polyfill-67c31b63031754035b52.js"],"app":["webpack-runtime-f36eab2c0da52c9b15f6.js","framework-f851dd4c68ba35ea457f.js","styles.ed9ad152a5c75a471d98.css","app-5f75a45e93580195f195.js"],"component---src-pages-404-js":["commons-3aa4d10633ba503a27de.js","component---src-pages-404-js-27842d7ed8830e74c3aa.js"],"component---src-pages-about-js":["commons-3aa4d10633ba503a27de.js","component---src-pages-about-js-c9d82eeefc0c25a33d16.js"],"component---src-pages-donate-js":["commons-3aa4d10633ba503a27de.js","component---src-pages-donate-js-5899d8c8b95030f5a1bb.js"],"component---src-pages-index-js":["commons-3aa4d10633ba503a27de.js","d352c97fe2d9dac170c373a6e8eddfefac95254b-62480750a44dee691769.js","component---src-pages-index-js-2f7a1945b6aa37a50672.js"],"component---src-pages-mdx-frontmatter-post-type-mdx-slug-js":["commons-3aa4d10633ba503a27de.js","d352c97fe2d9dac170c373a6e8eddfefac95254b-62480750a44dee691769.js","component---src-pages-mdx-frontmatter-post-type-mdx-slug-js-48b0a058daaca5b189f1.js"],"component---src-pages-portfolio-js":["commons-3aa4d10633ba503a27de.js","d352c97fe2d9dac170c373a6e8eddfefac95254b-62480750a44dee691769.js","component---src-pages-portfolio-js-3ad369d50ef16a5829cb.js"]},"childAssetsByChunkName":{}} \ No newline at end of file