Add pixel statistics
This commit is contained in:
Generated
+606
-4
@@ -26,6 +26,7 @@
|
||||
"process": "^0.11.10",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"recharts": "^2.15.1",
|
||||
"rgbquant": "^1.1.2",
|
||||
"stream": "^0.0.3",
|
||||
"stream-browserify": "^3.0.0",
|
||||
@@ -50,6 +51,18 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/runtime": {
|
||||
"version": "7.26.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.7.tgz",
|
||||
"integrity": "sha512-AOPI3D+a8dXnja+iwsUqGRjr1BbZIe771sXdapOtYI531gSqpi92vXivKcq2asu/DFpdl1ceFAKZyRzK2PCVcQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"regenerator-runtime": "^0.14.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/is-prop-valid": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz",
|
||||
@@ -601,6 +614,69 @@
|
||||
"@types/har-format": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-array": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz",
|
||||
"integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/d3-color": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz",
|
||||
"integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/d3-ease": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz",
|
||||
"integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/d3-interpolate": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz",
|
||||
"integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/d3-color": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-path": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz",
|
||||
"integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/d3-scale": {
|
||||
"version": "4.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz",
|
||||
"integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/d3-time": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-shape": {
|
||||
"version": "3.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz",
|
||||
"integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/d3-path": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-time": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz",
|
||||
"integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/d3-timer": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz",
|
||||
"integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/file-saver": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/file-saver/-/file-saver-2.0.7.tgz",
|
||||
@@ -1287,6 +1363,15 @@
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/clsx": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
|
||||
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
@@ -1363,8 +1448,128 @@
|
||||
"node_modules/csstype": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
|
||||
"dev": true
|
||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
|
||||
},
|
||||
"node_modules/d3-array": {
|
||||
"version": "3.2.4",
|
||||
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz",
|
||||
"integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"internmap": "1 - 2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-color": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
|
||||
"integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-ease": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
|
||||
"integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-format": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz",
|
||||
"integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-interpolate": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
|
||||
"integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"d3-color": "1 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-path": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz",
|
||||
"integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-scale": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
|
||||
"integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"d3-array": "2.10.0 - 3",
|
||||
"d3-format": "1 - 3",
|
||||
"d3-interpolate": "1.2.0 - 3",
|
||||
"d3-time": "2.1.1 - 3",
|
||||
"d3-time-format": "2 - 4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-shape": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz",
|
||||
"integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"d3-path": "^3.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-time": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz",
|
||||
"integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"d3-array": "2 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-time-format": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
|
||||
"integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"d3-time": "1 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-timer": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
|
||||
"integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.3.4",
|
||||
@@ -1383,6 +1588,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/decimal.js-light": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz",
|
||||
"integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/deep-is": {
|
||||
"version": "0.1.4",
|
||||
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
|
||||
@@ -1447,6 +1658,16 @@
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/dom-helpers": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
|
||||
"integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.8.7",
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/es-abstract": {
|
||||
"version": "1.22.4",
|
||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.4.tgz",
|
||||
@@ -1831,12 +2052,27 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/eventemitter3": {
|
||||
"version": "4.0.7",
|
||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
|
||||
"integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-deep-equal": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/fast-equals": {
|
||||
"version": "5.2.2",
|
||||
"resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.2.2.tgz",
|
||||
"integrity": "sha512-V7/RktU11J3I36Nwq2JnZEM7tNm17eBJz+u25qdxBZeCKiX6BkVSZQjwWIr+IobgnZy+ag73tTZgZi7tr0LrBw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/fast-glob": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
|
||||
@@ -2320,6 +2556,15 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/internmap": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
|
||||
"integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/is-array-buffer": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz",
|
||||
@@ -3233,6 +3478,37 @@
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
||||
},
|
||||
"node_modules/react-smooth": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.4.tgz",
|
||||
"integrity": "sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-equals": "^5.0.1",
|
||||
"prop-types": "^15.8.1",
|
||||
"react-transition-group": "^4.4.5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-transition-group": {
|
||||
"version": "4.4.5",
|
||||
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
|
||||
"integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.5.5",
|
||||
"dom-helpers": "^5.0.1",
|
||||
"loose-envify": "^1.4.0",
|
||||
"prop-types": "^15.6.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.6.0",
|
||||
"react-dom": ">=16.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/readable-stream": {
|
||||
"version": "3.6.2",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
|
||||
@@ -3258,6 +3534,44 @@
|
||||
"node": ">=8.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/recharts": {
|
||||
"version": "2.15.1",
|
||||
"resolved": "https://registry.npmjs.org/recharts/-/recharts-2.15.1.tgz",
|
||||
"integrity": "sha512-v8PUTUlyiDe56qUj82w/EDVuzEFXwEHp9/xOowGAZwfLjB9uAy3GllQVIYMWF6nU+qibx85WF75zD7AjqoT54Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"clsx": "^2.0.0",
|
||||
"eventemitter3": "^4.0.1",
|
||||
"lodash": "^4.17.21",
|
||||
"react-is": "^18.3.1",
|
||||
"react-smooth": "^4.0.4",
|
||||
"recharts-scale": "^0.4.4",
|
||||
"tiny-invariant": "^1.3.1",
|
||||
"victory-vendor": "^36.6.8"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/recharts-scale": {
|
||||
"version": "0.4.5",
|
||||
"resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz",
|
||||
"integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"decimal.js-light": "^2.4.1"
|
||||
}
|
||||
},
|
||||
"node_modules/recharts/node_modules/react-is": {
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
|
||||
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/reflect.getprototypeof": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.5.tgz",
|
||||
@@ -3279,6 +3593,12 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/regenerator-runtime": {
|
||||
"version": "0.14.1",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
|
||||
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/regexp.prototype.flags": {
|
||||
"version": "1.5.2",
|
||||
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz",
|
||||
@@ -3730,6 +4050,12 @@
|
||||
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/tiny-invariant": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
|
||||
"integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/to-regex-range": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||
@@ -3889,6 +4215,28 @@
|
||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/victory-vendor": {
|
||||
"version": "36.9.2",
|
||||
"resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz",
|
||||
"integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==",
|
||||
"license": "MIT AND ISC",
|
||||
"dependencies": {
|
||||
"@types/d3-array": "^3.0.3",
|
||||
"@types/d3-ease": "^3.0.0",
|
||||
"@types/d3-interpolate": "^3.0.1",
|
||||
"@types/d3-scale": "^4.0.2",
|
||||
"@types/d3-shape": "^3.1.0",
|
||||
"@types/d3-time": "^3.0.0",
|
||||
"@types/d3-timer": "^3.0.0",
|
||||
"d3-array": "^3.1.6",
|
||||
"d3-ease": "^3.0.1",
|
||||
"d3-interpolate": "^3.0.1",
|
||||
"d3-scale": "^4.0.2",
|
||||
"d3-shape": "^3.1.0",
|
||||
"d3-time": "^3.0.0",
|
||||
"d3-timer": "^3.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
@@ -4012,6 +4360,14 @@
|
||||
"integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==",
|
||||
"dev": true
|
||||
},
|
||||
"@babel/runtime": {
|
||||
"version": "7.26.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.7.tgz",
|
||||
"integrity": "sha512-AOPI3D+a8dXnja+iwsUqGRjr1BbZIe771sXdapOtYI531gSqpi92vXivKcq2asu/DFpdl1ceFAKZyRzK2PCVcQ==",
|
||||
"requires": {
|
||||
"regenerator-runtime": "^0.14.0"
|
||||
}
|
||||
},
|
||||
"@emotion/is-prop-valid": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz",
|
||||
@@ -4299,6 +4655,60 @@
|
||||
"@types/har-format": "*"
|
||||
}
|
||||
},
|
||||
"@types/d3-array": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz",
|
||||
"integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg=="
|
||||
},
|
||||
"@types/d3-color": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz",
|
||||
"integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A=="
|
||||
},
|
||||
"@types/d3-ease": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz",
|
||||
"integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA=="
|
||||
},
|
||||
"@types/d3-interpolate": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz",
|
||||
"integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==",
|
||||
"requires": {
|
||||
"@types/d3-color": "*"
|
||||
}
|
||||
},
|
||||
"@types/d3-path": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz",
|
||||
"integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg=="
|
||||
},
|
||||
"@types/d3-scale": {
|
||||
"version": "4.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz",
|
||||
"integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==",
|
||||
"requires": {
|
||||
"@types/d3-time": "*"
|
||||
}
|
||||
},
|
||||
"@types/d3-shape": {
|
||||
"version": "3.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz",
|
||||
"integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==",
|
||||
"requires": {
|
||||
"@types/d3-path": "*"
|
||||
}
|
||||
},
|
||||
"@types/d3-time": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz",
|
||||
"integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g=="
|
||||
},
|
||||
"@types/d3-timer": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz",
|
||||
"integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw=="
|
||||
},
|
||||
"@types/file-saver": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/file-saver/-/file-saver-2.0.7.tgz",
|
||||
@@ -4765,6 +5175,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"clsx": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
|
||||
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
@@ -4825,8 +5240,84 @@
|
||||
"csstype": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
|
||||
"dev": true
|
||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
|
||||
},
|
||||
"d3-array": {
|
||||
"version": "3.2.4",
|
||||
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz",
|
||||
"integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
|
||||
"requires": {
|
||||
"internmap": "1 - 2"
|
||||
}
|
||||
},
|
||||
"d3-color": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
|
||||
"integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA=="
|
||||
},
|
||||
"d3-ease": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
|
||||
"integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w=="
|
||||
},
|
||||
"d3-format": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz",
|
||||
"integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA=="
|
||||
},
|
||||
"d3-interpolate": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
|
||||
"integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
|
||||
"requires": {
|
||||
"d3-color": "1 - 3"
|
||||
}
|
||||
},
|
||||
"d3-path": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz",
|
||||
"integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ=="
|
||||
},
|
||||
"d3-scale": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
|
||||
"integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
|
||||
"requires": {
|
||||
"d3-array": "2.10.0 - 3",
|
||||
"d3-format": "1 - 3",
|
||||
"d3-interpolate": "1.2.0 - 3",
|
||||
"d3-time": "2.1.1 - 3",
|
||||
"d3-time-format": "2 - 4"
|
||||
}
|
||||
},
|
||||
"d3-shape": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz",
|
||||
"integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
|
||||
"requires": {
|
||||
"d3-path": "^3.1.0"
|
||||
}
|
||||
},
|
||||
"d3-time": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz",
|
||||
"integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",
|
||||
"requires": {
|
||||
"d3-array": "2 - 3"
|
||||
}
|
||||
},
|
||||
"d3-time-format": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
|
||||
"integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
|
||||
"requires": {
|
||||
"d3-time": "1 - 3"
|
||||
}
|
||||
},
|
||||
"d3-timer": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
|
||||
"integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA=="
|
||||
},
|
||||
"debug": {
|
||||
"version": "4.3.4",
|
||||
@@ -4837,6 +5328,11 @@
|
||||
"ms": "2.1.2"
|
||||
}
|
||||
},
|
||||
"decimal.js-light": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz",
|
||||
"integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg=="
|
||||
},
|
||||
"deep-is": {
|
||||
"version": "0.1.4",
|
||||
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
|
||||
@@ -4883,6 +5379,15 @@
|
||||
"esutils": "^2.0.2"
|
||||
}
|
||||
},
|
||||
"dom-helpers": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
|
||||
"integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.8.7",
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"es-abstract": {
|
||||
"version": "1.22.4",
|
||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.4.tgz",
|
||||
@@ -5181,12 +5686,22 @@
|
||||
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
|
||||
"dev": true
|
||||
},
|
||||
"eventemitter3": {
|
||||
"version": "4.0.7",
|
||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
|
||||
"integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="
|
||||
},
|
||||
"fast-deep-equal": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
||||
"dev": true
|
||||
},
|
||||
"fast-equals": {
|
||||
"version": "5.2.2",
|
||||
"resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.2.2.tgz",
|
||||
"integrity": "sha512-V7/RktU11J3I36Nwq2JnZEM7tNm17eBJz+u25qdxBZeCKiX6BkVSZQjwWIr+IobgnZy+ag73tTZgZi7tr0LrBw=="
|
||||
},
|
||||
"fast-glob": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
|
||||
@@ -5529,6 +6044,11 @@
|
||||
"side-channel": "^1.0.4"
|
||||
}
|
||||
},
|
||||
"internmap": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
|
||||
"integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg=="
|
||||
},
|
||||
"is-array-buffer": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz",
|
||||
@@ -6164,6 +6684,27 @@
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
||||
},
|
||||
"react-smooth": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.4.tgz",
|
||||
"integrity": "sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q==",
|
||||
"requires": {
|
||||
"fast-equals": "^5.0.1",
|
||||
"prop-types": "^15.8.1",
|
||||
"react-transition-group": "^4.4.5"
|
||||
}
|
||||
},
|
||||
"react-transition-group": {
|
||||
"version": "4.4.5",
|
||||
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
|
||||
"integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.5.5",
|
||||
"dom-helpers": "^5.0.1",
|
||||
"loose-envify": "^1.4.0",
|
||||
"prop-types": "^15.6.2"
|
||||
}
|
||||
},
|
||||
"readable-stream": {
|
||||
"version": "3.6.2",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
|
||||
@@ -6182,6 +6723,36 @@
|
||||
"picomatch": "^2.2.1"
|
||||
}
|
||||
},
|
||||
"recharts": {
|
||||
"version": "2.15.1",
|
||||
"resolved": "https://registry.npmjs.org/recharts/-/recharts-2.15.1.tgz",
|
||||
"integrity": "sha512-v8PUTUlyiDe56qUj82w/EDVuzEFXwEHp9/xOowGAZwfLjB9uAy3GllQVIYMWF6nU+qibx85WF75zD7AjqoT54Q==",
|
||||
"requires": {
|
||||
"clsx": "^2.0.0",
|
||||
"eventemitter3": "^4.0.1",
|
||||
"lodash": "^4.17.21",
|
||||
"react-is": "^18.3.1",
|
||||
"react-smooth": "^4.0.4",
|
||||
"recharts-scale": "^0.4.4",
|
||||
"tiny-invariant": "^1.3.1",
|
||||
"victory-vendor": "^36.6.8"
|
||||
},
|
||||
"dependencies": {
|
||||
"react-is": {
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
|
||||
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"recharts-scale": {
|
||||
"version": "0.4.5",
|
||||
"resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz",
|
||||
"integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==",
|
||||
"requires": {
|
||||
"decimal.js-light": "^2.4.1"
|
||||
}
|
||||
},
|
||||
"reflect.getprototypeof": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.5.tgz",
|
||||
@@ -6197,6 +6768,11 @@
|
||||
"which-builtin-type": "^1.1.3"
|
||||
}
|
||||
},
|
||||
"regenerator-runtime": {
|
||||
"version": "0.14.1",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
|
||||
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
|
||||
},
|
||||
"regexp.prototype.flags": {
|
||||
"version": "1.5.2",
|
||||
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz",
|
||||
@@ -6507,6 +7083,11 @@
|
||||
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
|
||||
"dev": true
|
||||
},
|
||||
"tiny-invariant": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
|
||||
"integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="
|
||||
},
|
||||
"to-regex-range": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||
@@ -6620,6 +7201,27 @@
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
|
||||
},
|
||||
"victory-vendor": {
|
||||
"version": "36.9.2",
|
||||
"resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz",
|
||||
"integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==",
|
||||
"requires": {
|
||||
"@types/d3-array": "^3.0.3",
|
||||
"@types/d3-ease": "^3.0.0",
|
||||
"@types/d3-interpolate": "^3.0.1",
|
||||
"@types/d3-scale": "^4.0.2",
|
||||
"@types/d3-shape": "^3.1.0",
|
||||
"@types/d3-time": "^3.0.0",
|
||||
"@types/d3-timer": "^3.0.0",
|
||||
"d3-array": "^3.1.6",
|
||||
"d3-ease": "^3.0.1",
|
||||
"d3-interpolate": "^3.0.1",
|
||||
"d3-scale": "^4.0.2",
|
||||
"d3-shape": "^3.1.0",
|
||||
"d3-time": "^3.0.0",
|
||||
"d3-timer": "^3.0.1"
|
||||
}
|
||||
},
|
||||
"which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
"process": "^0.11.10",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"recharts": "^2.15.1",
|
||||
"rgbquant": "^1.1.2",
|
||||
"stream": "^0.0.3",
|
||||
"stream-browserify": "^3.0.0",
|
||||
|
||||
@@ -11,6 +11,7 @@ import { debounce } from "lodash";
|
||||
import { TC_LOGO } from "../../constants";
|
||||
import styled from "styled-components";
|
||||
import { MuralEx } from "../../interfaces";
|
||||
import { PieCharts } from "./pixelCharts";
|
||||
|
||||
const Img = styled.img`
|
||||
width: 25px;
|
||||
@@ -35,6 +36,7 @@ interface Props {
|
||||
interface State extends StoreSettings{
|
||||
selected?: MuralEx;
|
||||
overlays: number[];
|
||||
showCharts: boolean;
|
||||
phantomOverlay: number;
|
||||
overlayModify?: OverlayReturn;
|
||||
}
|
||||
@@ -46,6 +48,7 @@ export class Main extends React.Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
showCharts: false,
|
||||
overlays: [],
|
||||
phantomOverlay: -1,
|
||||
opacity: 50,
|
||||
@@ -106,8 +109,14 @@ export class Main extends React.Component<Props, State> {
|
||||
|
||||
render() {
|
||||
return <>
|
||||
<MovableWindow title={this.title()} storage={this.props.storage} storageKey="main" >
|
||||
{this.state.showCharts ?
|
||||
<MovableWindow title={"Pixel chart"} storage={this.props.storage} storageKey="charts">
|
||||
<PieCharts palette={this.props.palette} store={this.props.store}/>
|
||||
</MovableWindow>
|
||||
: null}
|
||||
<MovableWindow title={this.title()} storage={this.props.storage} storageKey="main">
|
||||
<Menu
|
||||
showChart={() => this.setState({showCharts: !this.state.showCharts})}
|
||||
onOpacityChange={this.opacityChange}
|
||||
cords={this.props.cords}
|
||||
store={this.props.store}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { faCamera, faCaretDown, faCaretUp, faUpload } from "@fortawesome/free-solid-svg-icons";
|
||||
import { faCamera, faCaretDown, faCaretUp, faPieChart, faUpload } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
@@ -76,6 +76,7 @@ interface Props {
|
||||
storage: Storage;
|
||||
opacity: number;
|
||||
collapsed: boolean;
|
||||
showChart: () => void;
|
||||
onOpacityChange: (n: number) => void;
|
||||
onCollapsedChanged: (b: boolean) => void;
|
||||
}
|
||||
@@ -130,13 +131,30 @@ export class Menu extends React.Component<Props, State> {
|
||||
this.setState({takingCanvasShot: false});
|
||||
};
|
||||
render() {
|
||||
return <Container>
|
||||
return<Container>
|
||||
<Flex>
|
||||
<Btn onClick={this.screenshot} disabled={this.state.takingCanvasShot || !this.state.canvasShotAvailable} title="Screenshot"><FontAwesomeIcon icon={ faCamera } /></Btn>
|
||||
<Btn onClick={this.import} title="Import"><FontAwesomeIcon icon={ faUpload } /></Btn>
|
||||
<Btn
|
||||
onClick={this.screenshot}
|
||||
disabled={this.state.takingCanvasShot || !this.state.canvasShotAvailable}
|
||||
title="Screenshot">
|
||||
<FontAwesomeIcon icon={ faCamera } />
|
||||
</Btn>
|
||||
<Btn
|
||||
onClick={this.import}
|
||||
title="Import">
|
||||
<FontAwesomeIcon icon={ faUpload } />
|
||||
</Btn>
|
||||
<Btn
|
||||
onClick={() => this.props.showChart()}
|
||||
title="Pixels graph">
|
||||
<FontAwesomeIcon icon={ faPieChart } />
|
||||
</Btn>
|
||||
<InputRange type="range" min={0} max={100} value={this.props.opacity} onInput={this.inputElement} />
|
||||
<PercentageDiv>{this.props.opacity}%</PercentageDiv>
|
||||
<Btn onClick={() => this.props.onCollapsedChanged(!this.props.collapsed)}><FontAwesomeIcon icon={ this.props.collapsed ? faCaretDown : faCaretUp } /></Btn>
|
||||
<Btn
|
||||
onClick={() => this.props.onCollapsedChanged(!this.props.collapsed)}>
|
||||
<FontAwesomeIcon icon={ this.props.collapsed ? faCaretDown : faCaretUp } />
|
||||
</Btn>
|
||||
</Flex>
|
||||
{this.props.collapsed ? null : <MuralList store={this.props.store} palette={this.props.palette} cords={this.props.cords}/> }
|
||||
</Container>;
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
import React from "react";
|
||||
import { Palette } from "../../lib/palette";
|
||||
import { Store, StoreEvents } from "../../lib/store";
|
||||
import { PieChart, Pie, Cell, ResponsiveContainer } from "recharts";
|
||||
import styled from "styled-components";
|
||||
import { formatNumber } from "../../lib/utils";
|
||||
|
||||
const ColorBlock = styled.div`
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-right: 5px;
|
||||
border: 1px solid black;
|
||||
border-radius: 12px;
|
||||
`;
|
||||
|
||||
const ColorLine = styled.div`
|
||||
padding: 4px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
font-size: 15px;
|
||||
`;
|
||||
const FlexList = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
height: 100%;
|
||||
`;
|
||||
interface PixelData {
|
||||
color: string;
|
||||
value: number;
|
||||
}
|
||||
|
||||
interface State {
|
||||
total: number;
|
||||
groupData?: PixelData[];
|
||||
}
|
||||
|
||||
interface Props {
|
||||
store: Store;
|
||||
palette: Palette;
|
||||
}
|
||||
|
||||
export class PieCharts extends React.Component<Props, State> {
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
total: 0,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
this.refresh();
|
||||
this.props.store.on(StoreEvents.PixelLog, this.refresh);
|
||||
}
|
||||
componentWillUnmount() {
|
||||
this.props.store.off(StoreEvents.PixelLog, this.refresh);
|
||||
}
|
||||
private refresh = async () => {
|
||||
const colors: string[] = [];
|
||||
const groupData: PixelData[] = [];
|
||||
let total = 0;
|
||||
for (let i = 0; i < this.props.palette.hex.length; i++) {
|
||||
const hex = this.props.palette.hex[i];
|
||||
colors.push(hex);
|
||||
const logs = await this.props.store.fetchPixelsLogByColor(i);
|
||||
if (logs.length) {
|
||||
total += logs.length;
|
||||
groupData.push({
|
||||
color: hex,
|
||||
value: logs.length
|
||||
});
|
||||
}
|
||||
}
|
||||
groupData.sort((a,b) => a.value > b.value ? -1 : 1);
|
||||
this.setState(({
|
||||
groupData,
|
||||
total
|
||||
}));
|
||||
};
|
||||
render(): React.ReactNode
|
||||
{
|
||||
if (!this.state.groupData ) {
|
||||
return <div>Loading</div>;
|
||||
}
|
||||
|
||||
if (!this.state.groupData.length) {
|
||||
return <div>No data! Please a pixel to see pixel chart</div>;
|
||||
}
|
||||
const style:React.CSSProperties = {
|
||||
width: Math.min(250, window.innerWidth - 20),
|
||||
height: Math.min(250, window.innerHeight - 20)
|
||||
};
|
||||
|
||||
return <div>
|
||||
<h5>Total pixels: {formatNumber(this.state.total)}</h5>
|
||||
<div style={style}>
|
||||
<ResponsiveContainer width="100%" height="100%">
|
||||
<PieChart width={400} height={400}>
|
||||
<Pie
|
||||
data={this.state.groupData}
|
||||
cx="50%"
|
||||
cy="50%"
|
||||
labelLine={false}
|
||||
outerRadius={80}
|
||||
fill="#8884d8"
|
||||
dataKey="value"
|
||||
>
|
||||
{this.state.groupData.map((entry, index) => (
|
||||
<Cell
|
||||
key={`cell-${index}`}
|
||||
fill={entry.color}
|
||||
/>
|
||||
))}
|
||||
</Pie>
|
||||
</PieChart>
|
||||
</ResponsiveContainer>
|
||||
|
||||
</div>
|
||||
<div>
|
||||
<FlexList style={{ width: Math.min(250, window.innerWidth - 20)}}>
|
||||
{this.state.groupData.map((e, i) => {
|
||||
return <ColorLine key={i}>
|
||||
<ColorBlock style={{
|
||||
backgroundColor: e.color
|
||||
}}></ColorBlock>{formatNumber(e.value)}</ColorLine>;
|
||||
})}
|
||||
|
||||
</FlexList>
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import styled from "styled-components";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faSquare } from "@fortawesome/free-solid-svg-icons";
|
||||
import { formatNumber } from "../../lib/utils";
|
||||
import { StoreEvents } from "../../lib/store";
|
||||
|
||||
const CountBox = styled.div<{width: number}>`
|
||||
position: fixed;
|
||||
@@ -48,20 +49,16 @@ export class PixelCount extends React.Component<Props, State> {
|
||||
};
|
||||
}
|
||||
componentDidMount() {
|
||||
this.props.pixelCount.count.then(countMaybe => {
|
||||
if (typeof countMaybe === "number") {
|
||||
this.updateCount(countMaybe);
|
||||
}
|
||||
});
|
||||
this.props.pixelCount.on(this.updateCount);
|
||||
window.addEventListener("resize", this.onResize);
|
||||
this.updateCount();
|
||||
this.props.pixelCount.store.on(StoreEvents.PixelLog, this.updateCount);
|
||||
window.addEventListener("resize", this.onResize);
|
||||
}
|
||||
componentWillUnmount() {
|
||||
this.props.pixelCount.on(this.updateCount);
|
||||
this.props.pixelCount.store.off(StoreEvents.PixelLog, this.updateCount);
|
||||
window.removeEventListener("resize", this.onResize);
|
||||
}
|
||||
updateCount = (count: number) => {
|
||||
this.setState({count});
|
||||
updateCount = async () => {
|
||||
this.setState({count: await this.props.pixelCount.getCount()});
|
||||
};
|
||||
onResize = () => {
|
||||
this.setState({ width: window.innerWidth });
|
||||
|
||||
@@ -98,7 +98,7 @@ export async function importArtWorks(
|
||||
const pixels = await imageToMural(img, palette);
|
||||
const mural = await new Promise<Mural>((resolve, reject)=> {
|
||||
store.setOverlayModify({
|
||||
pixels,
|
||||
mural,
|
||||
muralObj: {
|
||||
name: img.alt,
|
||||
x: Math.floor(cords.ux - (img.width / 2)),
|
||||
|
||||
+2
-2
@@ -14,12 +14,12 @@ async function main() {
|
||||
const palette = new Palette();
|
||||
while(!palette.init()) { await waitForDraw();}
|
||||
const storage = new Storage(ENVIRONMENT === "browser-extension");
|
||||
const pixelPlaced = PixelPlaced.create(storage);
|
||||
const store = new Store(storage, palette);
|
||||
await store.load();
|
||||
const pixelPlaced = PixelPlaced.create(store);
|
||||
const coordinates = new Coordinates();
|
||||
await coordinates.init();
|
||||
|
||||
|
||||
createUI(store,storage, coordinates, palette, pixelPlaced);
|
||||
|
||||
console.log(
|
||||
|
||||
+102
-11
@@ -1,14 +1,105 @@
|
||||
// Creating artificial API between two worlds
|
||||
|
||||
const log = console.log;
|
||||
console.log = (...args: any) => {
|
||||
const firstArg = args[0];
|
||||
if (firstArg && typeof firstArg === "object" && "pixelsPlaced" in firstArg) {
|
||||
const pixelsPlaced = new CustomEvent("__pixelsPlaced", {
|
||||
detail: firstArg.pixelsPlaced,
|
||||
});
|
||||
document.dispatchEvent(pixelsPlaced);
|
||||
}
|
||||
log(...args);
|
||||
};
|
||||
export type InterceptBefore = (requestInfo: string, init?: RequestInit) => Promise<void> | void;
|
||||
export type InterceptAfter = (requestInfo: string, response: Response, init?: RequestInit) => Promise<void> | void;
|
||||
|
||||
export class Interceptor {
|
||||
|
||||
private static fetch = window.fetch;
|
||||
private static overwritten = false;
|
||||
private static before = new Map<string, InterceptBefore[]>();
|
||||
private static after = new Map<string, InterceptAfter[]>();
|
||||
|
||||
private constructor() {}
|
||||
|
||||
private static overrideFetch() {
|
||||
if (this.overwritten) {
|
||||
return;
|
||||
}
|
||||
this.overwritten = true;
|
||||
(window as any).fetch = async (...args: any) => {
|
||||
const url = ((args[0] as RequestInfo | URL) || "").toString();
|
||||
const init = args[1];
|
||||
|
||||
const befores = [...(this.before.get(url) || []), ...(this.before.get("*") || [])];
|
||||
if (befores) {
|
||||
for (const before of befores) {
|
||||
await before(url, init);
|
||||
}
|
||||
}
|
||||
|
||||
const response = (this.fetch as any)(...args);
|
||||
const afters = [...(this.after.get(url) || []), ...(this.after.get("*") || [])];
|
||||
if (afters) {
|
||||
const result = await response;
|
||||
for (const after of afters) {
|
||||
try {
|
||||
await after(url, result, init);
|
||||
} catch (_) {}
|
||||
}
|
||||
return response;
|
||||
} else {
|
||||
return response;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static onAfter(url: string, cb: InterceptAfter) {
|
||||
Interceptor.overrideFetch();
|
||||
const arr = Interceptor.after.get(url) || [];
|
||||
if (!arr.includes(cb)) {
|
||||
arr.push(cb);
|
||||
}
|
||||
Interceptor.after.set(url, arr);
|
||||
}
|
||||
static offAfter(url: string, cb: InterceptAfter) {
|
||||
const arr = Interceptor.after.get(url) || [];
|
||||
const index = arr.indexOf(cb);
|
||||
if (index !== -1) {
|
||||
arr.splice(index, 1);
|
||||
}
|
||||
if (arr.length) {
|
||||
Interceptor.after.set(url, arr);
|
||||
} else {
|
||||
Interceptor.after.delete(url);
|
||||
}
|
||||
}
|
||||
|
||||
static onBefore(url: string, cb: InterceptBefore) {
|
||||
Interceptor.overrideFetch();
|
||||
const arr = Interceptor.before.get(url) || [];
|
||||
if (!arr.includes(cb)) {
|
||||
arr.push(cb);
|
||||
}
|
||||
Interceptor.before.set(url, arr);
|
||||
}
|
||||
|
||||
static offBefore(url: string, cb: InterceptBefore) {
|
||||
const arr = Interceptor.before.get(url) || [];
|
||||
const index = arr.indexOf(cb);
|
||||
if (index !== -1) {
|
||||
arr.splice(index, 1);
|
||||
}
|
||||
if (arr.length) {
|
||||
Interceptor.before.set(url, arr);
|
||||
} else {
|
||||
Interceptor.before.delete(url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Interceptor.onAfter("/api/pixel", async (_, response, init) => {
|
||||
try {
|
||||
if (response.status === 200 && init && "body" in init) {
|
||||
const body = JSON.parse(init.body as any);
|
||||
if ("x" in body && "y" in body && "color" in body) {
|
||||
const pixelsPlaced = new CustomEvent("__pixelsPlaced", {
|
||||
detail: { color: body.color, x: body.x, y: body.y },
|
||||
});
|
||||
document.dispatchEvent(pixelsPlaced);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
export type InterceptBefore = (requestInfo: string, init?: RequestInit) => Promise<void> | void;
|
||||
export type InterceptAfter = (requestInfo: string, response: Response, init?: RequestInit) => Promise<void> | void;
|
||||
|
||||
export class Interceptor {
|
||||
|
||||
private static fetch = window.fetch;
|
||||
private static overwritten = false;
|
||||
private static before = new Map<string, InterceptBefore[]>();
|
||||
private static after = new Map<string, InterceptAfter[]>();
|
||||
|
||||
private constructor() {}
|
||||
|
||||
private static overrideFetch() {
|
||||
if (this.overwritten) {
|
||||
return;
|
||||
}
|
||||
this.overwritten = true;
|
||||
(window as any).fetch = async (...args: any) => {
|
||||
const url = ((args[0] as RequestInfo | URL) || "").toString();
|
||||
const init = args[1];
|
||||
|
||||
const befores = [...(this.before.get(url) || []), ...(this.before.get("*") || [])];
|
||||
if (befores) {
|
||||
for (const before of befores) {
|
||||
await before(url, init);
|
||||
}
|
||||
}
|
||||
|
||||
const response = (this.fetch as any)(...args);
|
||||
const afters = [...(this.after.get(url) || []), ...(this.after.get("*") || [])];
|
||||
if (afters) {
|
||||
const result = await response;
|
||||
for (const after of afters) {
|
||||
try {
|
||||
await after(url, result, init);
|
||||
} catch (_) {}
|
||||
}
|
||||
return response;
|
||||
} else {
|
||||
return response;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static onAfter(url: string, cb: InterceptAfter) {
|
||||
Interceptor.overrideFetch();
|
||||
const arr = Interceptor.after.get(url) || [];
|
||||
if (!arr.includes(cb)) {
|
||||
arr.push(cb);
|
||||
}
|
||||
Interceptor.after.set(url, arr);
|
||||
}
|
||||
static offAfter(url: string, cb: InterceptAfter) {
|
||||
const arr = Interceptor.after.get(url) || [];
|
||||
const index = arr.indexOf(cb);
|
||||
if (index !== -1) {
|
||||
arr.splice(index, 1);
|
||||
}
|
||||
if (arr.length) {
|
||||
Interceptor.after.set(url, arr);
|
||||
} else {
|
||||
Interceptor.after.delete(url);
|
||||
}
|
||||
}
|
||||
|
||||
static onBefore(url: string, cb: InterceptBefore) {
|
||||
Interceptor.overrideFetch();
|
||||
const arr = Interceptor.before.get(url) || [];
|
||||
if (!arr.includes(cb)) {
|
||||
arr.push(cb);
|
||||
}
|
||||
Interceptor.before.set(url, arr);
|
||||
}
|
||||
static offBefore(url: string, cb: InterceptBefore) {
|
||||
const arr = Interceptor.before.get(url) || [];
|
||||
const index = arr.indexOf(cb);
|
||||
if (index !== -1) {
|
||||
arr.splice(index, 1);
|
||||
}
|
||||
if (arr.length) {
|
||||
Interceptor.before.set(url, arr);
|
||||
} else {
|
||||
Interceptor.before.delete(url);
|
||||
}
|
||||
}
|
||||
}
|
||||
+23
-20
@@ -1,13 +1,16 @@
|
||||
import { INJECT_SCRIPT_PIXEL_OBSERVER } from "../constants";
|
||||
import { BasicEventEmitter } from "./eventEmitter";
|
||||
import { Storage } from "./storage";
|
||||
import { Store } from "./store";
|
||||
|
||||
interface Pixel {
|
||||
x: number;
|
||||
y: number;
|
||||
color: number;
|
||||
}
|
||||
|
||||
export class PixelPlaced {
|
||||
private emitter = new BasicEventEmitter();
|
||||
export class PixelPlaced {
|
||||
private static instance: PixelPlaced;
|
||||
private readonly storageKey = "__pixelsPlaced";
|
||||
private constructor(private storage: Storage) {
|
||||
private constructor(public readonly store: Store) {
|
||||
// The website is logging pixels as object onto pixelsPlaced onto console with .log.
|
||||
// With extension we need to cross 2 worlds. Adding artificial api to document
|
||||
|
||||
@@ -26,28 +29,28 @@ export class PixelPlaced {
|
||||
}
|
||||
|
||||
document.body.appendChild(api);
|
||||
(document as any).addEventListener("__pixelsPlaced", (event: CustomEvent<number>) => {
|
||||
if (typeof event.detail === "number") {
|
||||
this.storage.setItem(this.storageKey, event.detail);
|
||||
this.emitter.emit(0, event.detail);
|
||||
(document as any)
|
||||
.addEventListener("__pixelsPlaced", (event: CustomEvent<Pixel>) => {
|
||||
const body = event.detail;
|
||||
console.log(body);
|
||||
if (typeof body === "object" && "x" in body && "y" in body && "color" in body
|
||||
&& typeof body.color === "number" && typeof body.x === "number"
|
||||
&& typeof body.y === "number"
|
||||
) {
|
||||
const data = event.detail;
|
||||
this.store.addPixelLog(data.x, data.y, data.color);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static create(storage: Storage) {
|
||||
static create(store: Store) {
|
||||
if (!this.instance) {
|
||||
this.instance = new PixelPlaced(storage);
|
||||
this.instance = new PixelPlaced(store);
|
||||
}
|
||||
return this.instance;
|
||||
}
|
||||
|
||||
on(fn: (count: number) => void) {
|
||||
this.emitter.on(0, fn);
|
||||
}
|
||||
off(fn: (count: number) => void) {
|
||||
this.emitter.off(0, fn);
|
||||
}
|
||||
get count() {
|
||||
return this.storage.getItem(this.storageKey);
|
||||
getCount() {
|
||||
return this.store.getPixelCount();
|
||||
}
|
||||
|
||||
}
|
||||
+133
-4
@@ -16,6 +16,7 @@ export enum StoreEvents {
|
||||
MuralSelect,
|
||||
MuralOverlay,
|
||||
MuralPhantomOverlay,
|
||||
PixelLog,
|
||||
Any,
|
||||
}
|
||||
|
||||
@@ -25,23 +26,75 @@ export interface OverlayReturn {
|
||||
cb: (name: string, x: number, y: number, confirm?: boolean) => void;
|
||||
}
|
||||
|
||||
export interface PixelLog {
|
||||
timestamp: string;
|
||||
x: number;
|
||||
y: number;
|
||||
color: number;
|
||||
}
|
||||
|
||||
export interface PixelLogEx extends PixelLog {
|
||||
id: number;
|
||||
}
|
||||
|
||||
export class Store implements LoadUnload {
|
||||
private readonly STORAGE_KEY_MURAL = "_murals";
|
||||
private readonly STORAGE_KEY_SELECTED = "_mural";
|
||||
private readonly STORAGE_KEY_LOG_STORE = "records";
|
||||
private readonly STORAGE_KEY_PIXEL_LOG = "_pixel-log";
|
||||
private _murals: MuralEx[] = [];
|
||||
private _selected?: MuralEx;
|
||||
private emitter = new BasicEventEmitter();
|
||||
private _overlayIndices: number[] = [];
|
||||
private _phantomOverlay = -1;
|
||||
private _overlayModify?: OverlayReturn;
|
||||
private dbLog?: IDBDatabase;
|
||||
private db = localforage.createInstance({
|
||||
name: "pixel-canvas-overlay"
|
||||
});
|
||||
|
||||
});
|
||||
constructor(private storage: Storage, private palette: Palette) {}
|
||||
|
||||
async load() {
|
||||
console.log("loaded");
|
||||
globalThis.store = this;
|
||||
const pixelCanvasLog = "pixel-canvas-log";
|
||||
const logDb = indexedDB.open(pixelCanvasLog, 1);
|
||||
|
||||
logDb.addEventListener("upgradeneeded", event => {
|
||||
if (event) {
|
||||
const db = (event?.target as any).result;
|
||||
const store = db
|
||||
.createObjectStore(this.STORAGE_KEY_LOG_STORE,
|
||||
{ keyPath: "id", autoIncrement: true }
|
||||
);
|
||||
store.createIndex("by_timestamp", "timestamp");
|
||||
store.createIndex("by_color", "color");
|
||||
}
|
||||
});
|
||||
|
||||
this.dbLog = await new Promise<IDBDatabase | undefined>((resolve, reject) => {
|
||||
logDb.addEventListener("success", event => {
|
||||
const db = (event.target as any)?.result as IDBDatabase;
|
||||
if (db) {
|
||||
resolve(db);
|
||||
} else {
|
||||
reject(new Error("IndexDB did not return db object"));
|
||||
}
|
||||
}, { once: true });
|
||||
logDb.addEventListener("error", event => {
|
||||
if (confirm(
|
||||
"Unable to load data. Would you like to continue? Some feature will not work correctly"
|
||||
)) {
|
||||
indexedDB.deleteDatabase(pixelCanvasLog);
|
||||
|
||||
resolve(undefined);
|
||||
} else {
|
||||
reject((event.target as IDBOpenDBRequest).error);
|
||||
}
|
||||
}, { once: true });
|
||||
});
|
||||
|
||||
|
||||
|
||||
this._murals = [];
|
||||
const rawMurals = await this.db.getItem<Uint8Array[]>(this.STORAGE_KEY_MURAL) || [];
|
||||
|
||||
@@ -77,6 +130,83 @@ export class Store implements LoadUnload {
|
||||
}
|
||||
}
|
||||
}
|
||||
addPixelLog(x: number, y: number, color: number) {
|
||||
const timestamp = new Date().toISOString();
|
||||
const data: PixelLog = {
|
||||
timestamp,
|
||||
color,
|
||||
x,
|
||||
y
|
||||
};
|
||||
|
||||
return new Promise<IDBValidKey | null>((resolve, reject) => {
|
||||
if (!this.dbLog) {
|
||||
resolve(null);
|
||||
return;
|
||||
}
|
||||
const tx = this.dbLog.transaction(this.STORAGE_KEY_LOG_STORE, "readwrite");
|
||||
const store = tx.objectStore(this.STORAGE_KEY_LOG_STORE);
|
||||
const request = store.add(data);
|
||||
request.addEventListener("success", () => {
|
||||
resolve(request.result);
|
||||
this.emit(StoreEvents.PixelLog);
|
||||
}, { once: true });
|
||||
request.addEventListener("error", () => {
|
||||
reject(request.error);
|
||||
}, { once: true });
|
||||
});
|
||||
}
|
||||
async fetchPixelsLogByColor(color: number) {
|
||||
if (!this.dbLog) return Promise.resolve([]);
|
||||
const tx = this.dbLog.transaction("records", "readonly");
|
||||
const store = tx.objectStore("records");
|
||||
const index = store.index("by_color");
|
||||
const range = IDBKeyRange.only(color);
|
||||
const request = index.openCursor(range);
|
||||
return this.cursorIterator(request);
|
||||
}
|
||||
|
||||
async fetchPixelsLogByTime(startDate: Date, endDate: Date) {
|
||||
if (!this.dbLog) return Promise.resolve([]);
|
||||
const tx = this.dbLog.transaction(this.STORAGE_KEY_LOG_STORE, "readonly");
|
||||
const store = tx.objectStore(this.STORAGE_KEY_LOG_STORE);
|
||||
const index = store.index("by_timestamp");
|
||||
const range = IDBKeyRange.bound(startDate.toISOString(), endDate.toISOString());
|
||||
const request = index.openCursor(range);
|
||||
return this.cursorIterator(request);
|
||||
}
|
||||
|
||||
async getPixelCount() {
|
||||
if (!this.dbLog) return Promise.resolve(0);
|
||||
const tx = this.dbLog.transaction(this.STORAGE_KEY_LOG_STORE, "readonly");
|
||||
const store = tx.objectStore(this.STORAGE_KEY_LOG_STORE);
|
||||
|
||||
return new Promise<number>((resolve, reject) => {
|
||||
const request = store.count();
|
||||
request.addEventListener("success", () => {
|
||||
resolve(request.result);
|
||||
}, { once: true });
|
||||
request.addEventListener("error", () => {
|
||||
reject(request.error);
|
||||
}, { once: true });
|
||||
});
|
||||
}
|
||||
private cursorIterator(request: IDBRequest<IDBCursorWithValue | null>) {
|
||||
const results: PixelLogEx[] = [];
|
||||
return new Promise<PixelLogEx[]>((resolve) => {
|
||||
request.addEventListener("success", event => {
|
||||
const cursor = (event.target as IDBRequest).result;
|
||||
if (cursor) {
|
||||
results.push(cursor.value);
|
||||
cursor.continue();
|
||||
} else {
|
||||
resolve(results);
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
updateMural(_mural: Mural) {
|
||||
this.save();
|
||||
this.emit(StoreEvents.MuralUpdated);
|
||||
@@ -90,7 +220,6 @@ export class Store implements LoadUnload {
|
||||
this.emit(StoreEvents.Any);
|
||||
this.save();
|
||||
}
|
||||
|
||||
remove(mural: MuralEx) {
|
||||
this._overlayIndices = [];
|
||||
this._overlayModify = undefined;
|
||||
|
||||
Reference in New Issue
Block a user