diff --git a/precache-manifest.b7b01867e08b889bf3bb71ffa3c67b80.js b/precache-manifest.b7b01867e08b889bf3bb71ffa3c67b80.js
new file mode 100644
index 0000000..946b612
--- /dev/null
+++ b/precache-manifest.b7b01867e08b889bf3bb71ffa3c67b80.js
@@ -0,0 +1,22 @@
+self.__precacheManifest = (self.__precacheManifest || []).concat([
+ {
+ "revision": "676727e1ac57b735be981f20d838ec55",
+ "url": "/SkillDisplay/index.html"
+ },
+ {
+ "revision": "354d684373876ef5ddb3",
+ "url": "/SkillDisplay/static/css/main.dad11e5f.chunk.css"
+ },
+ {
+ "revision": "e3e1726d43da044c5a6a",
+ "url": "/SkillDisplay/static/js/2.7cddf10a.chunk.js"
+ },
+ {
+ "revision": "354d684373876ef5ddb3",
+ "url": "/SkillDisplay/static/js/main.811c5097.chunk.js"
+ },
+ {
+ "revision": "e4ff8796ac8613602dd0",
+ "url": "/SkillDisplay/static/js/runtime~main.20fadea3.js"
+ }
+]);
\ No newline at end of file
diff --git a/static/css/main.dad11e5f.chunk.css b/static/css/main.dad11e5f.chunk.css
new file mode 100644
index 0000000..a565086
--- /dev/null
+++ b/static/css/main.dad11e5f.chunk.css
@@ -0,0 +1,2 @@
+body{margin:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-size:16px}html{margin:0;height:100vh;overflow:hidden;background-color:rgba(20,20,20,.3)}.actions{background:linear-gradient(180deg,transparent calc(25% - 1px),hsla(0,0%,100%,.5) 25%,transparent calc(25% + 1px),transparent calc(50% - 1px),hsla(0,0%,100%,.5) 50%,transparent calc(50% + 1px),transparent calc(75% - 1px),hsla(0,0%,100%,.5) 75%,transparent calc(75% + 1px));height:3em;position:absolute;top:0;bottom:0;left:0;right:0;margin:auto}.action-icon{-webkit-animation-duration:10s;animation-duration:10s;-webkit-animation-name:action-move;animation-name:action-move;-webkit-animation-timing-function:linear;animation-timing-function:linear;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards;position:absolute}.gcd{width:3rem}.ogcd{width:2rem}@-webkit-keyframes action-move{0%{-webkit-transform:translateX(calc(100vw - 3rem));transform:translateX(calc(100vw - 3rem))}to{-webkit-transform:translateX(-3rem);transform:translateX(-3rem)}}@keyframes action-move{0%{-webkit-transform:translateX(calc(100vw - 3rem));transform:translateX(calc(100vw - 3rem))}to{-webkit-transform:translateX(-3rem);transform:translateX(-3rem)}}
+/*# sourceMappingURL=main.dad11e5f.chunk.css.map */
\ No newline at end of file
diff --git a/static/css/main.dad11e5f.chunk.css.map b/static/css/main.dad11e5f.chunk.css.map
new file mode 100644
index 0000000..168c64e
--- /dev/null
+++ b/static/css/main.dad11e5f.chunk.css.map
@@ -0,0 +1 @@
+{"version":3,"sources":["index.css","App.css","Action.css"],"names":[],"mappings":"AAAA,KACE,QAAS,CACT,mIAEY,CACZ,kCAAmC,CACnC,iCAAkC,CAClC,cACF,CAEA,KACE,QAAS,CACT,YAAa,CACb,eAAgB,CAChB,kCACF,CCfA,SAEC,+QAUI,CACJ,UAAW,CAEX,iBAAkB,CAClB,KAAK,CACL,QAAS,CACT,MAAO,CACP,OAAQ,CAER,WACD,CCtBA,aACE,8BAAuB,CAAvB,sBAAuB,CACvB,kCAA2B,CAA3B,0BAA2B,CAC3B,wCAAiC,CAAjC,gCAAiC,CACjC,oCAA6B,CAA7B,4BAA6B,CAC7B,iBACF,CAEA,KACC,UACD,CAEA,MACC,UACD,CAEA,+BACE,GACE,gDAAyC,CAAzC,wCACF,CAEA,GACE,mCAA4B,CAA5B,2BACF,CACF,CARA,uBACE,GACE,gDAAyC,CAAzC,wCACF,CAEA,GACE,mCAA4B,CAA5B,2BACF,CACF","file":"main.dad11e5f.chunk.css","sourcesContent":["body {\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Roboto\", \"Oxygen\",\n \"Ubuntu\", \"Cantarell\", \"Fira Sans\", \"Droid Sans\", \"Helvetica Neue\",\n sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n font-size: 16px;\n}\n\nhtml {\n margin: 0;\n height: 100vh;\n overflow: hidden;\n background-color: rgba(20, 20, 20, 0.3);\n}",".actions {\r\n\tmargin-top: 1em;\r\n\tbackground: linear-gradient(180deg, \r\n\t\trgba(0,0,0,0) calc(25% - 1px), \r\n rgba(255,255,255,0.5) calc(25%), \r\n rgba(0,0,0,0) calc(25% + 1px),\r\n rgba(0,0,0,0) calc(50% - 1px), \r\n rgba(255,255,255,0.5) calc(50%), \r\n rgba(0,0,0,0) calc(50% + 1px),\r\n\t\trgba(0,0,0,0) calc(75% - 1px), \r\n rgba(255,255,255,0.5) calc(75%), \r\n rgba(0,0,0,0) calc(75% + 1px)\r\n );\r\n\theight: 3em;\r\n\t\r\n\tposition: absolute;\r\n\ttop:0;\r\n\tbottom: 0;\r\n\tleft: 0;\r\n\tright: 0;\r\n \t\r\n\tmargin: auto;\r\n}",".action-icon {\r\n animation-duration: 10s;\r\n animation-name: action-move;\r\n animation-timing-function: linear;\r\n animation-fill-mode: forwards;\r\n position: absolute;\r\n}\r\n\r\n.gcd {\r\n\twidth: 3rem;\r\n}\r\n\r\n.ogcd {\r\n\twidth: 2rem;\r\n}\r\n\r\n@keyframes action-move {\r\n from {\r\n transform: translateX(calc(100vw - 3rem));\r\n }\r\n \r\n to {\r\n transform: translateX(-3rem);\r\n }\r\n}"]}
\ No newline at end of file
diff --git a/static/js/main.811c5097.chunk.js b/static/js/main.811c5097.chunk.js
new file mode 100644
index 0000000..e5fcfce
--- /dev/null
+++ b/static/js/main.811c5097.chunk.js
@@ -0,0 +1,2 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[0],{15:function(t,e,n){},16:function(t,e,n){},17:function(t,e,n){},18:function(t,e,n){"use strict";n.r(e);var a=n(0),i=n.n(a),o=n(8),r=n.n(o),c=(n(15),n(1)),s=n(2),u=n(5),d=n(4),l=n(3),h=n(6);n(16),n(17);var f=[15997,15998,15999,16e3,16001,16002,16003,16004,16191,16192,16193,16194,16195,16196,7418,16483],m=[3559,116,114],v=function(t){function e(t){var n;Object(c.a)(this,e),(n=Object(u.a)(this,Object(d.a)(e).call(this,t))).state={xivapi_data:[]};var a="https://xivapi.com/Action/"+t.action_id;return fetch(a,{mode:"cors"}).then(function(t){return t.json()}).then(function(t){n.setState({xivapi_data:t})}),n}return Object(h.a)(e,t),Object(s.a)(e,[{key:"isGCD",value:function(){return-1===m.indexOf(this.props.action_id)&&(-1!==f.indexOf(this.props.action_id)||4!==this.state.xivapi_data.ActionCategory.ID)}},{key:"render",value:function(){if(this.state.xivapi_data.Icon){var t=this.isGCD()?"action-icon gcd":"action-icon ogcd",e="https://xivapi.com"+this.state.xivapi_data.Icon;return i.a.createElement("img",{className:t,src:e,alt:""})}return null}}]),e}(i.a.Component),p=function(t){function e(t){var n;return Object(c.a)(this,e),(n=Object(u.a)(this,Object(d.a)(e).call(this,t))).state={me:0,actionlist:[],actionindex:1,lastAddedTimestamp:""},function t(e){var n=new URLSearchParams(window.location.search),a="".concat(n.get("HOST_PORT"),"BeforeLogLineRead")||!1,i=new WebSocket(a);i.onerror=function(){return t()},i.onmessage=function(t,n){if("."===t.data)return i.send(".");var a=JSON.parse(t.data);if("SendCharName"===a.msgtype)return e(a.msg);if("Chat"===a.msgtype){var o=a.msg.substring(0,2);if("21"===o||"22"===o)return e(a.msg)}}}(n.handleLogEvent.bind(Object(l.a)(n))),n}return Object(h.a)(e,t),Object(s.a)(e,[{key:"handleLogEvent",value:function(t){if(t.charID)this.setState({me:t.charID});else{var e=this.state.me;if(0!==e){var n=t.split("|");if(parseInt(n[2],16)===e){var a=parseInt(n[4],16);if(!(a<=8)&&this.state.lastAddedTimestamp!==n[1]){var i=this.state.actionindex;this.setState(function(t){return{actionindex:t.actionindex>=32?1:t.actionindex+1,lastAddedTimestamp:n[1],actionlist:t.actionlist.concat({index:i,action:a})}}),setTimeout(this.purgeAction.bind(this),1e4)}}}}}},{key:"purgeAction",value:function(){this.setState(function(t){return{actionlist:t.actionlist.slice(1)}})}},{key:"render",value:function(){var t=[],e=!0,n=!1,a=void 0;try{for(var o,r=this.state.actionlist[Symbol.iterator]();!(e=(o=r.next()).done);e=!0){var c=o.value;t.push(i.a.createElement(v,{key:c.index,action_id:c.action}))}}catch(s){n=!0,a=s}finally{try{e||null==r.return||r.return()}finally{if(n)throw a}}return i.a.createElement("div",{className:"actions"},t)}}]),e}(i.a.Component);Boolean("localhost"===window.location.hostname||"[::1]"===window.location.hostname||window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/));r.a.render(i.a.createElement(p,null),document.getElementById("root")),"serviceWorker"in navigator&&navigator.serviceWorker.ready.then(function(t){t.unregister()})},9:function(t,e,n){t.exports=n(18)}},[[9,1,2]]]);
+//# sourceMappingURL=main.811c5097.chunk.js.map
\ No newline at end of file
diff --git a/static/js/main.811c5097.chunk.js.map b/static/js/main.811c5097.chunk.js.map
new file mode 100644
index 0000000..c1c993f
--- /dev/null
+++ b/static/js/main.811c5097.chunk.js.map
@@ -0,0 +1 @@
+{"version":3,"sources":["Action.js","App.js","ACTWebsocket.js","serviceWorker.js","index.js"],"names":["gcdExceptions","ogcdExceptions","Action","props","_this","Object","classCallCheck","this","possibleConstructorReturn","getPrototypeOf","call","state","xivapi_data","actionUrl","action_id","fetch","mode","then","response","json","data","setState","indexOf","ActionCategory","ID","Icon","classes","isGCD","img","react_default","a","createElement","className","src","alt","React","Component","App","me","actionlist","actionindex","lastAddedTimestamp","listenActWebSocket","callback","url","URLSearchParams","window","location","search","wsUri","concat","get","undefined","ws","WebSocket","onerror","onmessage","e","m","send","obj","JSON","parse","msgtype","msg","code","substring","handleLogEvent","bind","assertThisInitialized","charID","log","split","parseInt","action","index","setTimeout","purgeAction","slice","actions","_iteratorNormalCompletion","_didIteratorError","_iteratorError","_step","_iterator","Symbol","iterator","next","done","value","push","src_Action","key","err","return","Boolean","hostname","match","ReactDOM","render","src_App","document","getElementById","navigator","serviceWorker","ready","registration","unregister"],"mappings":"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","file":"static/js/main.811c5097.chunk.js","sourcesContent":["import React from 'react'\r\nimport './css/Action.css'\r\n\r\nconst gcdExceptions = [\r\n\t15997, //standard step\r\n\t15998, //technical step\r\n\t15999, \r\n\t16000,\r\n\t16001,\r\n\t16002, //step actions\r\n\t16003, //standard finish\r\n\t16004, //technical finish\r\n\t16191, //single standard finish\r\n\t16192, //double standard finish (WHY IS IT LIKE THIS)\r\n\t16193, //single technical finish\r\n\t16194, //double technical finish\r\n\t16195, //triple technical finish\r\n\t16196, //quadruple technical finish\r\n\t7418, //flamethrower\r\n\t16483 //tsubame-gaeshi\r\n]\r\n\r\nconst ogcdExceptions = [\r\n\t3559, //bard WM\r\n\t116, //bard AP\r\n\t114 //bard MB\r\n]\r\n\r\nclass Action extends React.Component {\r\n\tstate = {\r\n\t\txivapi_data: []\r\n\t}\r\n\t\r\n\tconstructor(props) {\r\n\t\tsuper(props);\r\n\t\t\r\n\t\tconst actionUrl = \"https://xivapi.com/Action/\"+props.action_id;\r\n\t\t\r\n\t\tfetch(actionUrl, { mode: 'cors' })\r\n\t\t\t.then(response => response.json())\r\n\t\t\t.then(data => {this.setState({xivapi_data: data})})\r\n\t}\r\n\t\r\n\tisGCD() {\r\n\t\tif(ogcdExceptions.indexOf(this.props.action_id) !== -1) return false\r\n\t\tif(gcdExceptions.indexOf(this.props.action_id) !== -1) return true\r\n\t\t\r\n\t\treturn (this.state.xivapi_data.ActionCategory.ID !== 4)\r\n\t}\r\n\t\r\n\trender() {\r\n\t\tif(this.state.xivapi_data.Icon) {\r\n\t\t\tconst classes = this.isGCD()?'action-icon gcd':'action-icon ogcd'\r\n\t\t\tconst img = \"https://xivapi.com\"+this.state.xivapi_data.Icon\r\n\t\t\treturn \r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\treturn null\r\n\t\t}\r\n }\r\n}\r\n\r\nexport default Action","import React from 'react'\nimport listenActWebSocket from './ACTWebsocket'\nimport './css/App.css'\nimport Action from './Action'\n\nclass App extends React.Component {\n\tstate = {\n\t\tme: 0,\n\t\tactionlist: [],\n\t\tactionindex: 1,\n\t\tlastAddedTimestamp: ''\n\t}\n\t\n\tconstructor(props) {\n\t\tsuper(props)\n\t\t\n\t\tlistenActWebSocket(this.handleLogEvent.bind(this))\n\t}\n\t\n\thandleLogEvent(data) {\n\t\tif(data.charID) {\n\t\t\tthis.setState({me: data.charID})\n\t\t\treturn\n\t\t} //the ME data we need\n\t\t\n\t\tconst me = this.state.me\n\t\t\n\t\tif(me === 0) return //we need data on the character first\n\t\t\n\t\tlet log = data.split('|')\n\t\t\n\t\tif(parseInt(log[2],16) !== me) return //we only care about our actions\n\t\t\n\t\tconst action = parseInt(log[4],16)\n\t\t\n\t\tif(action <= 8) return //things we don't care about i.e. sprint auto-attacks\n\t\t\n\t\tif(this.state.lastAddedTimestamp === log[1]) return //no double aoe stuff\n\t\t\n\t\tconst index = this.state.actionindex\n\t\t\n\t\tthis.setState((state) => {\n\t\t\tconst actionindex = (state.actionindex >= 32)?1:state.actionindex+1\n\t\t\tconst lastAddedTimestamp = log[1]\n\t\t\tconst actionlist = state.actionlist.concat({index,action});\n\t\t\t\n\t\t\treturn {actionindex,lastAddedTimestamp,actionlist}\n\t\t})\n\t\t\n\t\tsetTimeout(this.purgeAction.bind(this), 10000)\n\t}\n\t\n\tpurgeAction() {\n\t\tthis.setState((state) => {\n\t\t\tconst actionlist = state.actionlist.slice(1)\n\t\t\t\n\t\t\treturn {actionlist}\n\t\t})\n\t}\n\t\n\trender() {\n\t\tlet actions = []\n\t\t\n\t\tfor (const action of this.state.actionlist) {\n\t\t\tactions.push(