├── manifest.json ├── style.css ├── LICENSE ├── README.md └── index.js /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "WPM", 3 | "description": "View words per minute on a nice little indicator.", 4 | "author": "rom#5692", 5 | "version": "1.2.0", 6 | "license": "MIT" 7 | } -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | #wpm-indicator-text { 2 | color: rgba(255, 255, 255, 0.5); 3 | user-select: none; 4 | pointer-events: none; 5 | display: block; 6 | position: absolute; 7 | bottom: .2em; 8 | z-index: 1; 9 | } 10 | .message-1PNnaP #wpm-indicator-text { 11 | right: unset; 12 | left: 5px; 13 | } 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Roman F. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Welcome to wpm 👋

2 |

3 | Version 4 | 5 | License: MIT License 6 | 7 |

8 | 9 | > View words per minute on a nice little indicator. 10 | 11 | ### 🏠 [Homepage](https://github.com/romdotdog/wpm) 12 | 13 | ## Install 14 | 15 | ```sh 16 | git clone https://github.com/romdotdog/wpm 17 | ``` 18 | 19 | ## Author 20 | 21 | 👤 **rom** 22 | 23 | * Website: https://rom.dog 24 | * Github: [@romdotdog](https://github.com/romdotdog) 25 | * Discord: rom#5692 26 | 27 | ## 🤝 Contributing 28 | 29 | Contributions, issues and feature requests are welcome!
Feel free to check [issues page](https://github.com/romdotdog/wpm/issues). 30 | 31 | ## Show your support 32 | 33 | Give a ⭐️ if this project helped you! 34 | 35 | ## 📝 License 36 | 37 | Copyright © 2020 [romdotdog](https://github.com/romdotdog).
38 | This project is [MIT](https://github.com/romdotdog/wpm/blob/main/LICENSE) licensed. 39 | 40 | *** 41 | _This README was generated with ❤️ by [readme-md-generator](https://github.com/kefranabg/readme-md-generator)_ 42 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const { Plugin } = require('powercord/entities'); 2 | const { 3 | getModule, 4 | getModuleByDisplayName, 5 | React, 6 | Flux 7 | } = require("powercord/webpack"); 8 | const { inject, uninject } = require('powercord/injector'); 9 | 10 | module.exports = class WPMPlugin extends Plugin { 11 | start = 0; 12 | 13 | resetTime() { 14 | this.start = new Date().getTime(); 15 | } 16 | 17 | timeSinceReset() { 18 | return (new Date().getTime() - this.start) / 1000 / 60; 19 | } 20 | 21 | async startPlugin() { 22 | this.loadStylesheet('style.css'); 23 | 24 | this.resetTime(); 25 | // from https://github.com/Inve1951/BetterDiscordStuff/blob/master/plugins/CharacterCounter.plugin.js 26 | 27 | const channelStore = await getModule(["hasChannel", "getChannel"]); 28 | const channelIdStore = await getModule(["getChannelId", "getLastSelectedChannelId"]); 29 | 30 | const WPM = Flux.connectStores([channelIdStore], (props) => { 31 | const channel = channelStore.getChannel(channelIdStore.getChannelId()); 32 | props.rateLimitPerUser = channel.rateLimitPerUser; 33 | return props; 34 | })(({ initValue, rateLimitPerUser }) => { 35 | const [value, setValue] = React.useState(initValue || ""); 36 | this.setValue = setValue; 37 | 38 | if (value.trim().length < 2) { 39 | // reset time at zero or one character 40 | this.resetTime(); 41 | } 42 | 43 | const right = rateLimitPerUser ? 360 : 16; 44 | const wpm = value.split(" ").length / this.timeSinceReset(); 45 | return React.createElement( 46 | "span", 47 | { 48 | id: "wpm-indicator-text", 49 | style: { 50 | right 51 | } 52 | }, 53 | `${isFinite(wpm) ? Math.floor(wpm) : 0} WPM` 54 | ); 55 | }); 56 | 57 | const ChannelEditorContainer = await getModuleByDisplayName('ChannelEditorContainer'); 58 | 59 | inject('wpm-hook', ChannelEditorContainer.prototype, 'render', (args, res) => { 60 | setTimeout(() => { 61 | const text = document.querySelector('[data-slate-editor="true"]')?.innerText; 62 | if (text && this.setValue) { 63 | this.setValue(text); 64 | } 65 | }); 66 | return res; 67 | }); 68 | 69 | const TypingUsers = await getModule(m => m.default && m.default.displayName === 'FluxContainer(TypingUsers)'); 70 | 71 | inject('wpm-indicator', TypingUsers.default.prototype, 'render', (args, res) => React.createElement( 72 | React.Fragment, null, res, React.createElement(WPM, { initValue: document.querySelector('[data-slate-editor="true"]')?.innerText }) 73 | )); 74 | } 75 | 76 | pluginWillUnload() { 77 | uninject('wpm-indicator'); 78 | uninject('wpm-hook'); 79 | } 80 | }; 81 | --------------------------------------------------------------------------------