{(!hideOnlineIndicator && !user.isBot) &&
}
-
+
{user.isBot &&
}
{hideLink ? (
{userName}
diff --git a/services/app/apps/codebattle/assets/js/widgets/config/editor/haskell.js b/services/app/apps/codebattle/assets/js/widgets/config/editor/haskell.js
new file mode 100644
index 000000000..85ac94073
--- /dev/null
+++ b/services/app/apps/codebattle/assets/js/widgets/config/editor/haskell.js
@@ -0,0 +1,86 @@
+import tagKeywords from './tagKeywords';
+
+export const language = {
+ tagKeywords,
+ keywords: [
+ 'module',
+ 'import',
+ 'main',
+ 'where',
+ 'otherwise',
+ 'newtype',
+ 'definition',
+ 'implementation',
+ 'from',
+ 'class',
+ 'instance',
+ 'abort',
+ ],
+
+ builtintypes: ['Int', 'Real', 'String'],
+
+ operators: [
+ '=',
+ '==',
+ '>=',
+ '<=',
+ '+',
+ '-',
+ '*',
+ '/',
+ '::',
+ '->',
+ '=:',
+ '=>',
+ '|',
+ '$',
+ ],
+
+ numbers: /-?[0-9.]/,
+
+ tokenizer: {
+ root: [
+ { include: '@whitespace' },
+
+ [/->/, 'operators'],
+
+ [/\|/, 'operators'],
+
+ [/(\w*)(\s?)(::)/, ['keyword', 'white', 'operators']],
+
+ [/[+\-*/=<>$]/, 'operators'],
+
+ [
+ /[a-zA-Z_][a-zA-Z0-9_]*/,
+ {
+ cases: {
+ '@builtintypes': 'type',
+ '@keywords': 'keyword',
+ '@default': '',
+ },
+ },
+ ],
+
+ [/[()[\],:]/, 'delimiter'],
+
+ [/@numbers/, 'number'],
+
+ [/(")(.*)(")/, ['string', 'string', 'string']],
+ ],
+
+ comment: [
+ [/[^/*]+/, 'comment'],
+ [/\*\//, 'comment', '@pop'],
+ [/[/*]/, 'comment'],
+ ],
+
+ whitespace: [
+ [/[ \t\r\n]+/, 'white'],
+ [/\/\*/, 'comment', '@comment'],
+ [/\/\/.*$/, 'comment'],
+ [/--.*$/, 'comment'],
+ ],
+ },
+};
+
+export default language;
diff --git a/services/app/apps/codebattle/assets/js/widgets/config/editor/sass.js b/services/app/apps/codebattle/assets/js/widgets/config/editor/sass.js
new file mode 100644
index 000000000..b19f6adc2
--- /dev/null
+++ b/services/app/apps/codebattle/assets/js/widgets/config/editor/sass.js
@@ -0,0 +1,227 @@
+import tagKeywords from './tagKeywords';
+
+export const language = {
+ // Set defaultToken to invalid to see what you do not tokenize yet
+ // defaultToken: 'invalid',
+ tagKeywords,
+ defaultToken: '',
+ tokenPostfix: '.scss',
+ ws: '[ \t\n\r\f]*',
+ identifier:
+ '-?-?([a-zA-Z]|(\\\\(([0-9a-fA-F]{1,6}\\s?)|[^[0-9a-fA-F])))([\\w\\-]|(\\\\(([0-9a-fA-F]{1,6}\\s?)|[^[0-9a-fA-F])))*',
+ brackets: [
+ { open: '{', close: '}', token: 'delimiter.curly' },
+ { open: '[', close: ']', token: 'delimiter.bracket' },
+ { open: '(', close: ')', token: 'delimiter.parenthesis' },
+ { open: '<', close: '>', token: 'delimiter.angle' },
+ ],
+ tokenizer: {
+ root: [{ include: '@selector' }],
+ selector: [
+ { include: '@comments' },
+ { include: '@import' },
+ { include: '@variabledeclaration' },
+ { include: '@warndebug' },
+ ['[@](include)', { token: 'keyword', next: '@includedeclaration' }],
+ [
+ '[@](keyframes|-webkit-keyframes|-moz-keyframes|-o-keyframes)',
+ { token: 'keyword', next: '@keyframedeclaration' },
+ ],
+ ['[@](page|content|font-face|-moz-document)', { token: 'keyword' }],
+ [
+ '[@](charset|namespace)',
+ { token: 'keyword', next: '@declarationbody' },
+ ],
+ ['[@](function)', { token: 'keyword', next: '@functiondeclaration' }],
+ ['[@](mixin)', { token: 'keyword', next: '@mixindeclaration' }],
+ ['url(\\-prefix)?\\(', { token: 'meta', next: '@urldeclaration' }],
+ { include: '@controlstatement' },
+ { include: '@selectorname' },
+ ['[&\\*]', 'tag'],
+ ['[>\\+,]', 'delimiter'],
+ ['\\[', { token: 'delimiter.bracket', next: '@selectorattribute' }],
+ ['{', { token: 'delimiter.curly', next: '@selectorbody' }],
+ ],
+ selectorbody: [
+ [
+ '[*_]?@identifier@ws:(?=(\\s|\\d|[^{;}]*[;}]))',
+ 'attribute.name',
+ '@rulevalue',
+ ],
+ { include: '@selector' },
+ ['[@](extend)', { token: 'keyword', next: '@extendbody' }],
+ ['[@](return)', { token: 'keyword', next: '@declarationbody' }],
+ ['}', { token: 'delimiter.curly', next: '@pop' }],
+ ],
+ selectorname: [
+ ['#{', { token: 'meta', next: '@variableinterpolation' }],
+ ['(\\.|#(?=[^{])|%|(@identifier)|:)+', 'tag'], // selector (.foo, div, ...)
+ ],
+ selectorattribute: [
+ { include: '@term' },
+ [']', { token: 'delimiter.bracket', next: '@pop' }],
+ ],
+ term: [
+ { include: '@comments' },
+ ['url(\\-prefix)?\\(', { token: 'meta', next: '@urldeclaration' }],
+ { include: '@functioninvocation' },
+ { include: '@numbers' },
+ { include: '@strings' },
+ { include: '@variablereference' },
+ ['(and\\b|or\\b|not\\b)', 'operator'],
+ { include: '@name' },
+ ['([<>=\\+\\-\\*\\/\\^\\|\\~,])', 'operator'],
+ [',', 'delimiter'],
+ ['!default', 'literal'],
+ ['\\(', { token: 'delimiter.parenthesis', next: '@parenthizedterm' }],
+ ],
+ rulevalue: [
+ { include: '@term' },
+ ['!important', 'literal'],
+ [';', 'delimiter', '@pop'],
+ ['{', { token: 'delimiter.curly', switchTo: '@nestedproperty' }],
+ ['(?=})', { token: '', next: '@pop' }], // missing semicolon
+ ],
+ nestedproperty: [
+ ['[*_]?@identifier@ws:', 'attribute.name', '@rulevalue'],
+ { include: '@comments' },
+ ['}', { token: 'delimiter.curly', next: '@pop' }],
+ ],
+ warndebug: [
+ ['[@](warn|debug)', { token: 'keyword', next: '@declarationbody' }],
+ ],
+ import: [['[@](import)', { token: 'keyword', next: '@declarationbody' }]],
+ variabledeclaration: [
+ // sass variables
+ ['\\$@identifier@ws:', 'variable.decl', '@declarationbody'],
+ ],
+ urldeclaration: [
+ { include: '@strings' },
+ ['[^)\r\n]+', 'string'],
+ ['\\)', { token: 'meta', next: '@pop' }],
+ ],
+ parenthizedterm: [
+ { include: '@term' },
+ ['\\)', { token: 'delimiter.parenthesis', next: '@pop' }],
+ ],
+ declarationbody: [
+ { include: '@term' },
+ [';', 'delimiter', '@pop'],
+ ['(?=})', { token: '', next: '@pop' }], // missing semicolon
+ ],
+ extendbody: [
+ { include: '@selectorname' },
+ ['!optional', 'literal'],
+ [';', 'delimiter', '@pop'],
+ ['(?=})', { token: '', next: '@pop' }], // missing semicolon
+ ],
+ variablereference: [
+ // sass variable reference
+ ['\\$@identifier', 'variable.ref'],
+ ['\\.\\.\\.', 'operator'],
+ ['#{', { token: 'meta', next: '@variableinterpolation' }], // sass var resolve
+ ],
+ variableinterpolation: [
+ { include: '@variablereference' },
+ ['}', { token: 'meta', next: '@pop' }],
+ ],
+ comments: [
+ ['\\/\\*', 'comment', '@comment'],
+ ['\\/\\/+.*', 'comment'],
+ ],
+ comment: [
+ ['\\*\\/', 'comment', '@pop'],
+ ['.', 'comment'],
+ ],
+ name: [['@identifier', 'attribute.value']],
+ numbers: [
+ ['(\\d*\\.)?\\d+([eE][\\-+]?\\d+)?', { token: 'number', next: '@units' }],
+ ['#[0-9a-fA-F_]+(?!\\w)', 'number.hex'],
+ ],
+ units: [
+ [
+ '(em|ex|ch|rem|vmin|vmax|vw|vh|vm|cm|mm|in|px|pt|pc|deg|grad|rad|turn|s|ms|Hz|kHz|%)?',
+ 'number',
+ '@pop',
+ ],
+ ],
+ functiondeclaration: [
+ ['@identifier@ws\\(', { token: 'meta', next: '@parameterdeclaration' }],
+ ['{', { token: 'delimiter.curly', switchTo: '@functionbody' }],
+ ],
+ mixindeclaration: [
+ // mixin with parameters
+ ['@identifier@ws\\(', { token: 'meta', next: '@parameterdeclaration' }],
+ // mixin without parameters
+ ['@identifier', 'meta'],
+ ['{', { token: 'delimiter.curly', switchTo: '@selectorbody' }],
+ ],
+ parameterdeclaration: [
+ ['\\$@identifier@ws:', 'variable.decl'],
+ ['\\.\\.\\.', 'operator'],
+ [',', 'delimiter'],
+ { include: '@term' },
+ ['\\)', { token: 'meta', next: '@pop' }],
+ ],
+ includedeclaration: [
+ { include: '@functioninvocation' },
+ ['@identifier', 'meta'],
+ [';', 'delimiter', '@pop'],
+ ['(?=})', { token: '', next: '@pop' }],
+ ['{', { token: 'delimiter.curly', switchTo: '@selectorbody' }],
+ ],
+ keyframedeclaration: [
+ ['@identifier', 'meta'],
+ ['{', { token: 'delimiter.curly', switchTo: '@keyframebody' }],
+ ],
+ keyframebody: [
+ { include: '@term' },
+ ['{', { token: 'delimiter.curly', next: '@selectorbody' }],
+ ['}', { token: 'delimiter.curly', next: '@pop' }],
+ ],
+ controlstatement: [
+ [
+ '[@](if|else|for|while|each|media)',
+ { token: 'keyword.flow', next: '@controlstatementdeclaration' },
+ ],
+ ],
+ controlstatementdeclaration: [
+ ['(in|from|through|if|to)\\b', { token: 'keyword.flow' }],
+ { include: '@term' },
+ ['{', { token: 'delimiter.curly', switchTo: '@selectorbody' }],
+ ],
+ functionbody: [
+ ['[@](return)', { token: 'keyword' }],
+ { include: '@variabledeclaration' },
+ { include: '@term' },
+ { include: '@controlstatement' },
+ [';', 'delimiter'],
+ ['}', { token: 'delimiter.curly', next: '@pop' }],
+ ],
+ functioninvocation: [
+ ['@identifier\\(', { token: 'meta', next: '@functionarguments' }],
+ ],
+ functionarguments: [
+ ['\\$@identifier@ws:', 'attribute.name'],
+ ['[,]', 'delimiter'],
+ { include: '@term' },
+ ['\\)', { token: 'meta', next: '@pop' }],
+ ],
+ strings: [
+ ['~?"', { token: 'string.delimiter', next: '@stringenddoublequote' }],
+ ["~?'", { token: 'string.delimiter', next: '@stringendquote' }],
+ ],
+ stringenddoublequote: [
+ ['\\\\.', 'string'],
+ ['"', { token: 'string.delimiter', next: '@pop' }],
+ ['.', 'string'],
+ ],
+ stringendquote: [
+ ['\\\\.', 'string'],
+ ["'", { token: 'string.delimiter', next: '@pop' }],
+ ['.', 'string'],
+ ],
+ },
+};
+
+export default language;
diff --git a/services/app/apps/codebattle/assets/js/widgets/config/editor/stylus.js b/services/app/apps/codebattle/assets/js/widgets/config/editor/stylus.js
new file mode 100644
index 000000000..397d62690
--- /dev/null
+++ b/services/app/apps/codebattle/assets/js/widgets/config/editor/stylus.js
@@ -0,0 +1,116 @@
+import tagKeywords from './tagKeywords';
+
+export const language = {
+ tagKeywords,
+ keywords: [
+ 'true',
+ 'false',
+ 'null',
+ 'return',
+ 'else',
+ 'for',
+ 'unless',
+ 'if',
+ 'else',
+ 'arguments',
+ // "!important",
+ 'in',
+ // "is defined",
+ // "is a",
+ ],
+ brackets: [
+ { open: '{', close: '}', token: 'delimiter.curly' },
+ { open: '[', close: ']', token: 'delimiter.bracket' },
+ { open: '(', close: ')', token: 'delimiter.parenthesis' },
+ ],
+ digits: /\d+/u,
+ escapes: /\\./u,
+ tokenizer: {
+ root: [
+ [/is defined\b/u, 'keyword'],
+ [/is a\b/u, 'keyword'],
+ [/!important\b/u, 'keyword'],
+ [/@[\w-]*/u, 'keyword'],
+ // Mixin / Function
+ [/[a-z][\w-]*(?=\()/u, 'tag'],
+ // identifiers
+ [
+ /[$\-_a-z][\w$-]*/u,
+ {
+ cases: {
+ '@keywords': 'keyword',
+ '@tagKeywords': 'tag',
+ '@default': 'identifier',
+ },
+ },
+ ],
+ // ID selector
+ [/#[a-z][\w-]*/u, 'tag'],
+ // Class selector
+ [/\.[a-z][\w-]*/u, 'tag'],
+
+ [/[,;]/u, 'delimiter'],
+ [/[()[\]{}]/u, '@brackets'],
+
+ // numbers
+ { include: '@numbers' },
+
+ // whitespace
+ [/[\t\n\f\r ]+/u, ''],
+ { include: '@comments' },
+
+ // strings
+ { include: '@strings' },
+ ],
+ numbers: [
+ [/(@digits)[Ee]([+-]?(@digits))?/u, 'attribute.value.number', '@units'],
+ [
+ /(@digits)\.(@digits)([Ee][+-]?(@digits))?/u,
+ 'attribute.value.number',
+ '@units',
+ ],
+ [/(@digits)/u, 'attribute.value.number', '@units'],
+ [
+ /#[\dA-Fa-f]{3}([\dA-Fa-f]([\dA-Fa-f]{2}){0,2})?\b(?!-)/u,
+ 'attribute.value.hex',
+ ],
+ ],
+ comments: [
+ [/\/\*/u, 'comment', '@commentBody'],
+ [/\/\/.*$/u, 'comment'],
+ ],
+ strings: [
+ [/"([^"\\]|\\.)*$/u, 'string.invalid'], // non-teminated string
+ [/'([^'\\]|\\.)*$/u, 'string.invalid'], // non-teminated string
+ [/"/u, 'string', '@stringDoubleBody'],
+ [/'/u, 'string', '@stringSingleBody'],
+ ],
+
+ commentBody: [
+ [/[^*/]+/u, 'comment'],
+ [/\*\//u, 'comment', '@pop'],
+ [/[*/]/u, 'comment'],
+ ],
+ stringDoubleBody: [
+ [/[^"\\]+/u, 'string'],
+ [/@escapes/u, 'string.escape'],
+ [/\\./u, 'string.escape.invalid'],
+ [/"/u, 'string', '@pop'],
+ ],
+ stringSingleBody: [
+ [/[^'\\]+/u, 'string'],
+ [/@escapes/u, 'string.escape'],
+ [/\\./u, 'string.escape.invalid'],
+ [/'/u, 'string', '@pop'],
+ ],
+ units: [
+ [
+ /((em|ex|ch|rem|vmin|vmax|vw|vh|vm|cm|mm|in|px|pt|pc|deg|grad|rad|turn|s|ms|Hz|kHz|%)\b)?/u,
+ 'attribute.value.unit',
+ '@pop',
+ ],
+ ],
+ },
+};
+
+export default language;
diff --git a/services/app/apps/codebattle/assets/js/widgets/config/editor/tagKeywords.js b/services/app/apps/codebattle/assets/js/widgets/config/editor/tagKeywords.js
new file mode 100644
index 000000000..c990d4d69
--- /dev/null
+++ b/services/app/apps/codebattle/assets/js/widgets/config/editor/tagKeywords.js
@@ -0,0 +1,117 @@
+// developer.mozilla.org/en-US/docs/Web/HTML/Element
+const tagKeywords = [
+ 'a',
+ 'abbr',
+ 'address',
+ 'area',
+ 'article',
+ 'aside',
+ 'audio',
+ 'b',
+ 'base',
+ 'bdi',
+ 'bdo',
+ 'bgsound',
+ 'blockquote',
+ 'body',
+ 'br',
+ 'button',
+ 'canvas',
+ 'caption',
+ 'cite',
+ 'code',
+ 'col',
+ 'colgroup',
+ 'data',
+ 'datalist',
+ 'dd',
+ 'del',
+ 'details',
+ 'dfn',
+ 'div',
+ 'dl',
+ 'dt',
+ 'em',
+ 'embed',
+ 'fieldset',
+ 'figcaption',
+ 'figure',
+ 'footer',
+ 'form',
+ 'h1',
+ 'h2',
+ 'h3',
+ 'h4',
+ 'h5',
+ 'h6',
+ 'head',
+ 'header',
+ 'hgroup',
+ 'hr',
+ 'html',
+ 'i',
+ 'iframe',
+ 'img',
+ 'input',
+ 'ins',
+ 'kbd',
+ 'keygen',
+ 'label',
+ 'legend',
+ 'li',
+ 'link',
+ 'main',
+ 'map',
+ 'mark',
+ 'marquee',
+ 'menu',
+ 'menuitem',
+ 'meta',
+ 'meter',
+ 'nav',
+ 'nobr',
+ 'noframes',
+ 'noscript',
+ 'object',
+ 'ol',
+ 'optgroup',
+ 'option',
+ 'output',
+ 'p',
+ 'param',
+ 'pre',
+ 'progress',
+ 'q',
+ 'rp',
+ 'rt',
+ 'ruby',
+ 's',
+ 'samp',
+ 'script',
+ 'section',
+ 'select',
+ 'small',
+ 'source',
+ 'span',
+ 'strong',
+ 'style',
+ 'sub',
+ 'summary',
+ 'sup',
+ 'table',
+ 'tbody',
+ 'td',
+ 'textarea',
+ 'tfoot',
+ 'th',
+ 'thead',
+ 'time',
+ 'tr',
+ 'track',
+ 'u',
+ 'ul',
+ 'var',
+ 'video',
+];
+
+export default tagKeywords;
diff --git a/services/app/apps/codebattle/assets/js/widgets/config/languages.js b/services/app/apps/codebattle/assets/js/widgets/config/languages.js
index 41aa3b6c0..c4d85076b 100644
--- a/services/app/apps/codebattle/assets/js/widgets/config/languages.js
+++ b/services/app/apps/codebattle/assets/js/widgets/config/languages.js
@@ -14,6 +14,10 @@ export default {
ruby: 'ruby',
rust: 'rust',
ts: 'typescript',
+ css: 'css',
+ less: 'less',
+ sass: 'scss',
+ stylus: 'stylus',
};
export const constructorLangauges = ['ruby'];
diff --git a/services/app/apps/codebattle/assets/js/widgets/pages/lobby/LobbyWidget.jsx b/services/app/apps/codebattle/assets/js/widgets/pages/lobby/LobbyWidget.jsx
index 7f129f97e..1e555c5be 100644
--- a/services/app/apps/codebattle/assets/js/widgets/pages/lobby/LobbyWidget.jsx
+++ b/services/app/apps/codebattle/assets/js/widgets/pages/lobby/LobbyWidget.jsx
@@ -304,7 +304,7 @@ const createBasicGameBtnClassName = cn(
const createCssGameBtnClassName = cn(
createBtnClassName,
- 'btn-secondary',
+ 'btn-secondary mt-2',
);
const CreateCssGameButton = ({ onClick, isOnline }) => (
diff --git a/services/app/apps/codebattle/assets/js/widgets/pages/lobby/Players.jsx b/services/app/apps/codebattle/assets/js/widgets/pages/lobby/Players.jsx
index a3170404e..1a7142eee 100644
--- a/services/app/apps/codebattle/assets/js/widgets/pages/lobby/Players.jsx
+++ b/services/app/apps/codebattle/assets/js/widgets/pages/lobby/Players.jsx
@@ -24,7 +24,7 @@ const Players = memo(({ players, isBot, gameId }) => {
return (
-
+
{tooltipInfo}}
placement="right"
@@ -44,6 +44,7 @@ const Players = memo(({ players, isBot, gameId }) => {
@@ -54,6 +55,7 @@ const Players = memo(({ players, isBot, gameId }) => {
|