├── public ├── _redirects ├── icon.png ├── robots.txt ├── favicon.ico ├── logo192.png ├── logo512.png ├── crewmates │ ├── red.png │ ├── black.png │ ├── blue.png │ ├── brown.png │ ├── cyan.png │ ├── green.png │ ├── lime.png │ ├── orange.png │ ├── pink.png │ ├── purple.png │ ├── white.png │ ├── yellow.png │ ├── red_dead.png │ ├── black_dead.png │ ├── blue_dead.png │ ├── brown_dead.png │ ├── cyan_dead.png │ ├── green_dead.png │ ├── lime_dead.png │ ├── orange_dead.png │ ├── pink_dead.png │ ├── purple_dead.png │ ├── white_dead.png │ ├── yellow_dead.png │ └── .well-known │ │ └── assetlinks.json ├── .well-known │ └── assetlinks.json ├── badges │ ├── patron.svg │ ├── booster.svg │ ├── subscriber.svg │ └── developer.svg ├── manifest.json ├── discord.svg ├── index.html └── logo.svg ├── src ├── config.json ├── PlayerIcon.jsx ├── Title.jsx ├── PatreonButton.jsx ├── index.css ├── DiscordButton.jsx ├── ConnectButton.jsx ├── Button.jsx ├── Container.jsx ├── VerticalContainer.jsx ├── JoinTipCard.jsx ├── LinkButton.jsx ├── CodePage.jsx ├── index.js ├── ConnectionStatus.jsx ├── Badge.jsx ├── CodeInput.jsx ├── PlayerButton.jsx ├── GamePage.jsx └── App.jsx ├── devcontainer.json ├── README.md ├── .gitignore └── package.json /public/_redirects: -------------------------------------------------------------------------------- 1 | /* /index.html 200 -------------------------------------------------------------------------------- /src/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "syncIdLength": 6 3 | } -------------------------------------------------------------------------------- /devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "devPort": 3000, 3 | "forwardPorts": [ 3000 ] 4 | } -------------------------------------------------------------------------------- /public/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrofracassi/amongcord-sync/HEAD/public/icon.png -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrofracassi/amongcord-sync/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrofracassi/amongcord-sync/HEAD/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrofracassi/amongcord-sync/HEAD/public/logo512.png -------------------------------------------------------------------------------- /public/crewmates/red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrofracassi/amongcord-sync/HEAD/public/crewmates/red.png -------------------------------------------------------------------------------- /public/crewmates/black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrofracassi/amongcord-sync/HEAD/public/crewmates/black.png -------------------------------------------------------------------------------- /public/crewmates/blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrofracassi/amongcord-sync/HEAD/public/crewmates/blue.png -------------------------------------------------------------------------------- /public/crewmates/brown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrofracassi/amongcord-sync/HEAD/public/crewmates/brown.png -------------------------------------------------------------------------------- /public/crewmates/cyan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrofracassi/amongcord-sync/HEAD/public/crewmates/cyan.png -------------------------------------------------------------------------------- /public/crewmates/green.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrofracassi/amongcord-sync/HEAD/public/crewmates/green.png -------------------------------------------------------------------------------- /public/crewmates/lime.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrofracassi/amongcord-sync/HEAD/public/crewmates/lime.png -------------------------------------------------------------------------------- /public/crewmates/orange.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrofracassi/amongcord-sync/HEAD/public/crewmates/orange.png -------------------------------------------------------------------------------- /public/crewmates/pink.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrofracassi/amongcord-sync/HEAD/public/crewmates/pink.png -------------------------------------------------------------------------------- /public/crewmates/purple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrofracassi/amongcord-sync/HEAD/public/crewmates/purple.png -------------------------------------------------------------------------------- /public/crewmates/white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrofracassi/amongcord-sync/HEAD/public/crewmates/white.png -------------------------------------------------------------------------------- /public/crewmates/yellow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrofracassi/amongcord-sync/HEAD/public/crewmates/yellow.png -------------------------------------------------------------------------------- /public/crewmates/red_dead.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrofracassi/amongcord-sync/HEAD/public/crewmates/red_dead.png -------------------------------------------------------------------------------- /public/crewmates/black_dead.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrofracassi/amongcord-sync/HEAD/public/crewmates/black_dead.png -------------------------------------------------------------------------------- /public/crewmates/blue_dead.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrofracassi/amongcord-sync/HEAD/public/crewmates/blue_dead.png -------------------------------------------------------------------------------- /public/crewmates/brown_dead.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrofracassi/amongcord-sync/HEAD/public/crewmates/brown_dead.png -------------------------------------------------------------------------------- /public/crewmates/cyan_dead.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrofracassi/amongcord-sync/HEAD/public/crewmates/cyan_dead.png -------------------------------------------------------------------------------- /public/crewmates/green_dead.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrofracassi/amongcord-sync/HEAD/public/crewmates/green_dead.png -------------------------------------------------------------------------------- /public/crewmates/lime_dead.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrofracassi/amongcord-sync/HEAD/public/crewmates/lime_dead.png -------------------------------------------------------------------------------- /public/crewmates/orange_dead.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrofracassi/amongcord-sync/HEAD/public/crewmates/orange_dead.png -------------------------------------------------------------------------------- /public/crewmates/pink_dead.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrofracassi/amongcord-sync/HEAD/public/crewmates/pink_dead.png -------------------------------------------------------------------------------- /public/crewmates/purple_dead.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrofracassi/amongcord-sync/HEAD/public/crewmates/purple_dead.png -------------------------------------------------------------------------------- /public/crewmates/white_dead.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrofracassi/amongcord-sync/HEAD/public/crewmates/white_dead.png -------------------------------------------------------------------------------- /public/crewmates/yellow_dead.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrofracassi/amongcord-sync/HEAD/public/crewmates/yellow_dead.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Amongcord Sync 2 | 3 | Web application that lets you control [Amongcord](https://github.com/pedrofracassi/amongcord) without typing commands. -------------------------------------------------------------------------------- /src/PlayerIcon.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components' 3 | 4 | const PlayerIconWrapper = styled.img` 5 | height: 35px; 6 | ` 7 | 8 | export default function PlayerIcon ({ player }) { 9 | return 10 | } -------------------------------------------------------------------------------- /src/Title.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default function Title ({ className }) { 4 | return ( 5 |
6 | Amongcord Icon 7 |
8 | Amongcord Sync 9 |
10 | ) 11 | } -------------------------------------------------------------------------------- /public/crewmates/.well-known/assetlinks.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "relation": ["delegate_permission/common.handle_all_urls"], 3 | "target" : { "namespace": "android_app", "package_name": "xyz.amongcord.sync.twa", 4 | "sha256_cert_fingerprints": ["13:5B:B9:80:41:0F:94:15:C1:13:DE:8E:34:86:83:37:E1:CB:4A:72:34:EC:77:85:66:FF:CF:97:0C:B3:B7:4E"] } 5 | }] 6 | -------------------------------------------------------------------------------- /public/.well-known/assetlinks.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "relation": ["delegate_permission/common.handle_all_urls"], 3 | "target": { 4 | "namespace": "android_app", 5 | "package_name": "xyz.amongcord.sync.twa", 6 | "sha256_cert_fingerprints": [ 7 | "DD:0B:FF:95:B3:2E:18:9D:D7:A7:12:5D:43:DE:AB:0B:DB:45:DD:5B:07:B8:98:EF:72:B8:46:EC:72:06:48:44" 8 | ] 9 | } 10 | }] -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /src/PatreonButton.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import styled from 'styled-components' 4 | import LinkButton from './LinkButton' 5 | 6 | const PatreonIcon = styled.img` 7 | height: 15px; 8 | margin-right: 10px; 9 | ` 10 | 11 | export default function PatreonButton () { 12 | return ( 13 | Become a Patron 14 | ) 15 | } -------------------------------------------------------------------------------- /public/badges/patron.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Poppins:regular,bold'); 2 | 3 | body { 4 | margin: 0; 5 | font-family: 'Poppins', 'Roboto', 'Oxygen', 6 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 7 | sans-serif; 8 | -webkit-font-smoothing: antialiased; 9 | -moz-osx-font-smoothing: grayscale; 10 | } 11 | 12 | * { 13 | user-select: none; 14 | } 15 | 16 | *,*:focus,*:hover{ 17 | outline:none; 18 | } -------------------------------------------------------------------------------- /src/DiscordButton.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import styled from 'styled-components' 4 | import LinkButton from './LinkButton' 5 | 6 | const DiscordIcon = styled.img` 7 | height: 15px; 8 | margin-right: 10px; 9 | ` 10 | 11 | export default function DiscordButton () { 12 | return ( 13 | Add the bot to your server 14 | ) 15 | } -------------------------------------------------------------------------------- /src/ConnectButton.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import { syncIdLength } from './config.json' 4 | import Button from './Button' 5 | import styled from 'styled-components' 6 | 7 | const ConnectButtonWrapper = styled(Button)` 8 | width: 100%; 9 | ` 10 | 11 | export default function ConnectButton ({ syncId, connectToGame, loading }) { 12 | return ( 13 | Connect 14 | ) 15 | } -------------------------------------------------------------------------------- /public/badges/booster.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/Button.jsx: -------------------------------------------------------------------------------- 1 | const { default: styled } = require("styled-components") 2 | 3 | export default styled.button` 4 | border: none; 5 | height: 50px; 6 | font-family: 'Poppins'; 7 | font-weight: bold; 8 | font-size: 18px; 9 | line-height: 27px; 10 | color: #ffffff; 11 | background-color: #505DD6; 12 | border-radius: 25px; 13 | margin-bottom: 10px; 14 | cursor: pointer; 15 | 16 | &:hover { 17 | background-color: #444FB6; 18 | } 19 | 20 | &:focus, button:active { 21 | border: none; 22 | } 23 | 24 | &:disabled { 25 | background-color: #A7AEEB 26 | } 27 | ` -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Sync", 3 | "name": "Amongcord Sync", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "orientation": "portrait", 24 | "theme_color": "#505DD6", 25 | "background_color": "#ffffff" 26 | } 27 | -------------------------------------------------------------------------------- /src/Container.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components'; 3 | 4 | const ExternalContainer = styled.div` 5 | display: flex; 6 | justify-content: center; 7 | width: 100%; 8 | ` 9 | 10 | const InternalContainer = styled.div` 11 | margin-right: 25px; 12 | margin-left: 25px; 13 | text-align: center; 14 | width: 100%; 15 | max-width: 330px; 16 | ` 17 | 18 | export default function Container ({ children }) { 19 | return ( 20 | 21 | 22 | {children} 23 | 24 | 25 | ) 26 | } -------------------------------------------------------------------------------- /src/VerticalContainer.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components'; 3 | 4 | const ExternalContainer = styled.div` 5 | display: flex; 6 | justify-content: center; 7 | width: 100%; 8 | height: 100vh; 9 | ` 10 | 11 | const InternalContainer = styled.div` 12 | margin-right: 25px; 13 | margin-left: 25px; 14 | text-align: center; 15 | align-self: center; 16 | width: 100%; 17 | max-width: 330px; 18 | ` 19 | 20 | export default function VerticalContainer ({ children }) { 21 | return ( 22 | 23 | 24 | {children} 25 | 26 | 27 | ) 28 | } -------------------------------------------------------------------------------- /src/JoinTipCard.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components' 3 | 4 | const Wrapper = styled.div` 5 | background-color: #E6EAF1; 6 | color: #3A3B3C; 7 | padding: 15px; 8 | border-radius: 5px; 9 | ` 10 | 11 | const Code = styled.code` 12 | background-color: #CFD5E4; 13 | padding: 3px; 14 | border-radius: 5px; 15 | font-weight: 600; 16 | ` 17 | 18 | const Small = styled.div` 19 | font-size: 0.9em; 20 | color: #6B6C6D; 21 | ` 22 | 23 | export default function JoinTipCard () { 24 | return ( 25 | 26 | There are no players in this game. 27 |
28 | Join it by typing ,join on a text channel. 29 |
30 | ) 31 | } -------------------------------------------------------------------------------- /src/LinkButton.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import Button from './Button' 4 | import styled from 'styled-components' 5 | import Color from 'color' 6 | 7 | export default function LinkButton ({ link, children, color, margin }) { 8 | const LinkButtonWrapper = styled(Button)` 9 | background-color: ${color}; 10 | width: 100%; 11 | cursor: pointer; 12 | ${ margin ? 'margin-top: 20px;' : ''} 13 | 14 | &:hover { 15 | background-color: ${Color(color).darken(0.07)}; 16 | } 17 | 18 | &:focus, button:active { 19 | border: none; 20 | } 21 | ` 22 | 23 | function openLink () { 24 | window.open(link) 25 | } 26 | 27 | return ( 28 | {children} 29 | ) 30 | } -------------------------------------------------------------------------------- /public/badges/subscriber.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/discord.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 14 | Amongcord Sync 15 | 16 | 17 | 18 |
19 | 20 | 21 | -------------------------------------------------------------------------------- /src/CodePage.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import Title from './Title' 4 | import CodeInput from './CodeInput'; 5 | import ConnectButton from './ConnectButton'; 6 | import VerticalContainer from './VerticalContainer'; 7 | import DiscordButton from './DiscordButton'; 8 | import PatreonButton from './PatreonButton'; 9 | 10 | function CodePage ({ syncId, setSyncId, connectToGame, loading }) { 11 | return ( 12 | 13 | 14 | <div> 15 | <CodeInput syncId={syncId} setSyncId={setSyncId} /> 16 | <ConnectButton loading={loading} syncId={syncId} connectToGame={connectToGame} /> 17 | <DiscordButton/> 18 | <PatreonButton/> 19 | </div> 20 | </VerticalContainer> 21 | ) 22 | } 23 | 24 | export default CodePage; 25 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import * as Sentry from "@sentry/react"; 4 | import { Integrations } from "@sentry/tracing"; 5 | import './index.css'; 6 | import App from './App'; 7 | 8 | Sentry.init({ 9 | dsn: process.env.REACT_APP_SENTRY_DSN, 10 | integrations: [ 11 | new Integrations.BrowserTracing({ 12 | tracingOrigins: [ process.env.REACT_APP_SYNC_SERVER_ADDRESS ], 13 | beforeNavigate: context => { 14 | return { 15 | ...context, 16 | name: window.location.pathname.replace(/[A-Z0-9]{6}$/, '/'), 17 | } 18 | } 19 | }), 20 | ], 21 | tracesSampleRate: 1.0, 22 | }) 23 | 24 | const rootElement = document.getElementById("root") 25 | 26 | if (rootElement.hasChildNodes()) { 27 | ReactDOM.hydrate(<App />, rootElement) 28 | } else { 29 | ReactDOM.render(<App />, rootElement) 30 | } -------------------------------------------------------------------------------- /src/ConnectionStatus.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components' 3 | 4 | const ConnectionStatusText = styled.p` 5 | font-family: 'Poppins'; 6 | font-size: 14px; 7 | text-transform: uppercase; 8 | vertical-align: center; 9 | margin-top: 30px; 10 | margin-bottom: 30px; 11 | ` 12 | 13 | const Dot = styled.span` 14 | height: 10px; 15 | width: 10px; 16 | background-color: #6ACC86; 17 | border-radius: 50%; 18 | display: inline-block; 19 | margin-right: 5px; 20 | ` 21 | 22 | const YellowDot = styled(Dot)` 23 | background-color: #E0E05B; 24 | ` 25 | 26 | export default function ConnectionStatus ({ channelName, reconnecting }) { 27 | return ( 28 | <ConnectionStatusText> 29 | { reconnecting ? ( 30 | <div> 31 | <YellowDot/> Reconnecting... 32 | </div> 33 | ) : ( 34 | <div> 35 | <Dot/> Connected to <b>{channelName}</b> 36 | </div> 37 | ) } 38 | </ConnectionStatusText> 39 | ) 40 | } -------------------------------------------------------------------------------- /src/Badge.jsx: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | import React from 'react' 3 | 4 | const badges = { 5 | patron: { 6 | icon: 'patron.svg', 7 | background: '#141518' 8 | }, 9 | developer: { 10 | icon: 'developer.svg', 11 | background: '#0000007F' 12 | }, 13 | booster: { 14 | icon: 'booster.svg', 15 | background: '#36393F' 16 | }, 17 | subscriber: { 18 | icon: 'subscriber.svg', 19 | background: '#9246FF' 20 | } 21 | } 22 | 23 | const Icon = styled.img` 24 | size: 14px; 25 | position: absolute; 26 | top: 50%; 27 | left: 50%; 28 | transform: translate(-50%, -50%); 29 | ` 30 | 31 | export default function Badge({ type }) { 32 | const Wrapper = styled.div` 33 | background-color: ${badges[type].background}; 34 | position: absolute; 35 | margin-left: -6px; 36 | margin-top: -9px; 37 | width: 24px; 38 | height: 24px; 39 | border-top-left-radius: 5px; 40 | border-bottom-right-radius: 5px; 41 | ` 42 | 43 | return <Wrapper> 44 | <Icon src={`badges/${badges[type].icon}`} alt={`${type} icon`}></Icon> 45 | </Wrapper> 46 | } -------------------------------------------------------------------------------- /src/CodeInput.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components' 3 | 4 | import { syncIdLength } from './config.json' 5 | 6 | const CodeInputWrapper = styled.input` 7 | text-transform: uppercase; 8 | font-family: 'Poppins'; 9 | font-size: 48px; 10 | height: 80px; 11 | width: 100%; 12 | border-radius: 5px; 13 | box-sizing: border-box; 14 | border: 2px solid #B3B3B3; 15 | text-align: center; 16 | margin-bottom: 10px; 17 | caret-color: transparent; 18 | user-select: initial; 19 | 20 | &::placeholder { 21 | color: #D9D9D9; 22 | } 23 | 24 | &:focus::placeholder { 25 | color: transparent; 26 | } 27 | ` 28 | 29 | export default function CodeInput ({ syncId, setSyncId }) { 30 | return ( 31 | <CodeInputWrapper 32 | key="codeinput" 33 | placeholder="ABC123" 34 | value={syncId} 35 | maxLength={syncIdLength} 36 | onInput={e => { 37 | if (e.target.value.length > syncIdLength) e.target.value = e.target.value.slice(0, syncIdLength) 38 | setSyncId(e.target.value.toUpperCase()) 39 | }} 40 | autoComplete="off" 41 | autoCorrect="off" 42 | autoCapitalize="off" 43 | spellCheck="false" 44 | /> 45 | ) 46 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "amongcord-sync", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@sentry/react": "^5.24.2", 7 | "@sentry/tracing": "^5.24.2", 8 | "@testing-library/jest-dom": "^4.2.4", 9 | "@testing-library/react": "^9.5.0", 10 | "@testing-library/user-event": "^7.2.1", 11 | "color": "^3.1.2", 12 | "nosleep.js": "^0.11.0", 13 | "react": "^16.13.1", 14 | "react-dom": "^16.13.1", 15 | "react-scripts": "3.4.3", 16 | "socket.io-client": "^2.3.0", 17 | "styled-components": "^5.2.0" 18 | }, 19 | "scripts": { 20 | "start": "react-scripts start", 21 | "build": "react-scripts build", 22 | "test": "react-scripts test", 23 | "postbuild": "react-snap", 24 | "eject": "react-scripts eject" 25 | }, 26 | "eslintConfig": { 27 | "extends": "react-app" 28 | }, 29 | "resolutions": { 30 | "styled-components": "^5" 31 | }, 32 | "browserslist": { 33 | "production": [ 34 | ">0.2%", 35 | "not dead", 36 | "not op_mini all" 37 | ], 38 | "development": [ 39 | "last 1 chrome version", 40 | "last 1 firefox version", 41 | "last 1 safari version" 42 | ] 43 | }, 44 | "devDependencies": { 45 | "react-snap": "^1.23.0" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /public/badges/developer.svg: -------------------------------------------------------------------------------- 1 | <svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 2 | <g clip-path="url(#clip0)"> 3 | <path d="M13.9597 3.40281C13.949 3.27819 13.8669 3.17113 13.7496 3.12844C13.6321 3.08549 13.5005 3.11473 13.412 3.20302L11.6025 5.01247L9.5964 4.37729L8.96123 2.37126L10.7708 0.561671C10.8594 0.473199 10.8883 0.341571 10.8456 0.224211C10.8025 0.106618 10.6957 0.0247114 10.5708 0.013915C9.47184 -0.0807723 8.39503 0.311604 7.61649 1.09026C6.55021 2.15662 6.26785 3.71381 6.76786 5.04112C6.71321 5.08717 6.65929 5.13596 6.60609 5.18857L0.565684 10.8884C0.563554 10.8904 0.561453 10.8927 0.559148 10.8948C-0.151226 11.6052 -0.151226 12.761 0.559148 13.4716C1.26964 14.1819 2.41706 14.1733 3.12732 13.4629C3.13038 13.46 3.13318 13.4571 3.13599 13.454L8.78606 7.36652C8.83756 7.31479 8.88539 7.26066 8.93047 7.2051C10.2581 7.70602 11.8163 7.42433 12.8835 6.35726C13.6619 5.57855 14.0545 4.50165 13.9597 3.40281ZM2.21707 12.7041C1.9553 12.9659 1.53135 12.9657 1.26961 12.7041C1.00796 12.4427 1.00796 12.0184 1.26961 11.7568C1.53135 11.4953 1.9553 11.4953 2.21707 11.7568C2.47872 12.0184 2.47872 12.4427 2.21707 12.7041Z" fill="white"/> 4 | </g> 5 | <defs> 6 | <clipPath id="clip0"> 7 | <path d="M0 5C0 2.23858 2.23858 0 5 0H14V14H0V5Z" fill="white"/> 8 | </clipPath> 9 | </defs> 10 | </svg> 11 | -------------------------------------------------------------------------------- /src/PlayerButton.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components' 3 | import Badge from './Badge' 4 | import PlayerIcon from './PlayerIcon' 5 | 6 | const backgroundColors = { 7 | black: '#72808B', 8 | cyan: '#68B4A7', 9 | green: '#4E885D', 10 | orange: '#E2A45B', 11 | pink: '#F59CD8', 12 | purple: '#A171DF', 13 | yellow: '#E0E05B', 14 | lime: '#90DB84', 15 | red: '#DD6867', 16 | white: '#B3BED2', 17 | brown: '#A07E58', 18 | blue: '#576AD4' 19 | } 20 | 21 | const PlayerButtonWrapper = styled.button` 22 | font-family: 'Poppins'; 23 | font-weight: bold; 24 | color: white; 25 | border: none; 26 | outline: white; 27 | border-radius: 5px; 28 | margin-bottom: 10px; 29 | height: 80px; 30 | width: calc(50% - 5px); 31 | ` 32 | 33 | export default function PlayerButton ({ player, loading, sendAliveUpdate }) { 34 | const ColoredPlayerButton = styled(PlayerButtonWrapper)` 35 | background-color: ${backgroundColors[player.color]}; 36 | filter: ${player.alive ? '' : 'brightness(0.6)'}; 37 | ` 38 | 39 | return ( 40 | <ColoredPlayerButton disabled={loading} onClick={() => { sendAliveUpdate(player.color, !player.alive) }}> 41 | { player.badge ? <Badge type={player.badge}/> : null } 42 | <PlayerIcon player={player} /> 43 | <div>{player.name}</div> 44 | </ColoredPlayerButton> 45 | ) 46 | } -------------------------------------------------------------------------------- /src/GamePage.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components' 3 | 4 | import PlayerButton from './PlayerButton'; 5 | import ConnectionStatus from './ConnectionStatus' 6 | import Button from './Button' 7 | import Container from './Container'; 8 | import JoinTipCard from './JoinTipCard'; 9 | 10 | const TopIcon = styled.img` 11 | height: 55px; 12 | margin-top: 25px; 13 | ` 14 | 15 | const FullButton = styled(Button)` 16 | width: 100%; 17 | ` 18 | 19 | const HalfButton = styled(Button)` 20 | width: calc(50% - 5px); 21 | ` 22 | 23 | const FlexContainer = styled.div` 24 | display: flex; 25 | justify-content: space-between; 26 | ` 27 | 28 | const PlayerContainer = styled(FlexContainer)` 29 | flex-wrap: wrap; 30 | ` 31 | 32 | const TopBar = styled.div` 33 | align-items: center; 34 | ` 35 | 36 | function GamePage ({ gameState, sendStageUpdate, sendAliveUpdate, loading, reconnecting }) { 37 | return ( 38 | <Container> 39 | <TopBar> 40 | <TopIcon src='icon.png' alt='Amongcord Icon'/> 41 | </TopBar> 42 | <ConnectionStatus reconnecting={reconnecting} channelName={gameState.channel_name} /> 43 | <FullButton disabled={gameState.game_stage === 'lobby' || loading} onClick={() => { sendStageUpdate('lobby') }}>Lobby</FullButton><br/> 44 | <FlexContainer> 45 | <HalfButton disabled={gameState.game_stage === 'tasks' || loading} onClick={() => { sendStageUpdate('tasks') }}>Tasks</HalfButton> 46 | <HalfButton disabled={gameState.game_stage === 'discussion' || loading} onClick={() => { sendStageUpdate('discussion') }}>Discussion</HalfButton> 47 | </FlexContainer> 48 | <section> 49 | <h2>Players</h2> 50 | { gameState.players.length > 0 ? ( 51 | <PlayerContainer> 52 | {gameState.players.map(p => ( 53 | <PlayerButton player={p} loading={loading} sendAliveUpdate={sendAliveUpdate} /> 54 | ))} 55 | </PlayerContainer> 56 | ) : ( 57 | <JoinTipCard /> 58 | ) } 59 | </section> 60 | </Container> 61 | ); 62 | } 63 | 64 | export default GamePage; 65 | -------------------------------------------------------------------------------- /src/App.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | 3 | import GamePage from './GamePage' 4 | import CodePage from './CodePage' 5 | 6 | import openSocket from 'socket.io-client' 7 | 8 | import NoSleep from 'nosleep.js'; 9 | const noSleep = new NoSleep() 10 | noSleep.enable() 11 | 12 | function App () { 13 | const idFromPath = window.location.pathname.split('/').join('') 14 | const [ syncId, setSyncId ] = useState(idFromPath) 15 | 16 | const [connected, setConnected] = useState(false) 17 | 18 | const [reconnecting, setReconnecting] = useState(false) 19 | 20 | const [socket, setSocket] = useState() 21 | 22 | const [loading, setLoading] = useState(false) 23 | 24 | const [gameState, setGameState] = useState({ 25 | sync_id: '', 26 | channel_name: '', 27 | game_stage: 'lobby', 28 | players: [] 29 | }) 30 | 31 | function sendStageUpdate (stage) { 32 | setLoading(true) 33 | socket.emit('setStage', JSON.stringify({ 34 | sync_id: gameState.sync_id, 35 | game_stage: stage 36 | })) 37 | } 38 | 39 | function sendAliveUpdate (color, alive) { 40 | setLoading(true) 41 | socket.emit('setAlive', JSON.stringify({ 42 | sync_id: gameState.sync_id, 43 | color, alive 44 | })) 45 | } 46 | 47 | function connectToGame () { 48 | const newSocket = openSocket(process.env.REACT_APP_SYNC_SERVER_ADDRESS || 'localhost:8081') 49 | 50 | setLoading(true) 51 | 52 | newSocket.on('connect', () => { 53 | newSocket.emit('join', syncId) 54 | }) 55 | 56 | newSocket.on('gameStateUpdate', state => { 57 | setLoading(false) 58 | setConnected(true) 59 | setGameState(state) 60 | }) 61 | 62 | newSocket.on('gameEnded', () => { 63 | setSyncId('') 64 | setConnected(false) 65 | }) 66 | 67 | newSocket.on('connect_error', () => { setConnected(false) }) 68 | newSocket.on('connect_timeout', () => { setConnected(false) }) 69 | newSocket.on('disconnect', () => { 70 | setConnected(false) 71 | }) 72 | newSocket.on('reconnecting', () => { setReconnecting(true) }) 73 | newSocket.on('reconnect', () => { setReconnecting(false) }) 74 | newSocket.on('reconnect_failed', () => { 75 | setConnected(false) 76 | setReconnecting(false) 77 | }) 78 | 79 | setSocket(newSocket) 80 | } 81 | 82 | return connected ? <GamePage 83 | gameState={gameState} 84 | reconnecting={reconnecting} 85 | sendAliveUpdate={sendAliveUpdate} 86 | sendStageUpdate={sendStageUpdate} 87 | loading={loading} 88 | /> : <CodePage 89 | syncId={syncId} 90 | setSyncId={setSyncId} 91 | connectToGame={connectToGame} 92 | loading={loading} 93 | /> 94 | } 95 | 96 | export default App; 97 | -------------------------------------------------------------------------------- /public/logo.svg: -------------------------------------------------------------------------------- 1 | <svg width="267" height="35" viewBox="0 0 267 35" fill="none" xmlns="http://www.w3.org/2000/svg"> 2 | <path d="M12.056 24.128H11.768C11.216 24.8 10.544 25.376 9.75197 25.856C8.98397 26.336 7.98797 26.576 6.76397 26.576C5.85197 26.576 4.98797 26.42 4.17197 26.108C3.37997 25.82 2.68397 25.412 2.08397 24.884C1.48397 24.356 1.01597 23.72 0.679969 22.976C0.343969 22.232 0.175969 21.416 0.175969 20.528C0.175969 19.616 0.355969 18.8 0.715969 18.08C1.07597 17.336 1.57997 16.7 2.22797 16.172C2.87597 15.644 3.63197 15.248 4.49597 14.984C5.38397 14.696 6.34397 14.552 7.37597 14.552C8.52797 14.552 9.47597 14.648 10.22 14.84C10.964 15.008 11.576 15.188 12.056 15.38V14.84C12.056 13.904 11.684 13.136 10.94 12.536C10.196 11.912 9.27197 11.6 8.16797 11.6C6.53597 11.6 5.15597 12.284 4.02797 13.652L0.607969 11.312C2.47997 8.96 5.03597 7.784 8.27597 7.784C11.012 7.784 13.1 8.432 14.54 9.728C15.98 11 16.7 12.896 16.7 15.416V26H12.056V24.128ZM12.056 18.836C11.504 18.572 10.928 18.368 10.328 18.224C9.75197 18.056 9.11597 17.972 8.41997 17.972C7.29197 17.972 6.41597 18.224 5.79197 18.728C5.19197 19.208 4.89197 19.808 4.89197 20.528C4.89197 21.248 5.17997 21.824 5.75597 22.256C6.35597 22.688 7.03997 22.904 7.80797 22.904C8.43197 22.904 9.00797 22.796 9.53597 22.58C10.064 22.34 10.508 22.04 10.868 21.68C11.252 21.296 11.54 20.864 11.732 20.384C11.948 19.88 12.056 19.364 12.056 18.836ZM20.4496 8.36H24.8776V10.664H25.1656C25.7176 9.8 26.4616 9.104 27.3976 8.576C28.3576 8.048 29.4136 7.784 30.5656 7.784C31.9096 7.784 33.0376 8.096 33.9496 8.72C34.8616 9.344 35.5096 10.112 35.8936 11.024C36.4456 10.136 37.2256 9.38 38.2336 8.756C39.2416 8.108 40.4656 7.784 41.9056 7.784C42.9856 7.784 43.9216 7.964 44.7136 8.324C45.5056 8.66 46.1536 9.14 46.6576 9.764C47.1856 10.388 47.5816 11.144 47.8456 12.032C48.1096 12.896 48.2416 13.844 48.2416 14.876V26H43.5256V15.74C43.5256 13.34 42.5416 12.14 40.5736 12.14C39.9496 12.14 39.3976 12.284 38.9176 12.572C38.4376 12.836 38.0296 13.208 37.6936 13.688C37.3816 14.144 37.1296 14.696 36.9376 15.344C36.7696 15.992 36.6856 16.688 36.6856 17.432V26H31.9696V15.74C31.9696 13.34 30.9376 12.14 28.8736 12.14C28.2736 12.14 27.7456 12.284 27.2896 12.572C26.8336 12.836 26.4496 13.208 26.1376 13.688C25.8256 14.144 25.5856 14.696 25.4176 15.344C25.2496 15.992 25.1656 16.688 25.1656 17.432V26H20.4496V8.36ZM60.4774 7.784C61.8454 7.784 63.0934 8.024 64.2214 8.504C65.3734 8.96 66.3574 9.608 67.1734 10.448C68.0134 11.264 68.6614 12.248 69.1174 13.4C69.5974 14.552 69.8374 15.812 69.8374 17.18C69.8374 18.548 69.5974 19.808 69.1174 20.96C68.6614 22.112 68.0134 23.108 67.1734 23.948C66.3574 24.764 65.3734 25.412 64.2214 25.892C63.0934 26.348 61.8454 26.576 60.4774 26.576C59.1094 26.576 57.8494 26.348 56.6974 25.892C55.5694 25.412 54.5854 24.764 53.7454 23.948C52.9294 23.108 52.2814 22.112 51.8014 20.96C51.3454 19.808 51.1174 18.548 51.1174 17.18C51.1174 15.812 51.3454 14.552 51.8014 13.4C52.2814 12.248 52.9294 11.264 53.7454 10.448C54.5854 9.608 55.5694 8.96 56.6974 8.504C57.8494 8.024 59.1094 7.784 60.4774 7.784ZM60.4774 22.22C61.0774 22.22 61.6534 22.112 62.2054 21.896C62.7814 21.656 63.2854 21.32 63.7174 20.888C64.1494 20.456 64.4854 19.928 64.7254 19.304C64.9894 18.68 65.1214 17.972 65.1214 17.18C65.1214 16.388 64.9894 15.68 64.7254 15.056C64.4854 14.432 64.1494 13.904 63.7174 13.472C63.2854 13.04 62.7814 12.716 62.2054 12.5C61.6534 12.26 61.0774 12.14 60.4774 12.14C59.8534 12.14 59.2654 12.26 58.7134 12.5C58.1614 12.716 57.6694 13.04 57.2374 13.472C56.8054 13.904 56.4574 14.432 56.1934 15.056C55.9534 15.68 55.8334 16.388 55.8334 17.18C55.8334 17.972 55.9534 18.68 56.1934 19.304C56.4574 19.928 56.8054 20.456 57.2374 20.888C57.6694 21.32 58.1614 21.656 58.7134 21.896C59.2654 22.112 59.8534 22.22 60.4774 22.22ZM76.944 10.664H77.232C77.784 9.776 78.528 9.08 79.464 8.576C80.4 8.048 81.468 7.784 82.668 7.784C83.772 7.784 84.744 7.964 85.584 8.324C86.424 8.684 87.108 9.2 87.636 9.872C88.188 10.52 88.596 11.312 88.86 12.248C89.148 13.16 89.292 14.18 89.292 15.308V26H84.576V15.884C84.576 14.612 84.288 13.676 83.712 13.076C83.16 12.452 82.344 12.14 81.264 12.14C80.616 12.14 80.04 12.284 79.536 12.572C79.056 12.836 78.636 13.208 78.276 13.688C77.94 14.144 77.676 14.696 77.484 15.344C77.316 15.968 77.232 16.64 77.232 17.36V26H72.516V8.36H76.944V10.664ZM101.148 34.352C99.9001 34.352 98.7841 34.184 97.8001 33.848C96.8401 33.536 96.0001 33.116 95.2801 32.588C94.5601 32.084 93.9601 31.52 93.4801 30.896C93.0001 30.272 92.6521 29.648 92.4361 29.024L96.9361 27.224C97.2721 28.16 97.8241 28.88 98.5921 29.384C99.3601 29.912 100.212 30.176 101.148 30.176C102.636 30.176 103.812 29.708 104.676 28.772C105.54 27.86 105.972 26.576 105.972 24.92V23.696H105.684C105.132 24.464 104.4 25.076 103.488 25.532C102.6 25.964 101.544 26.18 100.32 26.18C99.2641 26.18 98.2321 25.964 97.2241 25.532C96.2401 25.1 95.3641 24.488 94.5961 23.696C93.8281 22.88 93.2041 21.908 92.7241 20.78C92.2441 19.652 92.0041 18.392 92.0041 17C92.0041 15.608 92.2441 14.348 92.7241 13.22C93.2041 12.068 93.8281 11.096 94.5961 10.304C95.3641 9.488 96.2401 8.864 97.2241 8.432C98.2321 8 99.2641 7.784 100.32 7.784C101.544 7.784 102.6 8.012 103.488 8.468C104.4 8.9 105.132 9.5 105.684 10.268H105.972V8.36H110.472V24.74C110.472 26.228 110.244 27.56 109.788 28.736C109.332 29.936 108.696 30.944 107.88 31.76C107.064 32.6 106.08 33.236 104.928 33.668C103.8 34.124 102.54 34.352 101.148 34.352ZM101.328 21.932C101.904 21.932 102.468 21.824 103.02 21.608C103.596 21.392 104.1 21.08 104.532 20.672C104.964 20.24 105.312 19.724 105.576 19.124C105.84 18.5 105.972 17.792 105.972 17C105.972 16.208 105.84 15.5 105.576 14.876C105.312 14.252 104.964 13.736 104.532 13.328C104.1 12.896 103.596 12.572 103.02 12.356C102.468 12.14 101.904 12.032 101.328 12.032C100.752 12.032 100.188 12.14 99.6361 12.356C99.0841 12.572 98.5921 12.896 98.1601 13.328C97.7281 13.76 97.3801 14.288 97.1161 14.912C96.8521 15.512 96.7201 16.208 96.7201 17C96.7201 17.792 96.8521 18.5 97.1161 19.124C97.3801 19.724 97.7281 20.24 98.1601 20.672C98.5921 21.08 99.0841 21.392 99.6361 21.608C100.188 21.824 100.752 21.932 101.328 21.932ZM131.057 21.5C130.289 22.988 129.209 24.212 127.817 25.172C126.449 26.108 124.733 26.576 122.669 26.576C121.301 26.576 120.041 26.348 118.889 25.892C117.761 25.412 116.777 24.752 115.937 23.912C115.121 23.072 114.473 22.088 113.993 20.96C113.537 19.808 113.309 18.548 113.309 17.18C113.309 15.812 113.537 14.552 113.993 13.4C114.473 12.248 115.121 11.264 115.937 10.448C116.777 9.608 117.761 8.96 118.889 8.504C120.041 8.024 121.301 7.784 122.669 7.784C124.709 7.784 126.425 8.252 127.817 9.188C129.209 10.124 130.229 11.336 130.877 12.824L126.557 14.624C126.149 13.808 125.621 13.196 124.973 12.788C124.349 12.356 123.557 12.14 122.597 12.14C121.973 12.14 121.385 12.26 120.833 12.5C120.281 12.74 119.789 13.076 119.357 13.508C118.949 13.94 118.625 14.468 118.385 15.092C118.145 15.716 118.025 16.412 118.025 17.18C118.025 17.948 118.145 18.644 118.385 19.268C118.625 19.892 118.949 20.42 119.357 20.852C119.789 21.284 120.281 21.62 120.833 21.86C121.385 22.1 121.973 22.22 122.597 22.22C123.581 22.22 124.409 22.004 125.081 21.572C125.777 21.116 126.329 20.468 126.737 19.628L131.057 21.5ZM142.11 7.784C143.478 7.784 144.726 8.024 145.854 8.504C147.006 8.96 147.99 9.608 148.806 10.448C149.646 11.264 150.294 12.248 150.75 13.4C151.23 14.552 151.47 15.812 151.47 17.18C151.47 18.548 151.23 19.808 150.75 20.96C150.294 22.112 149.646 23.108 148.806 23.948C147.99 24.764 147.006 25.412 145.854 25.892C144.726 26.348 143.478 26.576 142.11 26.576C140.742 26.576 139.482 26.348 138.33 25.892C137.202 25.412 136.218 24.764 135.378 23.948C134.562 23.108 133.914 22.112 133.434 20.96C132.978 19.808 132.75 18.548 132.75 17.18C132.75 15.812 132.978 14.552 133.434 13.4C133.914 12.248 134.562 11.264 135.378 10.448C136.218 9.608 137.202 8.96 138.33 8.504C139.482 8.024 140.742 7.784 142.11 7.784ZM142.11 22.22C142.71 22.22 143.286 22.112 143.838 21.896C144.414 21.656 144.918 21.32 145.35 20.888C145.782 20.456 146.118 19.928 146.358 19.304C146.622 18.68 146.754 17.972 146.754 17.18C146.754 16.388 146.622 15.68 146.358 15.056C146.118 14.432 145.782 13.904 145.35 13.472C144.918 13.04 144.414 12.716 143.838 12.5C143.286 12.26 142.71 12.14 142.11 12.14C141.486 12.14 140.898 12.26 140.346 12.5C139.794 12.716 139.302 13.04 138.87 13.472C138.438 13.904 138.09 14.432 137.826 15.056C137.586 15.68 137.466 16.388 137.466 17.18C137.466 17.972 137.586 18.68 137.826 19.304C138.09 19.928 138.438 20.456 138.87 20.888C139.302 21.32 139.794 21.656 140.346 21.896C140.898 22.112 141.486 22.22 142.11 22.22ZM154.149 8.36H158.577V10.808H158.865C159.081 10.376 159.369 9.98 159.729 9.62C160.089 9.26 160.485 8.948 160.917 8.684C161.373 8.42 161.853 8.216 162.357 8.072C162.885 7.928 163.401 7.856 163.905 7.856C164.529 7.856 165.057 7.916 165.489 8.036C165.945 8.156 166.329 8.312 166.641 8.504L165.381 12.788C165.093 12.644 164.769 12.536 164.409 12.464C164.073 12.368 163.653 12.32 163.149 12.32C162.501 12.32 161.913 12.452 161.385 12.716C160.857 12.956 160.401 13.304 160.017 13.76C159.657 14.216 159.369 14.756 159.153 15.38C158.961 15.98 158.865 16.64 158.865 17.36V26H154.149V8.36ZM180.859 23.876H180.571C180.139 24.62 179.467 25.256 178.555 25.784C177.643 26.312 176.515 26.576 175.171 26.576C174.043 26.576 172.963 26.348 171.931 25.892C170.923 25.412 170.023 24.764 169.231 23.948C168.463 23.108 167.851 22.112 167.395 20.96C166.939 19.808 166.711 18.548 166.711 17.18C166.711 15.812 166.939 14.552 167.395 13.4C167.851 12.248 168.463 11.264 169.231 10.448C170.023 9.608 170.923 8.96 171.931 8.504C172.963 8.024 174.043 7.784 175.171 7.784C176.515 7.784 177.643 8.048 178.555 8.576C179.467 9.104 180.139 9.74 180.571 10.484H180.859L180.571 7.964V0.223999H185.287V26H180.859V23.876ZM176.143 22.22C176.767 22.22 177.355 22.112 177.907 21.896C178.483 21.656 178.987 21.32 179.419 20.888C179.851 20.456 180.199 19.928 180.463 19.304C180.727 18.68 180.859 17.972 180.859 17.18C180.859 16.388 180.727 15.68 180.463 15.056C180.199 14.432 179.851 13.904 179.419 13.472C178.987 13.04 178.483 12.716 177.907 12.5C177.355 12.26 176.767 12.14 176.143 12.14C175.519 12.14 174.919 12.26 174.343 12.5C173.791 12.74 173.299 13.076 172.867 13.508C172.435 13.94 172.087 14.468 171.823 15.092C171.559 15.716 171.427 16.412 171.427 17.18C171.427 17.948 171.559 18.644 171.823 19.268C172.087 19.892 172.435 20.42 172.867 20.852C173.299 21.284 173.791 21.62 174.343 21.86C174.919 22.1 175.519 22.22 176.143 22.22ZM210.715 21.104C210.715 22.64 210.043 23.936 208.699 24.992C207.355 26.048 205.663 26.576 203.623 26.576C201.847 26.576 200.287 26.12 198.943 25.208C197.599 24.272 196.639 23.048 196.063 21.536L199.015 20.276C199.447 21.332 200.071 22.16 200.887 22.76C201.727 23.336 202.639 23.624 203.623 23.624C204.679 23.624 205.555 23.396 206.251 22.94C206.971 22.484 207.331 21.944 207.331 21.32C207.331 20.192 206.467 19.364 204.739 18.836L201.715 18.08C198.283 17.216 196.567 15.56 196.567 13.112C196.567 11.504 197.215 10.22 198.511 9.26C199.831 8.276 201.511 7.784 203.551 7.784C205.111 7.784 206.515 8.156 207.763 8.9C209.035 9.644 209.923 10.64 210.427 11.888L207.475 13.112C207.139 12.368 206.587 11.792 205.819 11.384C205.075 10.952 204.235 10.736 203.299 10.736C202.435 10.736 201.655 10.952 200.959 11.384C200.287 11.816 199.951 12.344 199.951 12.968C199.951 13.976 200.899 14.696 202.795 15.128L205.459 15.812C208.963 16.676 210.715 18.44 210.715 21.104ZM229.366 8.36L218.314 33.776H214.894L218.998 24.884L211.726 8.36H215.326L220.582 21.032H220.654L225.766 8.36H229.366ZM231.528 8.36H234.696V10.808H234.84C235.344 9.944 236.112 9.224 237.144 8.648C238.2 8.072 239.292 7.784 240.42 7.784C242.58 7.784 244.236 8.408 245.388 9.656C246.564 10.88 247.152 12.632 247.152 14.912V26H243.84V15.128C243.768 12.248 242.316 10.808 239.484 10.808C238.164 10.808 237.06 11.348 236.172 12.428C235.284 13.484 234.84 14.756 234.84 16.244V26H231.528V8.36ZM259.103 26.576C256.487 26.576 254.315 25.688 252.587 23.912C250.883 22.088 250.031 19.844 250.031 17.18C250.031 14.468 250.883 12.224 252.587 10.448C254.315 8.672 256.487 7.784 259.103 7.784C260.903 7.784 262.475 8.24 263.819 9.152C265.163 10.04 266.171 11.276 266.843 12.86L263.819 14.12C262.883 11.912 261.239 10.808 258.887 10.808C257.375 10.808 256.067 11.42 254.963 12.644C253.883 13.868 253.343 15.38 253.343 17.18C253.343 18.98 253.883 20.492 254.963 21.716C256.067 22.94 257.375 23.552 258.887 23.552C261.311 23.552 263.015 22.448 263.999 20.24L266.951 21.5C266.303 23.084 265.283 24.332 263.891 25.244C262.523 26.132 260.927 26.576 259.103 26.576Z" fill="black"/> 3 | </svg> 4 | --------------------------------------------------------------------------------