├── .babelrc ├── .editorconfig ├── .eslintrc.js ├── .gitignore ├── .prettierrc ├── README.md ├── assets ├── README.md ├── mediasoup-demo-app.css ├── stylus │ ├── components │ │ ├── ChatInput.styl │ │ ├── Me.styl │ │ ├── NetworkThrottle.styl │ │ ├── Notifications.styl │ │ ├── Peer.styl │ │ ├── PeerView.styl │ │ ├── Peers.styl │ │ ├── Room.styl │ │ └── Stats.styl │ ├── fonts.styl │ ├── index.styl │ ├── mixins.styl │ ├── reset.styl │ └── variables.styl └── variables.scss ├── components ├── ChatInput.vue ├── Me.vue ├── NetworkThrottle.vue ├── Notifications.vue ├── Peer.vue ├── PeerView │ ├── PeerView.vue │ └── PeerViewProps.js ├── Peers.vue ├── PrintStats.vue ├── README.md ├── Room.vue └── Stats.vue ├── jest.config.js ├── jsconfig.json ├── layouts ├── README.md ├── default.vue └── error.vue ├── middleware └── README.md ├── nuxt.config.js ├── package.json ├── pages ├── README.md └── index.vue ├── plugins └── README.md ├── static ├── README.md ├── face-detector-models │ ├── tiny_face_detector_model-shard1 │ └── tiny_face_detector_model-weights_manifest.json ├── favicon.ico ├── fonts │ ├── Roboto-light-ext.woff2 │ ├── Roboto-light.ttf │ ├── Roboto-light.woff2 │ ├── Roboto-medium-ext.woff2 │ ├── Roboto-medium.ttf │ ├── Roboto-medium.woff2 │ ├── Roboto-regular-ext.woff2 │ ├── Roboto-regular.ttf │ └── Roboto-regular.woff2 ├── images │ ├── body-bg-1.jpg │ ├── body-bg-2.jpg │ ├── buddy.svg │ ├── devices │ │ ├── broadcaster.svg │ │ ├── chrome_16x16.png │ │ ├── edge_16x16.png │ │ ├── firefox_16x16.png │ │ ├── opera_16x16.png │ │ ├── safari_16x16.png │ │ └── unknown.svg │ ├── icon_change_webcam_black.svg │ ├── icon_change_webcam_white_unsupported.svg │ ├── icon_close_black.svg │ ├── icon_info_white_off.svg │ ├── icon_info_white_on.svg │ ├── icon_mic_black_on.svg │ ├── icon_mic_white_off.svg │ ├── icon_mic_white_unsupported.svg │ ├── icon_notification_error_white.svg │ ├── icon_notification_info_white.svg │ ├── icon_remote_mic_white_off.svg │ ├── icon_remote_webcam_white_off.svg │ ├── icon_restart_ice_white.svg │ ├── icon_share_black_on.svg │ ├── icon_share_white_on.svg │ ├── icon_share_white_unsupported.svg │ ├── icon_stats_white_on.svg │ ├── icon_video_black_off.svg │ ├── icon_video_elem_paused.svg │ ├── icon_video_white_on.svg │ ├── icon_volume_black_off.svg │ ├── icon_volume_white_on.svg │ ├── icon_webcam_black_on.svg │ ├── icon_webcam_black_unsupported.svg │ ├── icon_webcam_white_on.svg │ ├── icon_webcam_white_unsupported.svg │ ├── paint-stains-spots-bright-5K-wallpaper.jpg │ ├── room.svg │ └── stats.svg ├── js │ └── antiglobal.js ├── v.png ├── videos │ └── video-audio-stereo.mp4 └── vuetify-logo.svg ├── store ├── README.md ├── consumers.js ├── dataConsumers.js ├── dataProducers.js ├── index.js ├── me.js ├── notifications.js ├── peers.js ├── producers.js └── room.js ├── test └── Logo.spec.js ├── utils ├── RoomClient.js ├── cookiesManager.js ├── deviceInfo.js ├── randomName.js ├── randomString.js └── urlFactory.js └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "test": { 4 | "presets": [ 5 | [ 6 | "@babel/preset-env", 7 | { 8 | "targets": { 9 | "node": "current" 10 | } 11 | } 12 | ] 13 | ] 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | browser: true, 5 | node: true 6 | }, 7 | parserOptions: { 8 | parser: 'babel-eslint' 9 | }, 10 | extends: [ 11 | '@nuxtjs', 12 | 'prettier', 13 | 'prettier/vue', 14 | 'plugin:prettier/recommended', 15 | 'plugin:nuxt/recommended' 16 | ], 17 | plugins: [ 18 | 'prettier' 19 | ], 20 | // add your custom rules here 21 | rules: { 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Node template 3 | # Logs 4 | /logs 5 | *.log 6 | npm-debug.log* 7 | yarn-debug.log* 8 | yarn-error.log* 9 | 10 | # Runtime data 11 | pids 12 | *.pid 13 | *.seed 14 | *.pid.lock 15 | 16 | # Directory for instrumented libs generated by jscoverage/JSCover 17 | lib-cov 18 | 19 | # Coverage directory used by tools like istanbul 20 | coverage 21 | 22 | # nyc test coverage 23 | .nyc_output 24 | 25 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 26 | .grunt 27 | 28 | # Bower dependency directory (https://bower.io/) 29 | bower_components 30 | 31 | # node-waf configuration 32 | .lock-wscript 33 | 34 | # Compiled binary addons (https://nodejs.org/api/addons.html) 35 | build/Release 36 | 37 | # Dependency directories 38 | node_modules/ 39 | jspm_packages/ 40 | 41 | # TypeScript v1 declaration files 42 | typings/ 43 | 44 | # Optional npm cache directory 45 | .npm 46 | 47 | # Optional eslint cache 48 | .eslintcache 49 | 50 | # Optional REPL history 51 | .node_repl_history 52 | 53 | # Output of 'npm pack' 54 | *.tgz 55 | 56 | # Yarn Integrity file 57 | .yarn-integrity 58 | 59 | # dotenv environment variables file 60 | .env 61 | 62 | # parcel-bundler cache (https://parceljs.org/) 63 | .cache 64 | 65 | # next.js build output 66 | .next 67 | 68 | # nuxt.js build output 69 | .nuxt 70 | 71 | # Nuxt generate 72 | dist 73 | 74 | # vuepress build output 75 | .vuepress/dist 76 | 77 | # Serverless directories 78 | .serverless 79 | 80 | # IDE / Editor 81 | .idea 82 | 83 | # Service worker 84 | sw.* 85 | 86 | # macOS 87 | .DS_Store 88 | 89 | # Vim swap files 90 | *.swp 91 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "arrowParens": "always", 4 | "singleQuote": true 5 | } 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mediasoup-nuxt-demo 2 | 3 | > Life seemed very unfair with only mediasoup-react example out there. 4 | 5 | Mediasoup demo server not included 6 | 7 | ## Installation: 8 | 1. git clone https://github.com/versatica/mediasoup-demo 9 | 2. cd mediasoup-demo/server && yarn 10 | 3. Configure config.js (pay attention to tls section - if you plan to run it localhost don`t forget to create selfsigned ssl certs first) 11 | 4. node server.js 12 | 5. Create nginx vhost (e.g. https://rtc.loc) to reverse proxy to client app port (3000), don`t forget ssl certs there too 13 | 6. cd mediasoup-nuxt-demo && yarn 14 | 7. yarn dev 15 | 8. https://rtc.loc/?roomId=my_room_name 16 | 17 | After that demo app should work 18 | 19 | Bug reports are welcome, unless they are related to RTC, underlying mediasoup library or mediasoup-client. 20 | Also, just as original demo authors meant it to be, this is just a demo app, not production ready at all, you are always welcome to read [Mediasoup docs](https://mediasoup.org/documentation/) and create something of your own :) 21 | 22 | ## Disclaimer 23 | I definitely forgot to port some features and left some bugs, but besides **facial recognition** it seems to work ok 24 | 25 | For detailed explanation on how things work, check out [Nuxt.js docs](https://nuxtjs.org) and [Mediasoup docs](https://mediasoup.org/documentation/). 26 | -------------------------------------------------------------------------------- /assets/README.md: -------------------------------------------------------------------------------- 1 | # ASSETS 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your un-compiled assets such as LESS, SASS, or JavaScript. 6 | 7 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/assets#webpacked). 8 | -------------------------------------------------------------------------------- /assets/stylus/components/ChatInput.styl: -------------------------------------------------------------------------------- 1 | [data-component='ChatInput'] { 2 | position: relative; 3 | height: 100%; 4 | width: 100%; 5 | 6 | > textarea { 7 | height: 100%; 8 | width: 100%; 9 | padding: 4px 8px; 10 | resize: none; 11 | outline: none; 12 | background-color: rgba(#243B55, 1); 13 | color: #fff; 14 | font-family: inherit; 15 | font-size: 13px; 16 | font-weight: 400; 17 | line-height: 22px; 18 | border: none; 19 | 20 | +placeholder() { 21 | color: rgba(#fff, 0.35); 22 | } 23 | 24 | &:disabled { 25 | opacity: 0.65; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /assets/stylus/components/Me.styl: -------------------------------------------------------------------------------- 1 | [data-component='Me'] { 2 | position: relative; 3 | height: 100%; 4 | width: 100%; 5 | 6 | > .controls { 7 | position: absolute; 8 | z-index: 10; 9 | top: 0; 10 | left: 0; 11 | right: 0; 12 | display: flex; 13 | flex-direction: row; 14 | justify-content: flex-end; 15 | align-items: center; 16 | pointer-events: none; 17 | 18 | > .button { 19 | flex: 0 0 auto; 20 | margin: 4px; 21 | margin-left: 0; 22 | border-radius: 2px; 23 | pointer-events: auto; 24 | background-position: center; 25 | background-size: 75%; 26 | background-repeat: no-repeat; 27 | background-color: rgba(#000, 0.5); 28 | cursor: pointer; 29 | transition-property: opacity, background-color; 30 | transition-duration: 0.15s; 31 | 32 | +desktop() { 33 | width: 28px; 34 | height: 28px; 35 | opacity: 0.85; 36 | 37 | &:hover { 38 | opacity: 1; 39 | } 40 | } 41 | 42 | +mobile() { 43 | width: 26px; 44 | height: 26px; 45 | } 46 | 47 | &.unsupported { 48 | pointer-events: none; 49 | } 50 | 51 | &.disabled { 52 | pointer-events: none; 53 | opacity: 0.5; 54 | } 55 | 56 | &.on { 57 | background-color: rgba(#fff, 0.85); 58 | } 59 | 60 | &.mic { 61 | &.on { 62 | background-image: url('/resources/images/icon_mic_black_on.svg'); 63 | } 64 | 65 | &.off { 66 | background-image: url('/resources/images/icon_mic_white_off.svg'); 67 | background-color: rgba(#d42241, 0.7); 68 | } 69 | 70 | &.unsupported { 71 | background-image: url('/resources/images/icon_mic_white_unsupported.svg'); 72 | } 73 | } 74 | 75 | &.webcam { 76 | &.on { 77 | background-image: url('/resources/images/icon_webcam_black_on.svg'); 78 | } 79 | 80 | &.off { 81 | background-image: url('/resources/images/icon_webcam_white_on.svg'); 82 | } 83 | 84 | &.unsupported { 85 | background-image: url('/resources/images/icon_webcam_white_unsupported.svg'); 86 | } 87 | } 88 | 89 | &.change-webcam { 90 | &.on { 91 | background-image: url('/resources/images/icon_change_webcam_black.svg'); 92 | } 93 | 94 | &.unsupported { 95 | background-image: url('/resources/images/icon_change_webcam_white_unsupported.svg'); 96 | } 97 | } 98 | 99 | &.share { 100 | &.on { 101 | background-image: url('/resources/images/icon_share_black_on.svg'); 102 | } 103 | 104 | &.off { 105 | background-image: url('/resources/images/icon_share_white_on.svg'); 106 | } 107 | 108 | &.unsupported { 109 | background-image: url('/resources/images/icon_share_white_unsupported.svg'); 110 | } 111 | } 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /assets/stylus/components/NetworkThrottle.styl: -------------------------------------------------------------------------------- 1 | [data-component='NetworkThrottle'] { 2 | position: absolute; 3 | top: 0; 4 | left: 0; 5 | z-index: 99999; 6 | padding: 0 20px; 7 | background: rgba(#fff, 0.95); 8 | border-radius: 4px; 9 | box-shadow: 0px 5px 12px 2px rgba(#111, 0.5); 10 | font-family: 'Roboto'; 11 | 12 | > h1.draggable { 13 | padding: 20px; 14 | text-align: center; 15 | text-transform: uppercase; 16 | font-weight: 400; 17 | font-size: 12px; 18 | color: #111; 19 | user-select: none; 20 | cursor: move; 21 | } 22 | 23 | > .inputs { 24 | > .row { 25 | display: flex; 26 | justify-content: center; 27 | align-items: center; 28 | height: 20px; 29 | margin-bottom: 8px; 30 | 31 | &:last-child { 32 | margin-bottom: 0; 33 | } 34 | 35 | > .key { 36 | margin-right: 4px; 37 | flex: 0 0 auto; 38 | width: 100px; 39 | font-weight: 400; 40 | font-size: 11px; 41 | text-align: right; 42 | color: $COLOR_BG_1; 43 | } 44 | 45 | > .value { 46 | margin-left: 4px; 47 | flex: 0 0 auto; 48 | width: 100px; 49 | padding: 4px 8px; 50 | font-size: 11px; 51 | text-align: right; 52 | color: $COLOR_BG_2; 53 | border: none; 54 | background: rgba(#666, 0.1); 55 | border-radius: 2px; 56 | transition-property: opacity; 57 | transition-duration: 0.1s; 58 | 59 | &:disabled { 60 | opacity: 0.5; 61 | } 62 | 63 | &::placeholder { 64 | color: rgba($COLOR_BG_2, 0.25); 65 | } 66 | 67 | &:focus { 68 | &::placeholder { 69 | color: transparent; 70 | } 71 | } 72 | } 73 | } 74 | } 75 | 76 | > .buttons { 77 | display: flex; 78 | justify-content: center; 79 | align-items: center; 80 | padding: 20px; 81 | 82 | > button { 83 | margin: 0 4px; 84 | flex: 0 0 auto; 85 | width: 66px; 86 | height: 20px; 87 | display: flex; 88 | justify-content: center; 89 | align-items: center; 90 | border: none; 91 | border-radius: 2px; 92 | font-weight: 400; 93 | font-size: 11px; 94 | color: #fff; 95 | text-align: center; 96 | user-select: none; 97 | cursor: pointer; 98 | transition-property: opacity; 99 | transition-duration: 0.1s; 100 | 101 | &:disabled { 102 | opacity: 0.5; 103 | cursor: default; 104 | } 105 | 106 | &.apply { 107 | background: #e9736e; 108 | } 109 | 110 | &.reset { 111 | background: lighten($COLOR_BG_1, 15%); 112 | } 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /assets/stylus/components/Notifications.styl: -------------------------------------------------------------------------------- 1 | [data-component='Notifications'] { 2 | position: fixed; 3 | z-index: 9999; 4 | pointer-events: none; 5 | top: 0; 6 | right: 0; 7 | bottom: 0; 8 | padding: 20px; 9 | display: flex; 10 | flex-direction: column; 11 | justify-content: flex-end; 12 | align-items: flex-end; 13 | 14 | +desktop() { 15 | padding: 10px; 16 | width: 300px; 17 | } 18 | 19 | +mobile() { 20 | padding: 4px; 21 | width: 65%; 22 | max-width: 220px; 23 | } 24 | 25 | > .notification { 26 | pointer-events: auto; 27 | margin-top: 4px; 28 | border-radius: 4px; 29 | display: flex; 30 | flex-direction: row; 31 | justify-content: flex-start; 32 | align-items: center; 33 | 34 | +desktop() { 35 | min-width: 75%; 36 | } 37 | 38 | +mobile() { 39 | width: 100%; 40 | } 41 | 42 | &.Appear-appear { 43 | visibility: hidden; 44 | opacity: 0; 45 | transition: all 0.15s ease-in-out 0s, visibility 0s linear 0.25s; 46 | transform: translateX(200px); 47 | } 48 | 49 | &.Appear-appear.Appear-appear-active { 50 | visibility: visible; 51 | pointer-events: auto; 52 | opacity: 1; 53 | transform: translateY(0%); 54 | transition-delay: 0s, 0s; 55 | } 56 | 57 | +desktop() { 58 | padding: 16px 24px 16px 12px; 59 | } 60 | 61 | +mobile() { 62 | padding: 6px 16px 6px 12px; 63 | } 64 | 65 | > .icon { 66 | flex: 0 0 auto; 67 | height: 24px; 68 | width: 24px; 69 | margin-right: 12px; 70 | background-position: center; 71 | background-size: 100%; 72 | background-repeat: no-repeat; 73 | } 74 | 75 | > .body { 76 | > .title { 77 | font-weight: 500; 78 | user-select: none; 79 | cursor: default; 80 | line-height: 1.35em; 81 | margin-bottom: 10px; 82 | 83 | +desktop() { 84 | font-size: 14px; 85 | } 86 | 87 | +mobile() { 88 | font-size: 12px; 89 | } 90 | } 91 | 92 | > .text { 93 | user-select: none; 94 | cursor: default; 95 | line-height: 1.35em; 96 | 97 | +desktop() { 98 | font-size: 13px; 99 | } 100 | 101 | +mobile() { 102 | font-size: 12px; 103 | } 104 | } 105 | } 106 | 107 | &.info { 108 | background-color: $COLOR_BG_2; 109 | color: rgba(#fff, 0.75); 110 | 111 | >.icon { 112 | opacity: 0.65; 113 | background-image: url('/resources/images/icon_notification_info_white.svg'); 114 | } 115 | } 116 | 117 | &.error { 118 | background-color: rgba(#ff1914, 0.75); 119 | color: #fff; 120 | 121 | >.icon { 122 | opacity: 0.85; 123 | background-image: url('/resources/images/icon_notification_error_white.svg'); 124 | } 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /assets/stylus/components/Peer.styl: -------------------------------------------------------------------------------- 1 | [data-component='Peer'] { 2 | flex: 100 100 auto; 3 | position: relative; 4 | height: 100%; 5 | width: 100%; 6 | 7 | +mobile() { 8 | display: flex; 9 | flex-direction: column; 10 | justify-content: center; 11 | align-items: center; 12 | } 13 | 14 | > .indicators { 15 | position: absolute; 16 | z-index: 10; 17 | top: 0; 18 | right: 0; 19 | width: 150px; 20 | display: flex; 21 | flex-direction:; row; 22 | justify-content: flex-end; 23 | align-items: center; 24 | 25 | > .icon { 26 | flex: 0 0 auto; 27 | margin: 4px; 28 | margin-left: 0; 29 | width: 32px; 30 | height: 32px; 31 | background-position: center; 32 | background-size: 75%; 33 | background-repeat: no-repeat; 34 | transition-property: opacity; 35 | transition-duration: 0.15s; 36 | 37 | +desktop() { 38 | opacity: 0.85; 39 | } 40 | 41 | &.mic-off { 42 | background-image: url('/resources/images/icon_remote_mic_white_off.svg'); 43 | } 44 | 45 | &.webcam-off { 46 | background-image: url('/resources/images/icon_remote_webcam_white_off.svg'); 47 | } 48 | } 49 | } 50 | 51 | .incompatible-video { 52 | position: absolute; 53 | z-index: 2 54 | top: 0; 55 | bottom: 0; 56 | left: 0; 57 | right: 0; 58 | display: flex; 59 | flex-direction: column; 60 | justify-content: center; 61 | align-items: center; 62 | 63 | > p { 64 | padding: 6px 12px; 65 | border-radius: 6px; 66 | user-select: none; 67 | pointer-events: none; 68 | font-size: 15px; 69 | color: rgba(#fff, 0.55); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /assets/stylus/components/PeerView.styl: -------------------------------------------------------------------------------- 1 | [data-component='PeerView'] { 2 | position: relative; 3 | flex: 100 100 auto; 4 | height: 100%; 5 | width: 100%; 6 | display: flex; 7 | flex-direction: column; 8 | overflow: hidden; 9 | background-color: $COLOR_BG_1; 10 | background-image: url('/resources/images/buddy.svg'); 11 | background-position: bottom; 12 | background-size: auto 80%; 13 | background-repeat: no-repeat; 14 | 15 | > .info { 16 | $backgroundTint = #000; 17 | 18 | position: absolute; 19 | pointer-events: none; 20 | z-index: 5; 21 | top: 0; 22 | bottom: 0; 23 | left: 0; 24 | right: 0; 25 | display: flex; 26 | flex-direction: column; 27 | justify-content: space-between; 28 | background: linear-gradient(to bottom, 29 | rgba($backgroundTint, 0) 0%, 30 | rgba($backgroundTint, 0) 70%, 31 | rgba($backgroundTint, 0.1) 80%, 32 | rgba($backgroundTint, 0.5) 100%); 33 | 34 | > .icons { 35 | display: flex; 36 | flex-direction: row; 37 | 38 | > .icon { 39 | pointer-events: auto; 40 | margin: 4px; 41 | margin-right: 0; 42 | background-position: center; 43 | background-size: 100%; 44 | background-repeat: no-repeat; 45 | cursor: pointer; 46 | transition-property: opacity, background-color; 47 | transition-duration: 0.15s; 48 | 49 | +desktop() { 50 | width: 28px; 51 | height: 28px; 52 | opacity: 0.75; 53 | 54 | &:hover { 55 | opacity: 1; 56 | } 57 | } 58 | 59 | +mobile() { 60 | width: 26px; 61 | height: 26px; 62 | } 63 | 64 | &.info { 65 | &.on { 66 | background-image: url('/resources/images/icon_info_white_on.svg'); 67 | } 68 | 69 | &:not(.on) { 70 | background-image: url('/resources/images/icon_info_white_off.svg'); 71 | } 72 | } 73 | 74 | &.stats { 75 | background-image: url('/resources/images/icon_stats_white_on.svg'); 76 | } 77 | } 78 | } 79 | 80 | > .box { 81 | pointer-events: auto; 82 | position: absolute; 83 | left: 4px; 84 | right: 4px; 85 | bottom: 4px; 86 | padding: 4px 6px 50px 6px; 87 | border-radius: 2px; 88 | background-color: rgba(#000, 0.65); 89 | opacity: 1; 90 | overflow: auto; 91 | transform-origin: left top; 92 | transition: 0.1s; 93 | 94 | +desktop() { 95 | top: 36px; 96 | } 97 | 98 | +mobile() { 99 | top: 34px; 100 | } 101 | 102 | &:not(.visible) { 103 | z-index: -1; 104 | opacity: 0; 105 | transform: scale(0); 106 | } 107 | 108 | > h1 { 109 | pointer-events: none; 110 | margin-top: 5px; 111 | margin-bottom: 5px; 112 | color: #fff; 113 | font-size: 12px; 114 | font-weight: 400; 115 | } 116 | 117 | > p { 118 | pointer-events: none; 119 | margin-bottom: 2px; 120 | color: rgba(#fff, 0.75); 121 | font-size: 11px; 122 | 123 | &:last-child { 124 | margin-bottom: 0; 125 | } 126 | 127 | &.clickable, .clickable { 128 | pointer-events: auto; 129 | user-select: none; 130 | color: #44c5d6; 131 | 132 | &:hover { 133 | cursor: pointer; 134 | color: #73feff; 135 | text-decoration: underline; 136 | } 137 | } 138 | 139 | &.copiable, .copiable { 140 | pointer-events: auto; 141 | cursor: text; 142 | 143 | &:hover { 144 | color: #44c5d6; 145 | text-decoration: underline; 146 | } 147 | } 148 | 149 | &.indent { 150 | margin-left: 10px; 151 | } 152 | } 153 | } 154 | 155 | > .peer { 156 | flex: 0 0 auto; 157 | display: flex; 158 | flex-direction: column; 159 | justify-content: flex-end; 160 | 161 | +desktop() { 162 | &.is-me { 163 | padding: 10px; 164 | align-items: flex-start; 165 | } 166 | 167 | &:not(.is-me) { 168 | padding: 20px; 169 | align-items: flex-start; 170 | pointer-events: none; 171 | } 172 | } 173 | 174 | +mobile() { 175 | &.is-me { 176 | padding: 10px; 177 | align-items: flex-start; 178 | } 179 | 180 | &:not(.is-me) { 181 | padding: 10px; 182 | align-items: flex-end; 183 | } 184 | } 185 | 186 | > .display-name { 187 | font-size: 14px; 188 | font-weight: 400; 189 | color: rgba(#fff, 0.85); 190 | } 191 | 192 | > span.display-name { 193 | user-select: none; 194 | cursor: text; 195 | 196 | &:not(.editable) { 197 | cursor: default; 198 | } 199 | 200 | &.editable { 201 | pointer-events: auto; 202 | 203 | +desktop() { 204 | &:hover { 205 | background-color: rgba(#aeff00, 0.25); 206 | } 207 | } 208 | } 209 | 210 | &.loading { 211 | opacity: 0.5; 212 | } 213 | } 214 | 215 | > input.display-name { 216 | border: none; 217 | border-bottom: 1px solid #aeff00; 218 | background-color: transparent; 219 | } 220 | 221 | > .row { 222 | margin-top: 4px; 223 | display: flex; 224 | flex-direction: row; 225 | justify-content: flex-start; 226 | align-items: center; 227 | 228 | > .device-icon { 229 | height: 16px; 230 | width: 16px; 231 | margin-right: 5px; 232 | user-select: none; 233 | pointer-events: none; 234 | background-position: center; 235 | background-size: cover; 236 | background-repeat: no-repeat; 237 | background-image: url('/resources/images/devices/unknown.svg'); 238 | 239 | &.chrome { 240 | background-image: url('/resources/images/devices/chrome_16x16.png'); 241 | } 242 | 243 | &.firefox { 244 | background-image: url('/resources/images/devices/firefox_16x16.png'); 245 | } 246 | 247 | &.safari { 248 | background-image: url('/resources/images/devices/safari_16x16.png'); 249 | } 250 | 251 | &.msedge { 252 | background-image: url('/resources/images/devices/edge_16x16.png'); 253 | } 254 | 255 | &.opera { 256 | background-image: url('/resources/images/devices/opera_16x16.png'); 257 | } 258 | 259 | &.broadcaster { 260 | height: 18px; 261 | width: 18px; 262 | background-image: url('/resources/images/devices/broadcaster.svg'); 263 | } 264 | } 265 | 266 | > .device-version { 267 | user-select: none; 268 | pointer-events: none; 269 | font-size: 11px; 270 | color: rgba(#fff, 0.55); 271 | } 272 | } 273 | } 274 | } 275 | 276 | > video { 277 | flex: 100 100 auto; 278 | height: 100%; 279 | width: 100%; 280 | object-fit: cover; 281 | user-select: none; 282 | transition-property: opacity; 283 | transition-duration: 0.5s; 284 | background-color: rgba(#000, 0.75); 285 | 286 | &.is-me { 287 | transform: scaleX(-1); 288 | } 289 | 290 | &.hidden { 291 | opacity: 0; 292 | transition-duration: 0s; 293 | } 294 | 295 | &.network-error { 296 | filter: grayscale(100%) brightness(135%) blur(5px); 297 | } 298 | } 299 | 300 | > audio { 301 | display: none; 302 | 303 | position: absolute; 304 | top: 0; 305 | bottom: 0; 306 | // left: 0; 307 | right: 0; 308 | z-index: 1; 309 | border: 2px solid red; 310 | } 311 | 312 | > canvas.face-detection { 313 | position: absolute; 314 | top: 0; 315 | bottom: 0; 316 | left: 0; 317 | right: 0; 318 | z-index: 1; 319 | pointer-events: none; 320 | 321 | &.is-me { 322 | transform: scaleX(-1); 323 | } 324 | } 325 | 326 | > .volume-container { 327 | position: absolute; 328 | top: 0; 329 | bottom: 0; 330 | right: 2px; 331 | width: 10px; 332 | display: flex; 333 | flex-direction: column; 334 | justify-content: center; 335 | align-items: center; 336 | pointer-events: none; 337 | 338 | > .bar { 339 | width: 6px; 340 | border-radius: 6px; 341 | background: rgba(yellow, 0.65); 342 | transition-property: height background-color; 343 | transition-duration: 0.25s; 344 | 345 | &.level0 { height: 0; background-color: rgba(yellow, 0.65); } 346 | &.level1 { height: 10%; background-color: rgba(yellow, 0.65); } 347 | &.level2 { height: 20%; background-color: rgba(yellow, 0.65); } 348 | &.level3 { height: 30%; background-color: rgba(yellow, 0.65); } 349 | &.level4 { height: 40%; background-color: rgba(orange, 0.65); } 350 | &.level5 { height: 50%; background-color: rgba(orange, 0.65); } 351 | &.level6 { height: 60%; background-color: rgba(red, 0.65); } 352 | &.level7 { height: 70%; background-color: rgba(red, 0.65); } 353 | &.level8 { height: 80%; background-color: rgba(#000, 0.65); } 354 | &.level9 { height: 90%; background-color: rgba(#000, 0.65); } 355 | &.level10 { height: 100%; background-color: rgba(#000, 0.65); } 356 | } 357 | } 358 | 359 | > .spinner-container { 360 | position: absolute; 361 | top: 0; 362 | bottom: 0; 363 | left: 0; 364 | right: 0; 365 | pointer-events: none; 366 | background-color: rgba(#000, 0.75); 367 | 368 | .react-spinner { 369 | position: relative; 370 | width: 48px; 371 | height: 48px; 372 | top: 50%; 373 | left: 50%; 374 | 375 | .react-spinner_bar { 376 | position: absolute; 377 | width: 20%; 378 | height: 7.8%; 379 | top: -3.9%; 380 | left: -10%; 381 | animation: PeerView-spinner 1.2s linear infinite; 382 | border-radius: 5px; 383 | background-color: rgba(#fff, 0.5); 384 | } 385 | } 386 | } 387 | 388 | > .video-elem-paused { 389 | position: absolute; 390 | top: 0; 391 | bottom: 0; 392 | left: 0; 393 | right: 0; 394 | pointer-events: none; 395 | background-position: center; 396 | background-image: url('/resources/images/icon_video_elem_paused.svg'); 397 | background-size: 35%; 398 | background-color: rgba(#000, 0.25); 399 | background-repeat: no-repeat; 400 | } 401 | } 402 | 403 | @keyframes PeerView-spinner { 404 | 0% { opacity: 1; } 405 | 100% { opacity: 0.15; } 406 | } 407 | -------------------------------------------------------------------------------- /assets/stylus/components/Peers.styl: -------------------------------------------------------------------------------- 1 | [data-component='Peers'] { 2 | min-height: 100%; 3 | width: 100%; 4 | 5 | +desktop() { 6 | width: 100%; 7 | padding: 40px 0 220px 0; 8 | display: flex; 9 | flex-direction: row; 10 | flex-wrap: wrap; 11 | justify-content: center; 12 | align-items: center; 13 | align-content: center; 14 | } 15 | 16 | +mobile() { 17 | min-height: 100vh; 18 | width: 100%; 19 | display: flex; 20 | flex-direction: column; 21 | justify-content: center; 22 | align-items: center; 23 | } 24 | 25 | > .peer-container { 26 | overflow: hidden; 27 | 28 | AppearFadeIn(1000ms); 29 | 30 | +desktop() { 31 | flex: 0 0 auto; 32 | height: 382px; 33 | width: 450px; 34 | margin: 6px; 35 | border: 1px solid rgba(#fff, 0.15); 36 | box-shadow: 0px 5px 12px 2px rgba(#111, 0.5); 37 | transition-property: border-color; 38 | transition-duration: 0.35s; 39 | 40 | &.active-speaker { 41 | border-color: #fff; 42 | } 43 | } 44 | 45 | +mobile() { 46 | flex: 100 100 auto; 47 | order: 2; 48 | min-height: 25vh; 49 | width: 100%; 50 | display: flex; 51 | flex-direction: column; 52 | justify-content: center; 53 | align-items: center; 54 | 55 | &.active-speaker { 56 | order: 1; 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /assets/stylus/components/Room.styl: -------------------------------------------------------------------------------- 1 | [data-component='Room'] { 2 | position: relative; 3 | height: 100%; 4 | width: 100%; 5 | 6 | AppearFadeIn(300ms); 7 | 8 | > .state { 9 | position: fixed; 10 | z-index: 100; 11 | display: flex; 12 | flex-direction: row; 13 | justify-content: center; 14 | align-items: center; 15 | border-radius: 25px; 16 | background-color: $COLOR_BG_1; 17 | 18 | +desktop() { 19 | top: 20px; 20 | left: 20px; 21 | width: 124px; 22 | } 23 | 24 | +mobile() { 25 | top: 10px; 26 | left: 10px; 27 | width: 110px; 28 | } 29 | 30 | > .icon { 31 | flex: 0 0 auto; 32 | border-radius: 100%; 33 | 34 | +desktop() { 35 | margin: 6px; 36 | margin-right: 0; 37 | height: 16px; 38 | width: 16px; 39 | } 40 | 41 | +mobile() { 42 | margin: 4px; 43 | margin-right: 0; 44 | height: 16px; 45 | width: 16px; 46 | } 47 | 48 | &.new, &.closed { 49 | background-color: rgba(#aaa, 0.5); 50 | } 51 | 52 | &.connecting { 53 | animation: Room-info-state-connecting .75s infinite linear; 54 | } 55 | 56 | &.connected { 57 | background-color: rgba(#30bd18, 0.75); 58 | 59 | +mobile() { 60 | display: none; 61 | } 62 | } 63 | } 64 | 65 | > .text { 66 | flex: 100 0 auto; 67 | user-select: none; 68 | pointer-events: none; 69 | text-align: center; 70 | text-transform: uppercase; 71 | font-family: 'Roboto'; 72 | font-weight: 400; 73 | color: rgba(#fff, 0.75); 74 | 75 | +desktop() { 76 | font-size: 12px; 77 | } 78 | 79 | +mobile() { 80 | font-size: 10px; 81 | } 82 | 83 | &.connected { 84 | +mobile() { 85 | display: none; 86 | } 87 | } 88 | } 89 | } 90 | 91 | > .room-link-wrapper { 92 | pointer-events: none; 93 | position: absolute; 94 | z-index: 1; 95 | top: 0; 96 | left: 0; 97 | right: 0; 98 | display: flex; 99 | flex-direction: row; 100 | justify-content: center; 101 | 102 | > .room-link { 103 | width: auto; 104 | background-color: $COLOR_BG_1; 105 | border-bottom-right-radius: 4px; 106 | border-bottom-left-radius: 4px; 107 | box-shadow: 0px 3px 12px 2px rgba(#111, 0.4); 108 | 109 | > a.link { 110 | display: block;; 111 | user-select: none; 112 | pointer-events: auto; 113 | text-transform: uppercase; 114 | font-family: 'Roboto'; 115 | font-weight: 400; 116 | color: rgba(#fff, 0.75); 117 | cursor: pointer; 118 | text-decoration: none; 119 | transition-property: color; 120 | transition-duration: 0.1s; 121 | 122 | +desktop() { 123 | padding: 10px 20px; 124 | font-size: 12px; 125 | } 126 | 127 | +mobile() { 128 | padding: 6px 10px; 129 | font-size: 10px; 130 | } 131 | 132 | &:hover { 133 | color: #fff; 134 | text-decoration: underline; 135 | } 136 | } 137 | } 138 | } 139 | 140 | > .me-container { 141 | position: fixed; 142 | z-index: 100; 143 | overflow: hidden; 144 | box-shadow: 0px 5px 12px 2px rgba(#111, 0.5); 145 | transition-property: border-color; 146 | transition-duration: 0.2s; 147 | 148 | &.active-speaker { 149 | border-color: #fff; 150 | } 151 | 152 | +desktop() { 153 | height: 250px; 154 | width: 294px; 155 | bottom: 60px; 156 | left: 20px; 157 | border: 1px solid rgba(#fff, 0.15); 158 | } 159 | 160 | +mobile() { 161 | height: 220px; 162 | width: 200px; 163 | bottom: 50px; 164 | left: 10px; 165 | border: 1px solid rgba(#fff, 0.25); 166 | } 167 | } 168 | 169 | > .chat-input-container { 170 | position: fixed; 171 | z-index: 100; 172 | overflow: hidden; 173 | box-shadow: 0px 5px 12px 2px rgba(#111, 0.5); 174 | 175 | +desktop() { 176 | height: 30px; 177 | width: 294px; 178 | bottom: 20px; 179 | left: 20px; 180 | border: 1px solid rgba(#fff, 0.15); 181 | } 182 | 183 | +mobile() { 184 | height: 30px; 185 | width: 200px; 186 | bottom: 10px; 187 | left: 10px; 188 | border: 1px solid rgba(#fff, 0.25); 189 | } 190 | } 191 | 192 | > .sidebar { 193 | position: fixed; 194 | z-index: 101; 195 | top: calc(50% - 60px); 196 | height: 120px; 197 | display: flex; 198 | flex-direction: column; 199 | justify-content: center; 200 | align-items: center; 201 | 202 | +desktop() { 203 | left: 20px; 204 | width: 36px; 205 | margin-top: -60px; 206 | } 207 | 208 | +mobile() { 209 | left: 10px; 210 | width: 32px; 211 | margin-top: -80px; 212 | } 213 | 214 | > .button { 215 | flex: 0 0 auto; 216 | margin: 4px 0; 217 | background-position: center; 218 | background-size: 75%; 219 | background-repeat: no-repeat; 220 | background-color: $COLOR_BG_1; 221 | cursor: pointer; 222 | transition-property: opacity, background-color; 223 | transition-duration: 0.2s; 224 | border-radius: 100%; 225 | 226 | +desktop() { 227 | height: 36px; 228 | width: 36px; 229 | } 230 | 231 | +mobile() { 232 | height: 32px; 233 | width: 32px; 234 | } 235 | 236 | &.on { 237 | background-color: rgba(#fff, 0.7); 238 | } 239 | 240 | &.disabled { 241 | pointer-events: none; 242 | opacity: 0.5; 243 | transition-duration: 0s; 244 | } 245 | 246 | &.hide-videos { 247 | background-image: url('/resources/images/icon_video_white_on.svg'); 248 | 249 | &.on { 250 | background-image: url('/resources/images/icon_video_black_off.svg'); 251 | } 252 | } 253 | 254 | &.mute-audio { 255 | background-image: url('/resources/images/icon_volume_white_on.svg'); 256 | 257 | &.on { 258 | background-image: url('/resources/images/icon_volume_black_off.svg'); 259 | } 260 | } 261 | 262 | &.restart-ice { 263 | background-image: url('/resources/images/icon_restart_ice_white.svg'); 264 | } 265 | } 266 | } 267 | } 268 | 269 | @keyframes Room-info-state-connecting { 270 | 50% { background-color: rgba(orange, 0.75); } 271 | } 272 | -------------------------------------------------------------------------------- /assets/stylus/components/Stats.styl: -------------------------------------------------------------------------------- 1 | [data-component='Stats'] { 2 | $desktopWidth = 470px; 3 | $headerInfoHeight = 80px; 4 | $headerListHeight = 150px; 5 | 6 | position: fixed; 7 | z-index: 1000; 8 | top: 0; 9 | bottom: 0; 10 | right: 0; 11 | overflow: hidden; 12 | pointer-events: none; 13 | 14 | +desktop() { 15 | width: $desktopWidth; 16 | padding-left: 20px; 17 | } 18 | 19 | +mobile() { 20 | left: 0; 21 | } 22 | 23 | > .content { 24 | position: relative; 25 | height: 100%; 26 | pointer-events: auto; 27 | background: rgba(#fff, 0.925); 28 | overscroll-behavior: contain; 29 | transition-property: transform opacity; 30 | transition-duration: 0.25s; 31 | 32 | +desktop() { 33 | width: $desktopWidth - 20px; 34 | box-shadow: -4px 0px 12px 2px rgba(#000, 0.5); 35 | } 36 | 37 | +mobile() { 38 | width: 100%; 39 | } 40 | 41 | &:not(.visible) { 42 | transform: translateX(100%); 43 | opacity: 0; 44 | } 45 | 46 | > .header { 47 | position: absolute; 48 | z-index: 2; 49 | top: 0; 50 | left: 0; 51 | right: 0; 52 | padding: 0 28px; 53 | background: linear-gradient( 54 | to bottom, 55 | #fff, 56 | #fff, 57 | rgba(#fff, 0.9), 58 | rgba(#fff, 0)); 59 | 60 | > .info { 61 | height: $headerInfoHeight; 62 | display: flex; 63 | flex-direction: row; 64 | justify-content: center; 65 | align-items: center; 66 | 67 | > .close-icon { 68 | flex: 0 0 auto; 69 | width: 28px; 70 | height: 28px; 71 | margin-left: -6px; 72 | margin-right: 20px; 73 | cursor: pointer; 74 | background-position: left; 75 | background-size: 100%; 76 | background-repeat: no-repeat; 77 | background-image: url('/resources/images/icon_close_black.svg'); 78 | opacity: 0.75; 79 | 80 | &:hover { 81 | opacity: 1; 82 | } 83 | } 84 | 85 | > h1 { 86 | flex: 100 100 auto; 87 | text-transform: uppercase; 88 | color: #444; 89 | font-size: 14px; 90 | font-weight: 500; 91 | text-align: right; 92 | 93 | textWithEllipsis(); 94 | } 95 | } 96 | 97 | > .list { 98 | height: $headerListHeight; 99 | display: flex; 100 | flex-direction: column; 101 | justify-content: flex-start; 102 | align-items: flex-end; 103 | 104 | > p { 105 | margin-bottom: 5px; 106 | color: $COLOR_BG_1; 107 | font-size: 14px; 108 | font-weight: 400; 109 | user-select: none; 110 | 111 | > a { 112 | text-decoration: none; 113 | text-transform: uppercase; 114 | font-size: 12px; 115 | font-weight: 500; 116 | color: lighten($COLOR_BG_1, 35%); 117 | cursor: pointer; 118 | 119 | &.disabled { 120 | opacity: 0.5; 121 | cursor: not-allowed; 122 | } 123 | } 124 | } 125 | } 126 | } 127 | 128 | > .stats { 129 | padding: 0 28px; 130 | height: 100%; 131 | overflow: auto; 132 | 133 | > .items { 134 | margin-bottom: 40px; 135 | 136 | AppearFadeIn(150ms); 137 | 138 | &:after { 139 | $margin = $headerInfoHeight + $headerListHeight; 140 | 141 | content: ''; 142 | display: block; 143 | visibility: hidden; 144 | margin-bottom: (-1 * $margin); 145 | } 146 | 147 | > h2 { 148 | margin-bottom: 20px; 149 | text-transform: uppercase; 150 | color: $COLOR_BG_2; 151 | font-size: 13px; 152 | font-weight: 500; 153 | text-align: center; 154 | 155 | textWithEllipsis(); 156 | 157 | &:before { 158 | $margin = $headerInfoHeight + $headerListHeight; 159 | 160 | content: ''; 161 | display: block; 162 | visibility: hidden; 163 | height: $margin; 164 | } 165 | } 166 | 167 | > .item { 168 | padding-bottom: 10px; 169 | margin-bottom: 20px; 170 | border-bottom: 1px solid #e5e5e5; 171 | 172 | > .line { 173 | display: flex; 174 | flex-direction: row; 175 | justify-content: space-between; 176 | align-items: center; 177 | margin-bottom: 8px; 178 | 179 | > .key { 180 | flex: 0 0 auto; 181 | width: 38.5%; 182 | color: $COLOR_BG_2; 183 | font-size: 11px; 184 | font-weight: 400; 185 | word-break: break-all; 186 | text-align: right; 187 | } 188 | 189 | > .value { 190 | flex: 0 0 auto; 191 | width: 58.5%; 192 | color: lighten($COLOR_BG_1, 35%); 193 | font-size: 11px; 194 | font-weight: 400; 195 | word-break: break-all; 196 | text-align: left; 197 | } 198 | } 199 | } 200 | } 201 | } 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /assets/stylus/fonts.styl: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Roboto'; 3 | font-style: normal; 4 | font-weight: 300; 5 | src: 6 | local('Roboto Light'), 7 | local('Roboto-Light'), url('/resources/fonts/Roboto-light-ext.woff2') format('woff2'); 8 | unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF; 9 | } 10 | @font-face { 11 | font-family: 'Roboto'; 12 | font-style: normal; 13 | font-weight: 300; 14 | src: 15 | local('Roboto Light'), 16 | local('Roboto-Light'), 17 | url('/resources/fonts/Roboto-light.woff2') format('woff2'), 18 | url('/resources/fonts/Roboto-light.ttf') format('ttf'); 19 | unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000; 20 | } 21 | @font-face { 22 | font-family: 'Roboto'; 23 | font-style: normal; 24 | font-weight: 400; 25 | src: 26 | local('Roboto'), 27 | local('Roboto-Regular'), 28 | url('/resources/fonts/Roboto-regular-ext.woff2') format('woff2'); 29 | unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF; 30 | } 31 | @font-face { 32 | font-family: 'Roboto'; 33 | font-style: normal; 34 | font-weight: 400; 35 | src: 36 | local('Roboto'), 37 | local('Roboto-Regular'), 38 | url('/resources/fonts/Roboto-regular.woff2') format('woff2'), 39 | url('/resources/fonts/Roboto-regular.ttf') format('ttf'); 40 | unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000; 41 | } 42 | @font-face { 43 | font-family: 'Roboto'; 44 | font-style: normal; 45 | font-weight: 500; 46 | src: 47 | local('Roboto Medium'), 48 | local('Roboto-Medium'), 49 | url('/resources/fonts/Roboto-medium-ext.woff2') format('woff2'); 50 | unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF; 51 | } 52 | @font-face { 53 | font-family: 'Roboto'; 54 | font-style: normal; 55 | font-weight: 500; 56 | src: 57 | local('Roboto Medium'), 58 | local('Roboto-Medium'), 59 | url('/resources/fonts/Roboto-medium.woff2') format('woff2'), 60 | url('/resources/fonts/Roboto-medium.ttf') format('ttf'); 61 | unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000; 62 | } 63 | -------------------------------------------------------------------------------- /assets/stylus/index.styl: -------------------------------------------------------------------------------- 1 | @import '~nib/index.styl'; 2 | 3 | global-reset(); 4 | 5 | @import './variables'; 6 | @import './mixins'; 7 | @import './fonts'; 8 | @import './reset'; 9 | 10 | html { 11 | height: 100%; 12 | box-sizing: border-box; 13 | // background-image: url('/resources/images/body-bg-2.jpg'); 14 | // background-attachment: fixed; 15 | // background-position: center; 16 | // background-size: cover; 17 | // background-repeat: no-repeat; 18 | background: #141E30; 19 | background: linear-gradient(to top, #243B55, #141E30); 20 | font-family: 'Roboto'; 21 | font-weight: 300; 22 | 23 | +desktop() { 24 | font-size: 16px; 25 | } 26 | 27 | +mobile() { 28 | font-size: 12px; 29 | } 30 | } 31 | 32 | body { 33 | height: 100%; 34 | } 35 | 36 | #mediasoup-demo-app-container { 37 | height: 100%; 38 | width: 100%; 39 | 40 | // Components 41 | @import './components/Room'; 42 | @import './components/Me'; 43 | @import './components/ChatInput'; 44 | @import './components/Peers'; 45 | @import './components/Peer'; 46 | @import './components/PeerView'; 47 | @import './components/Stats'; 48 | @import './components/Notifications'; 49 | @import './components/NetworkThrottle'; 50 | } 51 | 52 | // Hack to detect in JS the current media query 53 | #mediasoup-demo-app-media-query-detector { 54 | position: relative; 55 | z-index: -1000; 56 | bottom: 0; 57 | left: 0; 58 | height: 1px; 59 | width: 1px; 60 | 61 | // In desktop let it "visible" so elem.offsetParent returns the parent element 62 | +desktop() {} 63 | 64 | // In mobile ensure it's not displayed so elem.offsetParent returns null 65 | +mobile() { 66 | display: none; 67 | position: fixed; // Required for old IE 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /assets/stylus/mixins.styl: -------------------------------------------------------------------------------- 1 | placeholder() 2 | &::-webkit-input-placeholder 3 | {block} 4 | &:-moz-placeholder 5 | {block} 6 | &::-moz-placeholder 7 | {block} 8 | &:-ms-input-placeholder 9 | {block} 10 | 11 | text-fill-color() 12 | -webkit-text-fill-color: arguments; 13 | -moz-text-fill-color: arguments; 14 | text-fill-color: arguments; 15 | 16 | textWithEllipsis() { 17 | white-space: nowrap; 18 | text-overflow: ellipsis; 19 | overflow: hidden; 20 | } 21 | 22 | mobile() 23 | @media (max-device-width: 720px) 24 | {block} 25 | 26 | desktop() 27 | @media (min-device-width: 721px) 28 | {block} 29 | 30 | AppearFadeIn($duration = 1s, $enterOpacity = 0, $activeOpacity = 1) 31 | will-change: opacity; 32 | 33 | &.Appear-appear 34 | opacity: $enterOpacity; 35 | 36 | &.Appear-appear.Appear-appear-active 37 | transition-property: opacity; 38 | transition-duration: $duration; 39 | opacity: $activeOpacity; 40 | -------------------------------------------------------------------------------- /assets/stylus/reset.styl: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | outline: none; 4 | } 5 | 6 | body { 7 | background: none; 8 | line-height: 1; 9 | } 10 | 11 | input { 12 | padding: 0; 13 | font-family: inherit; 14 | background-color: transparent; 15 | } 16 | -------------------------------------------------------------------------------- /assets/stylus/variables.styl: -------------------------------------------------------------------------------- 1 | $COLOR_BG_1 = rgba(#243B55, 0.75); 2 | $COLOR_BG_2 = rgba(#141E30, 0.65); 3 | -------------------------------------------------------------------------------- /assets/variables.scss: -------------------------------------------------------------------------------- 1 | // Ref: https://github.com/nuxt-community/vuetify-module#customvariables 2 | // 3 | // The variables you want to modify 4 | // $font-size-root: 20px; 5 | -------------------------------------------------------------------------------- /components/ChatInput.vue: -------------------------------------------------------------------------------- 1 |