diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js
index 2a13291d..73e7c498 100644
--- a/app/assets/javascripts/application.js
+++ b/app/assets/javascripts/application.js
@@ -17,6 +17,7 @@
//
// lib/assets
//= require flash
+//= require color_mode_picker
//
// vendor/assets
//= require ace/ace
diff --git a/app/assets/javascripts/channels/la_exercises.js b/app/assets/javascripts/channels/la_exercises.js
index 1f47c552..bfb7e0e2 100644
--- a/app/assets/javascripts/channels/la_exercises.js
+++ b/app/assets/javascripts/channels/la_exercises.js
@@ -208,7 +208,7 @@ $(document).on('turbolinks:load', function() {
.html(function(_event, _d) {
const e = rect.nodes();
const i = e.indexOf(this) % learners.length;
- return "Student: " + learners_name(i) + "
" +
+ return "Student: " + learners_name(i) + "
" +
"0: " + learners_time(0, i) + "
" +
"1: " + learners_time(1, i) + "
" +
"2: " + learners_time(2, i) + "
" +
diff --git a/app/assets/javascripts/community_solution.js b/app/assets/javascripts/community_solution.js
index 68c62d17..6566d45a 100644
--- a/app/assets/javascripts/community_solution.js
+++ b/app/assets/javascripts/community_solution.js
@@ -20,6 +20,7 @@ $(document).on('turbolinks:load', function() {
CodeOceanEditorSubmissions
)
+ $(document).on('theme:change:ace', CodeOceanEditor.handleAceThemeChangeEvent.bind(CodeOceanEditor));
$('#submit').one('click', CodeOceanEditorSubmissions.submitCode.bind(CodeOceanEditor));
$('#accept').one('click', CodeOceanEditorSubmissions.submitCode.bind(CodeOceanEditor));
}
diff --git a/app/assets/javascripts/editor.js b/app/assets/javascripts/editor.js
index 028ac69f..4f31c3ab 100644
--- a/app/assets/javascripts/editor.js
+++ b/app/assets/javascripts/editor.js
@@ -19,4 +19,13 @@ $(document).on('turbolinks:load', function(event) {
// Search for insertLines and Turbolinks reload / cache control
CodeOceanEditor.initializeEverything();
}
+
+ function handleThemeChangeEvent(event) {
+ if (CodeOceanEditor) {
+ CodeOceanEditor.THEME = event.detail.currentTheme === 'dark' ? 'ace/theme/tomorrow_night' : 'ace/theme/tomorrow';
+ document.dispatchEvent(new Event('theme:change:ace'));
+ }
+ }
+
+ $(document).on('theme:change', handleThemeChangeEvent.bind(this));
});
diff --git a/app/assets/javascripts/editor/editor.js.erb b/app/assets/javascripts/editor/editor.js.erb
index 401db40f..26f3eee2 100644
--- a/app/assets/javascripts/editor/editor.js.erb
+++ b/app/assets/javascripts/editor/editor.js.erb
@@ -2,7 +2,7 @@ var CodeOceanEditor = {
//ACE-Editor-Path
// ruby part adds the relative_url_root, if it is set.
ACE_FILES_PATH: '<%= "#{Rails.application.config.relative_url_root.chomp('/')}/assets/ace/" %>',
- THEME: 'ace/theme/textmate',
+ THEME: window.getCurrentTheme() === 'dark' ? 'ace/theme/tomorrow_night' : 'ace/theme/tomorrow',
//Color-Encoding for Percentages in Progress Bars (For submissions)
ADEQUATE_PERCENTAGE: 50,
@@ -88,13 +88,13 @@ var CodeOceanEditor = {
getCardClass: function (result) {
if (result.file_role === 'teacher_defined_linter') {
- return 'card bg-info text-white'
+ return 'bg-info text-white'
} else if (result.stderr && !result.score) {
- return 'card bg-danger text-white';
+ return 'bg-danger text-white';
} else if (result.score < 1) {
- return 'card bg-warning text-white';
+ return 'bg-warning text-white';
} else {
- return 'card bg-success text-white';
+ return 'bg-success text-white';
}
},
@@ -127,7 +127,7 @@ var CodeOceanEditor = {
if (!filetree.hasClass('jstree-loading')) {
filetree.jstree("deselect_all");
- filetree.jstree().select_node(file_id);
+ filetree.jstree(true).select_node(file_id);
} else {
setTimeout(CodeOceanEditor.selectFileInJsTree.bind(null, filetree, file_id), 250);
}
@@ -309,16 +309,12 @@ var CodeOceanEditor = {
});
}
-
editor.commands.bindKey("ctrl+alt+0", null);
this.editors.push(editor);
this.editor_for_file.set($(element).parent().data('filename'), editor);
var session = editor.getSession();
var mode = $(element).data('mode')
session.setMode(mode);
- if (mode === 'ace/mode/python') {
- editor.setTheme('ace/theme/tomorrow')
- }
session.setTabSize($(element).data('indent-size'));
session.setUseSoftTabs(true);
session.setUseWrapMode(true);
@@ -345,6 +341,12 @@ var CodeOceanEditor = {
}.bind(this));
},
+ handleAceThemeChangeEvent: function (event) {
+ this.editors.forEach(function (editor) {
+ editor.setTheme(this.THEME);
+ }.bind(this));
+ },
+
handleUTF16Surrogates: function (AceDeltaObject, AceSession) {
if (AceDeltaObject.data === undefined || AceDeltaObject.data.action !== "removeText") {
return;
@@ -372,6 +374,7 @@ var CodeOceanEditor = {
initializeEventHandlers: function () {
$(document).on('click', '#results a', this.showOutput.bind(this));
$(document).on('keydown', this.handleKeyPress.bind(this));
+ $(document).on('theme:change:ace', this.handleAceThemeChangeEvent.bind(this));
this.initializeFileTreeButtons();
this.initializeWorkspaceButtons();
this.initializeRequestForComments()
@@ -398,9 +401,6 @@ var CodeOceanEditor = {
}).fail(_.noop)
.always(function () {
ace.edit(editor).session.setMode(newMode);
- if (newMode === 'ace/mode/python') {
- ace.edit(editor).setTheme('ace/theme/tomorrow')
- }
});
},
@@ -411,7 +411,9 @@ var CodeOceanEditor = {
} else {
filesInstance = $('#files');
}
- filesInstance.jstree(filesInstance.data('entries'));
+ const jsTreeConfig = filesInstance.data('entries') || {core: {}};
+ jsTreeConfig.core.themes = {...jsTreeConfig.core.themes, name: window.getCurrentTheme() === "dark" ? "default-dark" : "default"}
+ filesInstance.jstree(jsTreeConfig);
filesInstance.on('click', 'li.jstree-leaf > a', function (event) {
const file_id = parseInt($(event.target).parent().attr('id'));
const frame = $('[data-file-id="' + file_id + '"]').parent();
@@ -419,6 +421,11 @@ var CodeOceanEditor = {
this.showFrame(frame);
this.toggleButtonStates();
}.bind(this));
+ $(document).on('theme:change', function(event) {
+ const newColorScheme = event.detail.currentTheme;
+ // Update the JStree theme
+ filesInstance.jstree(true).set_theme(newColorScheme === "dark" ? "default-dark" : "default");
+ });
},
initializeFileTreeButtons: function () {
@@ -539,7 +546,8 @@ var CodeOceanEditor = {
},
populateCard: function (card, result, index) {
- card.addClass(this.getCardClass(result));
+ card.addClass('card');
+ card.find('.card-header').addClass(this.getCardClass(result));
card.find('.card-title .filename').text(result.filename);
card.find('.card-title .number').text(index + 1);
card.find('.row .col-md-9').eq(0).find('.number').eq(0).text(result.passed);
diff --git a/app/assets/javascripts/exercise_graphs.js b/app/assets/javascripts/exercise_graphs.js
index 86014010..79421288 100644
--- a/app/assets/javascripts/exercise_graphs.js
+++ b/app/assets/javascripts/exercise_graphs.js
@@ -120,19 +120,19 @@ $(document).on('turbolinks:load', function() {
var largestSubmittedTimeStamp = submissions[submissions_length-1];
var largestArrayForRange;
- if(largestSubmittedTimeStamp.cause == "assess"){
+ if(largestSubmittedTimeStamp.cause === "assess"){
largestArrayForRange = submissionsScoreAndTimeAssess;
x.domain([0,largestArrayForRange[largestArrayForRange.length - 1][1]]).clamp(true);
- } else if(largestSubmittedTimeStamp.cause == "submit"){
+ } else if(largestSubmittedTimeStamp.cause === "submit"){
largestArrayForRange = submissionsScoreAndTimeSubmits;
x.domain([0,largestArrayForRange[largestArrayForRange.length - 1][1]]).clamp(true);
- } else if(largestSubmittedTimeStamp.cause == "run"){
+ } else if(largestSubmittedTimeStamp.cause === "run"){
largestArrayForRange = submissionsScoreAndTimeRuns;
x.domain([0,largestArrayForRange[largestArrayForRange.length - 1]]).clamp(true);
- } else if(largestSubmittedTimeStamp.cause == "autosave"){
+ } else if(largestSubmittedTimeStamp.cause === "autosave"){
largestArrayForRange = submissionsAutosaves;
x.domain([0,largestArrayForRange[largestArrayForRange.length - 1]]).clamp(true);
- } else if(largestSubmittedTimeStamp.cause == "save"){
+ } else if(largestSubmittedTimeStamp.cause === "save"){
largestArrayForRange = submissionsSaves;
x.domain([0,largestArrayForRange[largestArrayForRange.length - 1]]).clamp(true);
}
@@ -163,6 +163,7 @@ $(document).on('turbolinks:load', function() {
.call(yAxis);
svg.append("text") // y axis label
+ .attr("class", "y axis")
.attr("transform", "rotate(-90)")
.attr("x", -height / 2)
.attr("dy", "-3em")
@@ -180,12 +181,12 @@ $(document).on('turbolinks:load', function() {
.style('font-size', 20)
.style('text-decoration', 'underline');
-
+
svg.append("path")
//.datum()
.attr("class", "line")
.attr('id', 'myPath')// new
- .attr("stroke", "black")
+ .attr("stroke", "var(--bs-emphasis-color)")
.attr("stroke-width", 5)
.attr("fill", "none")// end new
.attr("d", line(submissionsScoreAndTimeAssess));//---
@@ -194,7 +195,7 @@ $(document).on('turbolinks:load', function() {
.datum(submissionsScoreAndTimeAssess)
.attr("class", "line")
.attr('id', 'myPath')// new
- .attr("stroke", "orange")
+ .attr("stroke", "var(--bs-warning)")
.attr("stroke-width", 5)
.attr("fill", "none")// end new
.attr("d", line);//---
@@ -203,6 +204,7 @@ $(document).on('turbolinks:load', function() {
svg.selectAll("dot") // Add dots to assesses
.data(submissionsScoreAndTimeAssess)
.enter().append("circle")
+ .attr("fill", "var(--bs-secondary)")
.attr("r", 3.5)
.attr("cx", function(d) { return x(d[1]); })
.attr("cy", function(d) { return y(d[0]); });
@@ -216,14 +218,14 @@ $(document).on('turbolinks:load', function() {
.data(submissionsScoreAndTimeSubmits)
.enter().append("circle")
.attr("r", 6)
- .attr("stroke", "black")
- .attr("fill", "blue")
+ .attr("stroke", "var(--bs-emphasis-color)")
+ .attr("fill", "var(--bs-blue)")
.attr("cx", function(d) { return x(d[1]); })
.attr("cy", function(d) { return y(d[0]); });
for (var i = 0; i < submissionsScoreAndTimeRuns.length; i++) {
svg.append("line")
- .attr("stroke", "red")
+ .attr("stroke", "var(--bs-red)")
.attr("stroke-width", 1)
.attr("fill", "none")// end new
.attr("y1", y(0))
@@ -232,9 +234,9 @@ $(document).on('turbolinks:load', function() {
.attr("x2", x(submissionsScoreAndTimeRuns[i]));
}
- var color_hash = { 0 : ["Submissions", "blue"],
- 1 : ["Assesses", "orange"],
- 2 : ["Runs", "red"]
+ var color_hash = { 0 : ["Submissions", "var(--bs-blue)"],
+ 1 : ["Assesses", "var(--bs-orange)"],
+ 2 : ["Runs", "var(--bs-red)"]
};
// add legend
diff --git a/app/assets/javascripts/exercises.js.erb b/app/assets/javascripts/exercises.js.erb
index 2709bdb0..57b64fbd 100644
--- a/app/assets/javascripts/exercises.js.erb
+++ b/app/assets/javascripts/exercises.js.erb
@@ -1,17 +1,13 @@
$(document).on('turbolinks:load', function () {
- // ruby part adds the relative_url_root, if it is set.
- var ACE_FILES_PATH = '<%= "#{Rails.application.config.relative_url_root.chomp('/')}/assets/ace/" %>';
- var THEME = 'ace/theme/textmate';
-
var TAB_KEY_CODE = 9;
var execution_environments;
var file_types;
-
+ const editors = [];
var configureEditors = function () {
_.each(['modePath', 'themePath', 'workerPath'], function (attribute) {
- ace.config.set(attribute, ACE_FILES_PATH);
+ ace.config.set(attribute, CodeOceanEditor.ACE_FILES_PATH);
});
};
@@ -28,7 +24,8 @@ $(document).on('turbolinks:load', function () {
// document.removeLines(document.getLength() - 1, document.getLength() - 1);
editor.setReadOnly($(element).data('read-only') !== undefined);
editor.setShowPrintMargin(false);
- editor.setTheme(THEME);
+ editor.setTheme(CodeOceanEditor.THEME);
+ editors.push(editor);
// For creating / editing an exercise
var textarea = $('textarea[id="exercise_files_attributes_' + index + '_content"]');
@@ -52,6 +49,14 @@ $(document).on('turbolinks:load', function () {
session.setUseWrapMode(true);
}
+ const handleAceThemeChangeEvent = function(event) {
+ editors.forEach(function (editor) {
+ editor.setTheme(CodeOceanEditor.THEME);
+ }.bind(this));
+ };
+
+ $(document).on('theme:change:ace', handleAceThemeChangeEvent.bind(this));
+
var initializeEditors = function () {
// initialize ace editors for all code textareas in the dom except the last one. The last one is the dummy area for new files, which is cloned when needed.
// this one must NOT! be initialized.
diff --git a/app/assets/javascripts/markdown_ace_editor.js.erb b/app/assets/javascripts/markdown_ace_editor.js.erb
index 9ac28df1..f5e0c80d 100644
--- a/app/assets/javascripts/markdown_ace_editor.js.erb
+++ b/app/assets/javascripts/markdown_ace_editor.js.erb
@@ -1,13 +1,14 @@
(function() {
- var ACE_FILES_PATH = '<%= "#{Rails.application.config.relative_url_root.chomp('/')}/assets/ace/" %>';
-
window.MarkdownEditor = function(selector) {
- ace.config.set('modePath', ACE_FILES_PATH);
+ _.each(['modePath', 'themePath', 'workerPath'], function (attribute) {
+ ace.config.set(attribute, CodeOceanEditor.ACE_FILES_PATH);
+ }.bind(this));
var editor = ace.edit($(selector).next()[0]);
editor.on('change', function() {
$(selector).val(editor.getValue());
});
editor.setShowPrintMargin(false);
+ editor.setTheme(CodeOceanEditor.THEME);
var session = editor.getSession();
session.setMode('ace/mode/markdown');
session.setUseWrapMode(true);
diff --git a/app/assets/javascripts/request_for_comments.js b/app/assets/javascripts/request_for_comments.js
index d999bcaa..54900fd5 100644
--- a/app/assets/javascripts/request_for_comments.js
+++ b/app/assets/javascripts/request_for_comments.js
@@ -70,6 +70,7 @@ $(document).on('turbolinks:load', function () {
// set editor mode (used for syntax highlighting
currentEditor.getSession().setMode($(editor).data('mode'));
currentEditor.getSession().setOption("useWorker", false);
+ currentEditor.setTheme(CodeOceanEditor.THEME);
currentEditor.commentVisualsByLine = {};
setAnnotations(currentEditor, $(editor).data('file-id'));
@@ -77,6 +78,14 @@ $(document).on('turbolinks:load', function () {
currentEditor.on("guttermousemove", showPopover);
});
+ const handleAceThemeChangeEvent = function() {
+ $('.editor').each(function (_, editor) {
+ ace.edit(editor).setTheme(CodeOceanEditor.THEME);
+ }.bind(this));
+ };
+
+ $(document).on('theme:change:ace', handleAceThemeChangeEvent.bind(this));
+
function preprocess(commentText) {
// sanitize comments to deal with XSS attacks:
commentText = $('div.sanitizer').text(commentText).html();
diff --git a/app/assets/javascripts/shell.js b/app/assets/javascripts/shell.js
index 5e64c5d2..a1d1c822 100644
--- a/app/assets/javascripts/shell.js
+++ b/app/assets/javascripts/shell.js
@@ -99,13 +99,16 @@ $(document).on('turbolinks:load', function () {
fileTree.removeClass('my-3 justify-content-center');
fileTree.jstree({
'core': {
+ 'themes': {
+ 'name': window.getCurrentTheme() === "dark" ? "default-dark" : "default"
+ },
'data': {
'url': function (node) {
const params = {sudo: sudo.is(':checked')};
return Routes.list_files_in_execution_environment_path(id, params);
},
'data': function (node) {
- return {'path': getPath(fileTree.jstree(), node)|| '/'};
+ return {'path': getPath(fileTree.jstree(true), node)|| '/'};
}
}
}
@@ -130,6 +133,11 @@ $(document).on('turbolinks:load', function () {
window.location = downloadPath;
}
}.bind(this));
+ $(document).on('theme:change', function(event) {
+ const newColorScheme = event.detail.currentTheme;
+ // Update the JStree theme
+ fileTree.jstree(true).set_theme(newColorScheme === "dark" ? "default-dark" : "default");
+ });
}
}
diff --git a/app/assets/javascripts/submission_statistics.js.erb b/app/assets/javascripts/submission_statistics.js.erb
index 1a57354b..7885654b 100644
--- a/app/assets/javascripts/submission_statistics.js.erb
+++ b/app/assets/javascripts/submission_statistics.js.erb
@@ -1,8 +1,4 @@
$(document).on('turbolinks:load', function(event) {
-
- var ACE_FILES_PATH = '<%= "#{Rails.application.config.relative_url_root.chomp('/')}/assets/ace/" %>';
- var THEME = 'ace/theme/textmate';
-
var currentSubmission = 0;
var active_file = undefined;
var fileTrees = [];
@@ -24,7 +20,7 @@ $(document).on('turbolinks:load', function(event) {
var selectFileInJsTree = function() {
if (!filetree.hasClass('jstree-loading')) {
filetree.jstree("deselect_all");
- filetree.jstree().select_node(active_file.file_id);
+ filetree.jstree("select_node", active_file.file_id);
} else {
setTimeout(selectFileInJsTree, 250);
}
@@ -38,8 +34,10 @@ $(document).on('turbolinks:load', function(event) {
var initializeFileTree = function() {
$('.files').each(function(index, element) {
- fileTree = $(element).jstree($(element).data('entries'));
- fileTree.on('click', 'li.jstree-leaf', function() {
+ const jsTreeConfig = $(element).data('entries')
+ jsTreeConfig.core.themes = {...jsTreeConfig.core.themes, name: window.getCurrentTheme() === "dark" ? "default-dark" : "default"}
+ const fileTree = $(element).jstree(jsTreeConfig);
+ $(element).on('click', 'li.jstree-leaf', function() {
var id = parseInt($(this).attr('id'));
_.each(files[currentSubmission], function(file) {
if (file.file_id === id) {
@@ -48,6 +46,11 @@ $(document).on('turbolinks:load', function(event) {
});
showActiveFile();
});
+ $(document).on('theme:change', function(event) {
+ const newColorScheme = event.detail.currentTheme;
+ // Update the JStree theme
+ fileTree.jstree(true).set_theme(newColorScheme === "dark" ? "default-dark" : "default");
+ });
fileTrees.push(fileTree);
});
};
@@ -60,7 +63,7 @@ $(document).on('turbolinks:load', function(event) {
if ($.isController('exercises') && $('#timeline').isPresent() && event.originalEvent.data.url.includes("/statistics")) {
_.each(['modePath', 'themePath', 'workerPath'], function(attribute) {
- ace.config.set(attribute, ACE_FILES_PATH);
+ ace.config.set(attribute, CodeOceanEditor.ACE_FILES_PATH);
});
var slider = $('#submissions-slider>input');
@@ -72,7 +75,7 @@ $(document).on('turbolinks:load', function(event) {
editor = ace.edit('current-file');
editor.setShowPrintMargin(false);
- editor.setTheme(THEME);
+ editor.setTheme(CodeOceanEditor.THEME);
editor.$blockScrolling = Infinity;
editor.setReadOnly(true);
@@ -90,6 +93,12 @@ $(document).on('turbolinks:load', function(event) {
});
});
+ const handleAceThemeChangeEvent = function() {
+ editor.setTheme(CodeOceanEditor.THEME);
+ };
+
+ $(document).on('theme:change:ace', handleAceThemeChangeEvent.bind(this));
+
const onSliderChange = function(event) {
currentSubmission = slider.val();
var currentFiles = files[currentSubmission];
diff --git a/app/assets/javascripts/working_time_graphs.js b/app/assets/javascripts/working_time_graphs.js
index 7fa3c802..7d09169d 100644
--- a/app/assets/javascripts/working_time_graphs.js
+++ b/app/assets/javascripts/working_time_graphs.js
@@ -113,6 +113,7 @@ $(document).on('turbolinks:load', function() {
.call(yAxis);
svg.append("text") // y axis label
+ .attr("class", "y axis")
.attr("transform", "rotate(-90)")
.attr("x", -height / 2)
.attr("dy", "-3em")
@@ -134,7 +135,7 @@ $(document).on('turbolinks:load', function() {
.datum(minutes_count)
.attr("class", "line")
.attr('id', 'myPath')
- .attr("stroke", "orange")
+ .attr("stroke", "var(--bs-warning)")
.attr("stroke-width", 5)
.attr("fill", "none")
.attr("d", line);
@@ -216,7 +217,7 @@ $(document).on('turbolinks:load', function() {
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function(_event, d) {
- return "Students: " + d + "";
+ return "Students: " + d + "";
});
var svg = d3.select("#chart_2").append("svg")
@@ -248,6 +249,7 @@ $(document).on('turbolinks:load', function() {
.attr("dy", ".71em");
svg.append("text")
+ .attr("class", "y axis")
.attr("transform", "rotate(-90)")
.attr("x", -height / 2)
.attr("dy", "-3em")
diff --git a/app/assets/stylesheets/base.css.scss b/app/assets/stylesheets/base.css.scss
index ff240cc2..c642c09c 100644
--- a/app/assets/stylesheets/base.css.scss
+++ b/app/assets/stylesheets/base.css.scss
@@ -9,7 +9,7 @@ h1, h2, h3, h4, h5, h6 {
.lead {
font-size: 16px;
- color: rgba(70, 70, 70, 1);
+ color: var(--bs-dark-text-emphasis);
}
a:not(.dropdown-item, .dropdown-toggle, .dropdown-link, .btn, .page-link), .btn-link {
@@ -26,10 +26,10 @@ i.fa-solid, i.fa-regular, i.fa-solid {
}
pre, .output-element {
- background-color: #FAFAFA;
+ background-color: var(--bs-light-bg-subtle);
margin: 0;
padding: .25rem!important;
- border: 1px solid #CCCCCC;
+ border: 1px solid var(--bs-border-color-translucent);
}
span.caret {
@@ -50,14 +50,14 @@ span.caret {
.progress {
margin: 0;
- border: 1px solid #CCCCCC;
+ border: 1px solid var(--bs-border-color-translucent);
padding: 0.125rem !important;
height: 1.25rem !important;
.progress-bar {
line-height: initial;
min-width: 2em;
- color: white;
+ color: var(--bs-white);
}
}
@@ -96,10 +96,17 @@ span.caret {
.flash {
font-size: 100%;
+}
- a, a:hover {
- color: white;
- font-weight: bold;
+html[data-bs-theme="dark"] {
+ .alert .alert-link:hover {
+ filter: brightness(135%);
+ }
+}
+
+html[data-bs-theme="light"] {
+ .alert .alert-link:hover {
+ filter: brightness(175%);
}
}
@@ -110,7 +117,7 @@ span.caret {
.spinner {
width: 40px;
height: 40px;
- background-color: #333;
+ background-color: var(--bs-body-color);
margin: 100px auto;
-webkit-animation: sk-rotateplane 1.2s infinite ease-in-out;
diff --git a/app/assets/stylesheets/bootstrap-dropdown-submenu.css.scss b/app/assets/stylesheets/bootstrap-dropdown-submenu.css.scss
index ce930c28..562f77ce 100644
--- a/app/assets/stylesheets/bootstrap-dropdown-submenu.css.scss
+++ b/app/assets/stylesheets/bootstrap-dropdown-submenu.css.scss
@@ -20,13 +20,13 @@
border-color: transparent;
border-style: solid;
border-width: 5px 0 5px 5px;
- border-left-color: #cccccc;
+ border-left-color: var(--bs-border-color-translucent);
margin-top: 5px;
margin-right: -10px;
}
.dropdown-submenu:hover > a:after {
- border-left-color: #ffffff;
+ border-left-color: var(--bs-dropdown-link-active-color);
}
.dropdown-submenu.float-start {
diff --git a/app/assets/stylesheets/editor.css.scss b/app/assets/stylesheets/editor.css.scss
index 38ada147..224eba2c 100644
--- a/app/assets/stylesheets/editor.css.scss
+++ b/app/assets/stylesheets/editor.css.scss
@@ -6,8 +6,11 @@
.own-editor {
height: 100%;
width: 100%;
- .ace_scroller .ace_content {
- background: #FAFAFA;
+}
+
+html[data-bs-theme="light"] {
+ .own-editor .ace_scroller .ace_content {
+ background-color: var(--bs-secondary-border-subtle);
}
}
@@ -51,7 +54,7 @@
}
#editor-buttons {
- background-color: #008CBA;
+ background-color: var(--bs-primary);
margin-top: 0;
width: 100%;
display: flex;
@@ -97,7 +100,7 @@
visibility: hidden;
margin-top: .2em;
height: 1.6em;
- color: #777;
+ color: var(--bs-tertiary-color);
font-size: 0.8em;
}
@@ -188,7 +191,7 @@
#error-hints {
display: none;
- background-color: #FAFAFA;
+ background-color: var(--bs-light-bg-subtle);
.heading {
font-weight: bold;
diff --git a/app/assets/stylesheets/exercise_collections.scss b/app/assets/stylesheets/exercise_collections.scss
index 1b3c6c76..681812f2 100644
--- a/app/assets/stylesheets/exercise_collections.scss
+++ b/app/assets/stylesheets/exercise_collections.scss
@@ -1,7 +1,7 @@
-$time-color: #008cba;
-$min-color: #8efa00;
-$avg-color: #ffca00;
-$max-color: #ff2600;
+$time-color: var(--bs-blue);
+$min-color: var(--bs-yellow);
+$avg-color: var(--bs-teal);
+$max-color: var(--bs-red);
path.line.minimum-working-time {
stroke: $min-color;
@@ -31,7 +31,7 @@ rect.value-bar {
.box {
width: 20px;
height: 20px;
- border: solid 1px #000;
+ border: solid 1px var(--bs-emphasis-color);
}
.box.time {
@@ -62,8 +62,8 @@ rect.value-bar {
display: none;
min-width: 80px;
height: auto;
- background: none repeat scroll 0 0 #ffffff;
- border: 1px solid #008cba;
+ background: none repeat scroll 0 0 var(--bs-body-bg);
+ border: 1px solid var(--bs-primary);
padding: 14px;
text-align: center;
}
diff --git a/app/assets/stylesheets/exercises.css.scss b/app/assets/stylesheets/exercises.css.scss
index a2c0e8af..94a9a11c 100644
--- a/app/assets/stylesheets/exercises.css.scss
+++ b/app/assets/stylesheets/exercises.css.scss
@@ -1,5 +1,5 @@
code {
- background-color: #F8F8F8 !important;
+ background-color: var(--bs-light-bg-subtle) !important;
max-height: 100px;
overflow: scroll;
}
@@ -14,7 +14,7 @@ input[type='file'] {
.exercise {
border-radius: 3px;
- box-shadow: 0 2px 4px 0 rgba(0,0,0,0.2);
+ box-shadow: 0 2px 4px 0 rgba(var(--bs-black-rgb), 0.2);
padding: 1px 10px 1px 10px;
margin-bottom: 10px;
@@ -50,15 +50,17 @@ input[type='file'] {
// Graph Settings
+text.axis, {
+ fill: var(--bs-body-color);
+}
+
.axis path {
fill: none;
- stroke: #100;
shape-rendering: crispEdges;
}
.axis line {
fill: none;
- stroke: #999;
- //shape-rendering: crispEdges;
+ stroke: var(--bs-tertiary-color);
}
.y.axis path {
@@ -77,29 +79,29 @@ input[type='file'] {
}
div#chart_1 {
- background-color: #FAFAFA;
+ background-color: var(--bs-light-bg-subtle);
}
div#chart_2 {
- background-color: #FAFAFA;
+ background-color: var(--bs-light-bg-subtle);
}
div#chart_stacked {
max-height: 500px;
- background-color: #FAFAFA;
+ background-color: var(--bs-light-bg-subtle);
}
a.file-heading {
- color: black !important;
+ color: var(--bs-body-color) !important;
text-decoration: none;
}
.bar {
- fill: orange;
+ fill: var(--bs-warning);
}
.bar:hover {
- fill: #ffd897;
+ fill: var(--bs-warning-border-subtle);
}
.container > form > .actions {
@@ -110,8 +112,8 @@ a.file-heading {
line-height: 1;
font-weight: bold;
padding: 12px;
- background: rgba(0, 0, 0, 0.8);
- color: #fff;
+ background: rgba(var(--bs-black-rgb), 0.8);
+ color: var(--bs-white);
border-radius: 2px;
}
@@ -122,7 +124,7 @@ a.file-heading {
font-size: 14px;
width: 100%;
line-height: 1;
- color: rgba(0, 0, 0, 0.8);
+ color: rgba(var(--bs-black-rgb), 0.8);
content: "\25BC";
position: absolute;
text-align: center;
@@ -142,7 +144,7 @@ a.file-heading {
}
.value {
- border: 1px solid grey;
+ border: 1px solid var(--bs-border-color-translucent);
padding: 10px;
margin-bottom: 10px;
}
@@ -216,11 +218,11 @@ a.file-heading {
}
.export-success {
- color: darkgreen;
+ color: var(--bs-success);
font-size: 12pt;
font-weight: 600;
}
.export-failure {
- color: darkred;
+ color: var(--bs-danger);
}
diff --git a/app/assets/stylesheets/forms.css.scss b/app/assets/stylesheets/forms.css.scss
index fd797034..f072c5e1 100644
--- a/app/assets/stylesheets/forms.css.scss
+++ b/app/assets/stylesheets/forms.css.scss
@@ -31,3 +31,7 @@
.toggle-input {
font-size: 80%;
}
+
+.wmd-preview {
+ background-color: var(--bs-secondary-bg);
+}
diff --git a/app/assets/stylesheets/request-for-comments.css.scss b/app/assets/stylesheets/request-for-comments.css.scss
index 2b34915e..c8dadf92 100644
--- a/app/assets/stylesheets/request-for-comments.css.scss
+++ b/app/assets/stylesheets/request-for-comments.css.scss
@@ -1,7 +1,7 @@
.rfc {
h5 {
- color: #008CBA;
+ color: var(--bs-primary);
}
.text {
@@ -25,8 +25,8 @@
.text {
padding: 5px;
- background-color: #FAFAFA;
- border: 1px solid #CCCCCC;
+ background-color: var(--bs-light-bg-subtle);
+ border: 1px solid var(--bs-border-color-translucent);
}
}
@@ -44,8 +44,8 @@
.text {
padding: 5px;
- background-color: #FAFAFA;
- border: 1px solid #CCCCCC;
+ background-color: var(--bs-light-bg-subtle);
+ border: 1px solid var(--bs-border-color-translucent);
}
pre {
@@ -77,26 +77,26 @@
.passed {
border-radius: 50%;
- background-color: #8efa00;
- -webkit-box-shadow: 0 0 11px 1px rgba(44,222,0,1);
- -moz-box-shadow: 0 0 11px 1px rgba(44,222,0,1);
- box-shadow: 0 0 11px 1px rgba(44,222,0,1);
+ background-color: var(--bs-success);
+ -webkit-box-shadow: 0 0 11px 1px rgba(var(--bs-success-rgb), 1);
+ -moz-box-shadow: 0 0 11px 1px rgba(var(--bs-success-rgb), 1);
+ box-shadow: 0 0 11px 1px rgba(var(--bs-success-rgb), 1);
}
.unknown {
border-radius: 50%;
- background-color: #ffca00;
- -webkit-box-shadow: 0 0 11px 1px rgb(255, 202, 0);
- -moz-box-shadow: 0 0 11px 1px rgb(255, 202, 0);
- box-shadow: 0 0 11px 1px rgb(255, 202, 0);
+ background-color: var(--bs-warning);
+ -webkit-box-shadow: 0 0 11px 1px rgba(var(--bs-warning-rgb), 1);
+ -moz-box-shadow: 0 0 11px 1px rgba(var(--bs-warning-rgb), 1);
+ box-shadow: 0 0 11px 1px rgba(var(--bs-warning-rgb), 1);
}
.failed {
border-radius: 50%;
- background-color: #ff2600;
- -webkit-box-shadow: 0 0 11px 1px rgba(222,0,0,1);
- -moz-box-shadow: 0 0 11px 1px rgba(222,0,0,1);
- box-shadow: 0 0 11px 1px rgba(222,0,0,1);
+ background-color: var(--bs-danger);
+ -webkit-box-shadow: 0 0 11px 1px rgba(var(--bs-danger-rgb), 1);
+ -moz-box-shadow: 0 0 11px 1px rgba(var(--bs-danger-rgb), 1);
+ box-shadow: 0 0 11px 1px rgba(var(--bs-danger-rgb), 1);
}
}
@@ -109,8 +109,8 @@
display: none;
margin-top: 20px;
padding: 5px;
- border: solid lightgrey 1px;
- background-color: rgba(20, 180, 20, 0.2);
+ border: solid var(--bs-border-color-translucent) 1px;
+ background-color: rgba(var(--bs-success-rgb),0.2);
border-radius: 4px;
button {
@@ -127,7 +127,12 @@
#commentitor {
margin-bottom: 2rem;
height: 600px;
- background-color:#f9f9f9
+}
+
+html[data-bs-theme="light"] {
+ #commentitor {
+ background-color: var(--bs-secondary-border-subtle);
+ }
}
:not(.allow_ace_tooltip) > .ace_tooltip {
@@ -180,7 +185,7 @@
.comment-date {
text-align: right;
- color: #008cba;
+ color: var(--bs-primary);
margin-left: 60%;
font-size: x-small;
}
@@ -212,7 +217,7 @@
.comment-divider {
width: 100%;
height: 1px;
- background-color: #008cba;
+ background-color: var(--bs-primary);
overflow: hidden;
margin-top: 10px;
margin-bottom: 10px;
@@ -226,7 +231,7 @@
.container {
width: 100%;
overflow-y: auto;
- border: 1px solid #cccccc;
+ border: 1px solid var(--bs-border-color-translucent);
padding: 15px;
.comment-removed {
@@ -264,14 +269,10 @@ input#subscribe {
}
.popover-footer {
- color: #008cba;
+ color: var(--bs-primary);
margin-top: 10px;
}
-.do-not-answer {
- background-color: #ea2f1085;
-}
-
#q_submission_study_group_id_in_chosen {
margin-right: 20px;
}
diff --git a/app/assets/stylesheets/statistics.css.scss b/app/assets/stylesheets/statistics.css.scss
index 40852ac4..65668595 100644
--- a/app/assets/stylesheets/statistics.css.scss
+++ b/app/assets/stylesheets/statistics.css.scss
@@ -36,51 +36,54 @@ div.unit-test-result {
div.positive-result {
border-radius: 50%;
- background-color: #8efa00;
- -webkit-box-shadow: 0px 0px 11px 1px rgba(44,222,0,1);
- -moz-box-shadow: 0px 0px 11px 1px rgba(44,222,0,1);
- box-shadow: 0px 0px 11px 1px rgba(44,222,0,1);
+ background-color: var(--bs-success);
+ -webkit-box-shadow: 0px 0px 11px 1px rgba(var(--bs-success-rgb), 1);
+ -moz-box-shadow: 0px 0px 11px 1px rgba(var(--bs-success-rgb), 1);
+ box-shadow: 0px 0px 11px 1px rgba(var(--bs-success-rgb), 1);
}
div.unknown-result {
border-radius: 50%;
- background-color: #ffca00;
- -webkit-box-shadow: 0px 0px 11px 1px rgb(255, 202, 0);
- -moz-box-shadow: 0px 0px 11px 1px rgb(255, 202, 0);
- box-shadow: 0px 0px 11px 1px rgb(255, 202, 0);
+ background-color: var(--bs-warning);
+ -webkit-box-shadow: 0px 0px 11px 1px rgba(var(--bs-warning-rgb), 1);
+ -moz-box-shadow: 0px 0px 11px 1px rgba(var(--bs-warning-rgb), 1);
+ box-shadow: 0px 0px 11px 1px rgba(var(--bs-warning-rgb), 1);
}
div.negative-result {
border-radius: 50%;
- background-color: #ff2600;
- -webkit-box-shadow: 0px 0px 11px 1px rgba(222,0,0,1);
- -moz-box-shadow: 0px 0px 11px 1px rgba(222,0,0,1);
- box-shadow: 0px 0px 11px 1px rgba(222,0,0,1);
+ background-color: var(--bs-danger);
+ -webkit-box-shadow: 0px 0px 11px 1px rgba(var(--bs-danger-rgb), 1);
+ -moz-box-shadow: 0px 0px 11px 1px rgba(var(--bs-danger-rgb), 1);
+ box-shadow: 0px 0px 11px 1px rgba(var(--bs-danger-rgb), 1);
}
-tr.active {
- filter: brightness(85%);
- color: #000000;
+html[data-bs-theme="dark"] {
+ tr.active {
+ filter: brightness(175%);
+ }
}
-tr:not(.before_deadline,.within_grace_period,.after_late_deadline) {
- background-color: #ffffff;
+html[data-bs-theme="light"] {
+ tr.active {
+ filter: brightness(85%);
+ }
}
tr.highlight {
- border-top: 2px solid rgba(222,0,0,1);
+ border-top: 2px solid var(--bs-red);
}
-.before_deadline {
- background-color: #DAF7A6;
+.before_deadline, .before_deadline > * {
+ background-color: var(--bs-success-bg-subtle) !important;
}
-.within_grace_period {
- background-color: #F7DC6F;
+.within_grace_period, .within_grace_period > * {
+ background-color: var(--bs-warning-bg-subtle) !important;
}
-.after_late_deadline {
- background-color: #EC7063;
+.after_late_deadline, .after_late_deadline > * {
+ background-color: var(--bs-danger-bg-subtle) !important;
}
/////////////////////////////////////////////////////////////////////////////////////////////
@@ -97,13 +100,13 @@ tr.highlight {
grid-gap: 10px;
> a {
- color: #fff !important;
+ color: var(--bs-white) !important;
text-decoration: none !important;
> div {
- border: 2px solid #0055ba;
+ border: 2px solid var(--bs-primary-text-emphasis);
border-radius: 5px;
- background-color: #008cba;
+ background-color: var(--bs-primary);
padding: 1em;
display: flex;
flex-flow: column-reverse;
diff --git a/app/javascript/application.js b/app/javascript/application.js
index fc35b9cc..5310cb64 100644
--- a/app/javascript/application.js
+++ b/app/javascript/application.js
@@ -37,7 +37,9 @@ window.SentryUtils = { dynamicSamplingContextToSentryBaggageHeader, startIdleTra
// CSS
import 'chosen-js/chosen.css';
+import 'chosen-dark.scss';
import 'jstree/dist/themes/default/style.min.css';
+import 'jstree/dist/themes/default-dark/style.min.css';
// custom jquery-ui library for minimal mouse interaction support
import 'jquery-ui/ui/widget'
diff --git a/app/javascript/chosen-dark.scss b/app/javascript/chosen-dark.scss
new file mode 100644
index 00000000..909f0f1f
--- /dev/null
+++ b/app/javascript/chosen-dark.scss
@@ -0,0 +1,140 @@
+/*!
+Chosen, a Select Box Enhancer for jQuery and Prototype
+by Patrick Filler for Harvest, http://getharvest.com
+
+Version 1.7.0
+Full source at https://github.com/harvesthq/chosen
+Copyright (c) 2011-2017 Harvest http://getharvest.com
+
+MIT License, https://github.com/harvesthq/chosen/blob/master/LICENSE.md
+This file is generated by `grunt build`, do not edit it by hand.
+*/
+
+/*
+Modified to dark version by Daniel Ziegler https://daniel-ziegler.com
+MIT License
+Full source at https://github.com/nook24/chosen-dark
+*/
+
+/*
+Changed to work in conjunction with Bootstrap 5 for CodeOcean
+*/
+
+html[data-bs-theme="dark"] {
+
+ .chosen-container .chosen-drop {
+ border-color: #333;
+ background: #212121;
+ box-shadow: 0 4px 5px rgba(0, 0, 0, 0.15);
+ }
+
+
+ /* @end */
+ /* @group Single Chosen */
+ .chosen-container-single .chosen-single {
+ border-color: #333;
+ background: #212121;
+ background-image: linear-gradient(#353535 1%, #212121 15%);
+ box-shadow: 0 0 2px #5d5d5d inset, 0 1px 0 rgba(0, 0, 0, 0.05);
+ color: #e3e3e3;
+ }
+
+ .chosen-container-single .chosen-default {
+ color: #999;
+ }
+
+ .chosen-container-single .chosen-search input[type="text"] {
+ border-color: #333;
+ color: #e3e3e3;
+ }
+
+ /* @end */
+ /* @group Results */
+ .chosen-container .chosen-results {
+ color: #e3e3e3;
+ }
+
+
+ .chosen-container .chosen-results li.disabled-result {
+ color: #505050;
+ }
+
+ .chosen-container .chosen-results li.highlighted {
+ background-color: #3875d7;
+ background-image: linear-gradient(#3875d7 20%, #2a62bc 90%);
+ color: #fff;
+ }
+
+ .chosen-container .chosen-results li.no-results {
+ color: #e3e3e3;
+ background: #1f1d1d;
+ }
+
+
+ /* @end */
+ /* @group Multi Chosen */
+ .chosen-container-multi .chosen-choices {
+ border-color: #333;
+ background: #212121;
+ background-image: linear-gradient(#353535 1%, #212121 15%);
+ }
+
+ .chosen-container-multi .chosen-choices li.search-field input[type="text"] {
+ color: #e3e3e3;
+ }
+
+ .chosen-container-multi .chosen-choices li.search-choice {
+ border-color: #000;
+ background-color: #212121;
+ background-image: linear-gradient(#353535 1%, #212121 15%);
+ box-shadow: 0 0 2px #5d5d5d inset, 0 1px 0 rgba(0, 0, 0, 0.05);
+ color: #e3e3e3;
+ }
+
+
+ .chosen-container-multi .chosen-choices li.search-choice-disabled {
+ border: 1px solid #ccc;
+ background-color: #e4e4e4;
+ background-image: linear-gradient(#f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eee 100%);
+ color: #666;
+ }
+
+ .chosen-container-multi .chosen-choices li.search-choice-focus {
+ background: #d4d4d4;
+ }
+
+
+ .chosen-container-multi .chosen-drop .result-selected {
+ color: #505050;
+ }
+
+ /* @end */
+ /* @group Active */
+ .chosen-container-active .chosen-single {
+ border: 1px solid #5897fb;
+ box-shadow: 0 0 5px rgba(0, 0, 0, 0.3);
+ }
+
+ .chosen-container-active.chosen-with-drop .chosen-single {
+ border-color: #333;
+ background-image: linear-gradient(#353535 1%, #212121 15%);
+ box-shadow: 0 1px 0 rgba(0, 0, 0, 0.3) inset;
+ }
+
+
+ .chosen-container-active .chosen-choices {
+ border: 1px solid #5897fb;
+ box-shadow: 0 0 5px rgba(0, 0, 0, 0.3);
+ }
+
+ .chosen-container-active .chosen-choices li.search-field input[type="text"] {
+ color: #e3e3e3 !important;
+ }
+
+ /* @end */
+ /* @group Disabled Support */
+ .chosen-disabled {
+ opacity: 0.5 !important;
+ }
+ /* @end */
+}
diff --git a/app/javascript/highlight.js b/app/javascript/highlight.js
index 375a92e1..9ee2a8b6 100644
--- a/app/javascript/highlight.js
+++ b/app/javascript/highlight.js
@@ -5,6 +5,3 @@ import hljs from 'highlight.js/lib/common'
import julia from 'highlight.js/lib/languages/julia';
hljs.registerLanguage('julia', julia);
window.hljs = hljs;
-
-// CSS
-import 'highlight.js/styles/base16/tomorrow.css'
diff --git a/app/javascript/highlight.scss b/app/javascript/highlight.scss
new file mode 100644
index 00000000..82e8a328
--- /dev/null
+++ b/app/javascript/highlight.scss
@@ -0,0 +1,9 @@
+// CSS for highlight.js
+
+html[data-bs-theme="light"] {
+ @import 'highlight.js/styles/base16/tomorrow';
+}
+
+html[data-bs-theme="dark"] {
+ @import 'highlight.js/styles/base16/tomorrow-night';
+}
diff --git a/app/javascript/stylesheets.scss b/app/javascript/stylesheets.scss
index 5a8fc14a..a57349e6 100644
--- a/app/javascript/stylesheets.scss
+++ b/app/javascript/stylesheets.scss
@@ -11,6 +11,29 @@ $web-font-path: '//';
@import '~bootswatch/dist/yeti/variables';
@import '~bootstrap/scss/bootstrap';
@import '~bootswatch/dist/yeti/bootswatch';
+
+// We define our own button style here, since `btn-outline-dark` and `btn-outline-light` do not switch colors.
+html[data-bs-theme="dark"] {
+ .btn-outline-contrast {
+ @extend .btn-outline-light;
+ }
+
+ .bg-contrast {
+ @extend .bg-light;
+ }
+}
+
+
+html[data-bs-theme="light"] {
+ .btn-outline-contrast {
+ @extend .btn-outline-dark;
+ }
+
+ .bg-contrast {
+ @extend .bg-dark;
+ }
+}
+
$fa-font-path: '~@fortawesome/fontawesome-free/webfonts/';
@import '~@fortawesome/fontawesome-free/scss/fontawesome';
@import '~@fortawesome/fontawesome-free/scss/solid';
diff --git a/app/javascript/vis.js b/app/javascript/vis.js
index 8e81ed90..5dee8d25 100644
--- a/app/javascript/vis.js
+++ b/app/javascript/vis.js
@@ -3,6 +3,3 @@
// JS
import * as vis from 'vis';
window.vis = vis;
-
-// CSS
-import 'vis-timeline/dist/vis-timeline-graph2d.css';
diff --git a/app/javascript/vis.scss b/app/javascript/vis.scss
new file mode 100644
index 00000000..0b157803
--- /dev/null
+++ b/app/javascript/vis.scss
@@ -0,0 +1,19 @@
+// CSS
+
+@import 'vis-timeline/dist/vis-timeline-graph2d';
+
+html[data-bs-theme="dark"] {
+ // Color overwrites for dark mode
+
+ .vis-legend {
+ background-color: rgba(var(--bs-secondary-bg-rgb), 0.65) !important;
+ }
+
+ .vis-timeline .vis-outline {
+ fill: var(--bs-body-bg) !important;
+ }
+
+ .vis-data-axis .vis-major, .vis-time-axis .vis-text {
+ color: var(--bs-secondary-text-emphasis) !important;
+ }
+}
diff --git a/app/views/application/_breadcrumbs_and_title.html.slim b/app/views/application/_breadcrumbs_and_title.html.slim
index ff5d1671..730a54b1 100644
--- a/app/views/application/_breadcrumbs_and_title.html.slim
+++ b/app/views/application/_breadcrumbs_and_title.html.slim
@@ -20,7 +20,7 @@
- title = "#{active_action} - #{application_name}"
- content_for :breadcrumbs do
.container.mb-4
- ul.breadcrumb.bg-light.px-3.py-2
+ ul.breadcrumb.bg-body-secondary.px-3.py-2
- if root_element.present?
li.breadcrumb-item.small
= root_element
diff --git a/app/views/application/_color_mode_selector.html.slim b/app/views/application/_color_mode_selector.html.slim
new file mode 100644
index 00000000..8b93e512
--- /dev/null
+++ b/app/views/application/_color_mode_selector.html.slim
@@ -0,0 +1,17 @@
+li.nav-item.dropdown
+ a.nav-link.dropdown-toggle.me-3 data-bs-toggle='dropdown' href='#'
+ = t('shared.color_mode.title')
+ span.caret
+ ul.dropdown-menu.p-0.mt-1 role='menu'
+ li
+ button.dropdown-item.d-flex.align-items-center data={ 'bs-theme-value': 'light' }
+ i.fa-fw.fa-solid.fa-sun
+ = t('shared.color_mode.light')
+ li
+ button.dropdown-item.d-flex.align-items-center data={ 'bs-theme-value': 'dark' }
+ i.fa-fw.fa-solid.fa-moon
+ = t('shared.color_mode.dark')
+ li
+ button.dropdown-item.d-flex.align-items-center data={ 'bs-theme-value': 'auto' }
+ i.fa-fw.fa-solid.fa-wand-magic-sparkles
+ = t('shared.color_mode.auto')
diff --git a/app/views/application/_locale_selector.html.slim b/app/views/application/_locale_selector.html.slim
index 51a41677..a6332186 100644
--- a/app/views/application/_locale_selector.html.slim
+++ b/app/views/application/_locale_selector.html.slim
@@ -1,5 +1,5 @@
li.nav-item.dropdown
- a.nav-link.dropdown-toggle.mx-3 data-bs-toggle='dropdown' href='#'
+ a.nav-link.dropdown-toggle.me-3 data-bs-toggle='dropdown' href='#'
= t("locales.#{I18n.locale}")
span.caret
ul.dropdown-menu.p-0.mt-1 role='menu'
diff --git a/app/views/community_solutions/_form.html.slim b/app/views/community_solutions/_form.html.slim
index 480bf004..7f59b4f2 100644
--- a/app/views/community_solutions/_form.html.slim
+++ b/app/views/community_solutions/_form.html.slim
@@ -42,7 +42,7 @@
= render('exercises/editor_frame', exercise: @community_solution.exercise, file: file)
.col-xl-6.container-fluid
- div.bg-dark.h-100.float-start.row style="width: 1px"
+ div.bg-contrast.h-100.float-start.row style="width: 1px"
div
h4
= t('community_solutions.your_submission')
diff --git a/app/views/consumers/show.html.slim b/app/views/consumers/show.html.slim
index c5acac9d..b5a3a12e 100644
--- a/app/views/consumers/show.html.slim
+++ b/app/views/consumers/show.html.slim
@@ -5,7 +5,7 @@ h1
= row(label: 'consumer.name', value: @consumer.name)
- %w[oauth_key oauth_secret].each do |attribute|
= row(label: "consumer.#{attribute}") do
- = content_tag(:input, nil, class: 'form-control bg-secondary', readonly: true, value: @consumer.send(attribute))
+ = content_tag(:input, nil, class: 'form-control bg-body-secondary', readonly: true, value: @consumer.send(attribute))
= row(label: 'consumer.rfc_visibility', value: t("activerecord.attributes.consumer.rfc_visibility_type.#{@consumer.rfc_visibility}"))
= render('study_groups/table', study_groups: @consumer.study_groups.sort)
diff --git a/app/views/execution_environments/show.html.slim b/app/views/execution_environments/show.html.slim
index 53537db0..31b506b6 100644
--- a/app/views/execution_environments/show.html.slim
+++ b/app/views/execution_environments/show.html.slim
@@ -3,10 +3,10 @@ h1.d-inline-block = @execution_environment
= render('shared/edit_button', object: @execution_environment)
button.btn.btn-secondary.float-end.dropdown-toggle data-bs-toggle='dropdown' type='button'
ul.dropdown-menu.dropdown-menu-end role='menu'
- li = link_to(t('execution_environments.index.synchronize.button'), sync_to_runner_management_execution_environment_path(@execution_environment), method: :post, class: 'dropdown-item text-dark') if policy(@execution_environment).sync_to_runner_management?
- li = link_to(t('execution_environments.index.shell'), shell_execution_environment_path(@execution_environment), class: 'dropdown-item text-dark') if policy(@execution_environment).shell?
- li = link_to(t('shared.statistics'), statistics_execution_environment_path(@execution_environment), 'data-turbolinks' => "false", class: 'dropdown-item text-dark') if policy(@execution_environment).statistics?
- li = link_to(t('shared.destroy'), @execution_environment, data: {confirm: t('shared.confirm_destroy')}, method: :delete, class: 'dropdown-item text-dark') if policy(@execution_environment).destroy?
+ li = link_to(t('execution_environments.index.synchronize.button'), sync_to_runner_management_execution_environment_path(@execution_environment), method: :post, class: 'dropdown-item') if policy(@execution_environment).sync_to_runner_management?
+ li = link_to(t('execution_environments.index.shell'), shell_execution_environment_path(@execution_environment), class: 'dropdown-item') if policy(@execution_environment).shell?
+ li = link_to(t('shared.statistics'), statistics_execution_environment_path(@execution_environment), 'data-turbolinks' => "false", class: 'dropdown-item') if policy(@execution_environment).statistics?
+ li = link_to(t('shared.destroy'), @execution_environment, data: {confirm: t('shared.confirm_destroy')}, method: :delete, class: 'dropdown-item') if policy(@execution_environment).destroy?
= row(label: 'execution_environment.name', value: @execution_environment.name)
= row(label: 'execution_environment.user', value: link_to_if(policy(@execution_environment.author).show?, @execution_environment.author, @execution_environment.author))
diff --git a/app/views/exercises/_editor.html.slim b/app/views/exercises/_editor.html.slim
index dfd3f477..8143e873 100644
--- a/app/views/exercises/_editor.html.slim
+++ b/app/views/exercises/_editor.html.slim
@@ -35,8 +35,8 @@
#statusbar.d-flex.justify-content-between
div
- - if !@embed_options[:disable_download] && @exercise.hide_file_tree?
- button#download.p-0.border-0.btn-link.visible.bg-white.text-primary
+ - if !@embed_options[:disable_download] && !@exercise.hide_file_tree?
+ button#download.p-0.border-0.btn-link.visible.bg-body.text-primary
i.fa-solid.fa-arrow-down
= t('exercises.editor.download')
@@ -47,7 +47,7 @@
= " | "
- button#start-over-active-file.p-0.border-0.btn-link.bg-white.text-primary data-message-confirm=t('exercises.editor.confirm_start_over_active_file') data-url=reload_exercise_path(@exercise)
+ button#start-over-active-file.p-0.border-0.btn-link.bg-body.text-primary data-message-confirm=t('exercises.editor.confirm_start_over_active_file') data-url=reload_exercise_path(@exercise)
i.fa-solid.fa-circle-notch.fa-spin.d-none
i.fa-solid.fa-clock-rotate-left
= t('exercises.editor.start_over_active_file')
diff --git a/app/views/exercises/_editor_file_tree.html.slim b/app/views/exercises/_editor_file_tree.html.slim
index f9d83300..34fc202b 100644
--- a/app/views/exercises/_editor_file_tree.html.slim
+++ b/app/views/exercises/_editor_file_tree.html.slim
@@ -1,11 +1,11 @@
div.d-grid.gap-2 id='sidebar-collapsed' class=(@exercise.hide_file_tree && @tips.blank? ? '' : 'd-none')
- = render('editor_button', classes: 'btn-outline-dark', data: {:'data-bs-toggle' => 'tooltip', :'data-bs-placement' => 'right'}, icon: 'fa-solid fa-square-plus', id: 'sidebar-collapse-collapsed', label:'', title:t('exercises.editor.expand_action_sidebar'))
+ = render('editor_button', classes: 'btn-outline-contrast', data: {:'data-bs-toggle' => 'tooltip', :'data-bs-placement' => 'right'}, icon: 'fa-solid fa-square-plus', id: 'sidebar-collapse-collapsed', label:'', title:t('exercises.editor.expand_action_sidebar'))
- unless @embed_options[:disable_hints] or @tips.blank?
= render('editor_button', classes: 'btn-secondary btn mb-4', data: {:'data-bs-toggle' => 'tooltip', :'data-bs-placement' => 'right'}, icon: 'fa-solid fa-lightbulb', id: 'tips-collapsed', label:'', title: t('exercises.form.tips'))
div.d-grid.enforce-bottom-margin id='sidebar-uncollapsed' class=(@exercise.hide_file_tree && @tips.blank? ? 'd-none' : '')
- = render('editor_button', classes: 'btn-outline-dark overflow-hidden mb-2', icon: 'fa-solid fa-square-minus', id: 'sidebar-collapse', label: t('exercises.editor.collapse_action_sidebar'))
+ = render('editor_button', classes: 'btn-outline-contrast overflow-hidden mb-2', icon: 'fa-solid fa-square-minus', id: 'sidebar-collapse', label: t('exercises.editor.collapse_action_sidebar'))
#content-left-sidebar.overflow-scroll
- unless @exercise.hide_file_tree
div.overflow-scroll
diff --git a/app/views/exercises/_editor_output.html.slim b/app/views/exercises/_editor_output.html.slim
index 0d4b3bd6..d7c7ac47 100644
--- a/app/views/exercises/_editor_output.html.slim
+++ b/app/views/exercises/_editor_output.html.slim
@@ -1,7 +1,7 @@
div.d-grid id='output_sidebar_collapsed'
- = render('editor_button', classes: 'btn-outline-dark btn', data: {:'data-bs-toggle' => 'tooltip', :'data-bs-placement' => 'left'}, title: t('exercises.editor.expand_output_sidebar'), icon: 'fa-solid fa-square-plus', id: 'toggle-sidebar-output-collapsed', label: '')
+ = render('editor_button', classes: 'btn-outline-contrast btn', data: {:'data-bs-toggle' => 'tooltip', :'data-bs-placement' => 'left'}, title: t('exercises.editor.expand_output_sidebar'), icon: 'fa-solid fa-square-plus', id: 'toggle-sidebar-output-collapsed', label: '')
div.d-grid id='output_sidebar_uncollapsed' class='d-none col-sm-12 enforce-bottom-margin' data-message-no-output=t('exercises.implement.no_output_yet')
- = render('editor_button', classes: 'btn-outline-dark btn overflow-hidden mb-2', icon: 'fa-solid fa-square-minus', id: 'toggle-sidebar-output', label: t('exercises.editor.collapse_output_sidebar'))
+ = render('editor_button', classes: 'btn-outline-contrast btn overflow-hidden mb-2', icon: 'fa-solid fa-square-minus', id: 'toggle-sidebar-output', label: t('exercises.editor.collapse_output_sidebar'))
#content-right-sidebar.overflow-scroll
@@ -17,7 +17,7 @@ div.d-grid id='output_sidebar_uncollapsed' class='d-none col-sm-12 enforce-botto
li.card.mt-2
.card-header.py-2
h5.card-title.m-0 == t('exercises.implement.test_file', filename: '', number: 0)
- .card-body.bg-white.text-dark
+ .card-body
= row(label: 'exercises.implement.passed_tests') do
span.number
| 0
@@ -37,7 +37,7 @@ div.d-grid id='output_sidebar_uncollapsed' class='d-none col-sm-12 enforce-botto
li.card.mt-2
.card-header.py-2
h5.card-title.m-0 == t('exercises.implement.linter_file', filename: '', number: 0)
- .card-body.bg-white.text-dark
+ .card-body
= row(label: 'exercises.implement.code_rating') do
span.number
| 0
@@ -84,6 +84,6 @@ div.d-grid id='output_sidebar_uncollapsed' class='d-none col-sm-12 enforce-botto
#output
.output-element.overflow-scroll = t('exercises.implement.no_output_yet')
- if CodeOcean::Config.new(:code_ocean).read[:flowr][:enabled] && !@embed_options[:disable_hints] && !@embed_options[:hide_test_results]
- #flowrHint.mb-2.card.text-white.bg-info data-url=CodeOcean::Config.new(:code_ocean).read[:flowr][:url] role='tab'
- .card-header = t('exercises.implement.flowr.heading')
- .card-body.text-dark.bg-white
+ #flowrHint.mb-2.card data-url=CodeOcean::Config.new(:code_ocean).read[:flowr][:url] role='tab'
+ .card-header.text-white.bg-info = t('exercises.implement.flowr.heading')
+ .card-body
diff --git a/app/views/exercises/_tips_content.html.slim b/app/views/exercises/_tips_content.html.slim
index cefd7fbb..e3070e5f 100644
--- a/app/views/exercises/_tips_content.html.slim
+++ b/app/views/exercises/_tips_content.html.slim
@@ -9,5 +9,5 @@
.card-header.py-2
i.fa-solid.fa-lightbulb
= t('exercises.implement.tips.heading')
- .card-body.text-dark.bg-white.p-2
+ .card-body.p-2
= render(partial: 'tips/collapsed_card', collection: @tips, as: :exercise_tip, locals: { tip_prefix: '' })
diff --git a/app/views/exercises/external_users/statistics.html.slim b/app/views/exercises/external_users/statistics.html.slim
index 3fedb649..d6628369 100644
--- a/app/views/exercises/external_users/statistics.html.slim
+++ b/app/views/exercises/external_users/statistics.html.slim
@@ -40,17 +40,17 @@ h1
=index
- index += 1
- if policy(@exercise).detailed_statistics?
- .bg-light.w-100.p-2.mb-4.align-items-center.d-flex.justify-content-between
+ .bg-body-secondary.w-100.p-2.mb-4.align-items-center.d-flex.justify-content-between
- if @show_autosaves
span.ps-1.pb-1
i.fa-solid.fa-circle-info.align-middle
small.me-5.ms-1 = t('.toggle_status_on')
- = link_to t('.toggle_autosave_off'), statistics_external_user_exercise_path(show_autosaves: false), class: "btn btn-outline-dark float-end btn-sm"
+ = link_to t('.toggle_autosave_off'), statistics_external_user_exercise_path(show_autosaves: false), class: "btn btn-outline-contrast float-end btn-sm"
- else
span.ps-1.pb-1
i.fa-solid.fa-circle-info.align-middle
small.me-5.ms-1 = t('.toggle_status_off')
- = link_to t('.toggle_autosave_on'), statistics_external_user_exercise_path(show_autosaves: true), class: "btn btn-outline-dark float-end btn-sm"
+ = link_to t('.toggle_autosave_on'), statistics_external_user_exercise_path(show_autosaves: true), class: "btn btn-outline-contrast float-end btn-sm"
#timeline
.table-responsive
table.table
diff --git a/app/views/exercises/show.html.slim b/app/views/exercises/show.html.slim
index f8762ed0..bdf2a95e 100644
--- a/app/views/exercises/show.html.slim
+++ b/app/views/exercises/show.html.slim
@@ -10,13 +10,13 @@ h1.d-inline-block = @exercise
= render('shared/edit_button', object: @exercise)
button.btn.btn-secondary.float-end.dropdown-toggle data-bs-toggle='dropdown' type='button'
ul.dropdown-menu.dropdown-menu-end role='menu'
- li = link_to(t('exercises.index.implement'), implement_exercise_path(@exercise), 'data-turbolinks' => "false", class: 'dropdown-item text-dark') if policy(@exercise).implement?
- li = link_to(t('shared.statistics'), statistics_exercise_path(@exercise), 'data-turbolinks' => "false", class: 'dropdown-item text-dark') if policy(@exercise).statistics?
- li = link_to(t('activerecord.models.user_exercise_feedback.other'), feedback_exercise_path(@exercise), class: 'dropdown-item text-dark') if policy(@exercise).feedback?
- li = link_to(t('activerecord.models.request_for_comment.other'), rfcs_for_exercise_path(@exercise), class: 'dropdown-item text-dark') if policy(@exercise).rfcs_for_exercise?
- li = link_to(t('shared.destroy'), @exercise, data: {confirm: t('shared.confirm_destroy')}, method: :delete, class: 'dropdown-item text-dark') if policy(@exercise).destroy?
- li = link_to(t('exercises.index.clone'), clone_exercise_path(@exercise), data: {confirm: t('shared.confirm_destroy')}, method: :post, class: 'dropdown-item text-dark') if policy(@exercise).clone?
- li = link_to(t('exercises.export_codeharbor.label'), '', class: 'dropdown-item export-start text-dark', data: {'exercise-id' => @exercise.id}) if policy(@exercise).export_external_confirm?
+ li = link_to(t('exercises.index.implement'), implement_exercise_path(@exercise), 'data-turbolinks' => "false", class: 'dropdown-item') if policy(@exercise).implement?
+ li = link_to(t('shared.statistics'), statistics_exercise_path(@exercise), 'data-turbolinks' => "false", class: 'dropdown-item') if policy(@exercise).statistics?
+ li = link_to(t('activerecord.models.user_exercise_feedback.other'), feedback_exercise_path(@exercise), class: 'dropdown-item') if policy(@exercise).feedback?
+ li = link_to(t('activerecord.models.request_for_comment.other'), rfcs_for_exercise_path(@exercise), class: 'dropdown-item') if policy(@exercise).rfcs_for_exercise?
+ li = link_to(t('shared.destroy'), @exercise, data: {confirm: t('shared.confirm_destroy')}, method: :delete, class: 'dropdown-item') if policy(@exercise).destroy?
+ li = link_to(t('exercises.index.clone'), clone_exercise_path(@exercise), data: {confirm: t('shared.confirm_destroy')}, method: :post, class: 'dropdown-item') if policy(@exercise).clone?
+ li = link_to(t('exercises.export_codeharbor.label'), '', class: 'dropdown-item export-start', data: {'exercise-id' => @exercise.id}) if policy(@exercise).export_external_confirm?
= row(label: 'exercise.title', value: @exercise.title)
= row(label: 'exercise.user', value: link_to_if(policy(@exercise.author).show?, @exercise.author, @exercise.author))
@@ -35,7 +35,7 @@ h1.d-inline-block = @exercise
= row(label: 'exercise.uuid', value: @exercise.uuid)
= row(label: 'exercise.tags', value: @exercise.exercise_tags.map{|et| "#{et.tag.name} (#{et.factor})"}.sort.join(", "))
= row(label: 'exercise.embedding_parameters', class: 'mb-4') do
- = content_tag(:input, nil, class: 'form-control bg-secondary mb-4', readonly: true, value: @exercise.unpublished? ? t('exercises.show.is_unpublished') : embedding_parameters(@exercise))
+ = content_tag(:input, nil, class: 'form-control bg-body-secondary mb-4', readonly: true, value: @exercise.unpublished? ? t('exercises.show.is_unpublished') : embedding_parameters(@exercise))
- unless @tips.blank?
.mt-2
diff --git a/app/views/exercises/statistics.html.slim b/app/views/exercises/statistics.html.slim
index a192572f..d50194a8 100644
--- a/app/views/exercises/statistics.html.slim
+++ b/app/views/exercises/statistics.html.slim
@@ -81,10 +81,10 @@ h1 = @exercise
- latest_user_submission = submissions.where(user: user).final.latest
- if latest_user_submission.present?
- if latest_user_submission.before_deadline?
- .unit-test-result.positive-result.before_deadline
+ .unit-test-result.positive-result
- elsif latest_user_submission.within_grace_period?
- .unit-test-result.unknown-result.within_grace_period
+ .unit-test-result.unknown-result
- elsif latest_user_submission.after_late_deadline?
- .unit-test-result.negative-result.after_late_deadline
+ .unit-test-result.negative-result
td = us['runs'] if policy(@exercise).detailed_statistics?
td = @exercise.average_working_time_for(user) or 0 if policy(@exercise).detailed_statistics?
diff --git a/app/views/layouts/application.html.slim b/app/views/layouts/application.html.slim
index 5174f9ed..a8ddade3 100644
--- a/app/views/layouts/application.html.slim
+++ b/app/views/layouts/application.html.slim
@@ -29,6 +29,7 @@ html lang="#{I18n.locale || I18n.default_locale}" data-default-locale="#{I18n.de
#navbar-collapse.collapse.navbar-collapse
= render('navigation', cached: true)
ul.nav.navbar-nav.ms-auto
+ = render('color_mode_selector', cached: true)
= render('locale_selector', cached: true)
li.nav-item.me-3 = link_to(t('shared.help.link'), '#modal-help', data: {'bs-toggle': 'modal'}, class: 'nav-link')
= render('session')
diff --git a/app/views/proxy_exercises/show.html.slim b/app/views/proxy_exercises/show.html.slim
index e740bbe0..275a3db2 100644
--- a/app/views/proxy_exercises/show.html.slim
+++ b/app/views/proxy_exercises/show.html.slim
@@ -9,7 +9,7 @@ h1
= row(label: 'exercise.public', value: @proxy_exercise.public?)
= row(label: 'exercise.description', value: @proxy_exercise.description)
= row(label: 'exercise.embedding_parameters', class: 'mb-4') do
- = content_tag(:input, nil, class: 'form-control bg-secondary mb-4', readonly: true, value: embedding_parameters(@proxy_exercise))
+ = content_tag(:input, nil, class: 'form-control bg-body-secondary mb-4', readonly: true, value: embedding_parameters(@proxy_exercise))
h2.mt-4 Exercises
.table-responsive
diff --git a/app/views/request_for_comments/_list_entry.html.slim b/app/views/request_for_comments/_list_entry.html.slim
index 72984276..7237bc37 100644
--- a/app/views/request_for_comments/_list_entry.html.slim
+++ b/app/views/request_for_comments/_list_entry.html.slim
@@ -3,7 +3,7 @@ tr.table-row-clickable data-id=request_for_comment.id data-href=request_for_comm
- if request_for_comment.solved?
span.fa-solid.fa-check.fa-2x.text-success aria-hidden="true"
- elsif request_for_comment.full_score_reached
- span.fa-solid.fa-check.fa-2x style="color:darkgrey" aria-hidden="true"
+ span.fa-solid.fa-check.fa-2x style="color: var(--bs-secondary-text-emphasis);" aria-hidden="true"
- else
= ''
td.text-center = request_for_comment.comments_count
diff --git a/app/views/request_for_comments/index.html.slim b/app/views/request_for_comments/index.html.slim
index bade3637..929d34e2 100644
--- a/app/views/request_for_comments/index.html.slim
+++ b/app/views/request_for_comments/index.html.slim
@@ -37,7 +37,7 @@ h1 = RequestForComment.model_name.human(count: 2)
span class="fa-solid fa-check" aria-hidden="true"
- elsif request_for_comment.full_score_reached
td
- span class="fa-solid fa-check" style="color:darkgrey" aria-hidden="true"
+ span class="fa-solid fa-check" style="color: var(--bs-secondary-text-emphasis);" aria-hidden="true"
- else
td = ''
td = link_to_if(policy(request_for_comment).show?, request_for_comment.submission.exercise.title, request_for_comment)
diff --git a/app/views/request_for_comments/show.html.slim b/app/views/request_for_comments/show.html.slim
index f39dff79..69e075e9 100644
--- a/app/views/request_for_comments/show.html.slim
+++ b/app/views/request_for_comments/show.html.slim
@@ -1,7 +1,7 @@
.list-group
h4#exercise_caption.list-group-item-heading data-exercise-id="#{@request_for_comment.exercise.id}" data-rfc-id="#{@request_for_comment.id}"
- if @request_for_comment.solved?
- span.fa-solid.fa-check aria-hidden="true"
+ span.fa-solid.fa-check.me-2 aria-hidden="true"
= link_to_if(policy(@request_for_comment.exercise).show?, @request_for_comment.exercise.title, [:implement, @request_for_comment.exercise])
p.list-group-item-text
- user = @request_for_comment.user
diff --git a/app/views/shared/_form_filters.html.slim b/app/views/shared/_form_filters.html.slim
index 533a2c82..0dfcc19d 100644
--- a/app/views/shared/_form_filters.html.slim
+++ b/app/views/shared/_form_filters.html.slim
@@ -1,4 +1,4 @@
-.card.card-body.bg-light.flex-row.container.justify-content-center
+.card.card-body.bg-body-secondary.flex-row.container.justify-content-center
= search_form_for(@search, class: 'clearfix filter-form align-items-center row g-2 w-100') do |f|
= yield(f)
.col-sm.ge-4.gy-2
diff --git a/config/locales/de.yml b/config/locales/de.yml
index 2d35ee4c..9eb33370 100644
--- a/config/locales/de.yml
+++ b/config/locales/de.yml
@@ -817,7 +817,7 @@ de:
runtime_output: "Programmausgabe"
test_results: "Testergebnisse"
sessions:
- expired: Ihre Session ist abgelaufen. Bitte laden Sie diese Seite neu bevor Sie fortfahren.
+ expired: Ihre Session ist abgelaufen. Bitte laden Sie diese Seite neu bevor Sie fortfahren.
create:
failure: Fehlerhafte E-Mail oder Passwort.
success: Sie haben sich erfolgreich angemeldet.
@@ -859,6 +859,11 @@ de:
confirm_destroy: Sind Sie sicher?
create: '%{model} erstellen'
created_at: Erstellt
+ color_mode:
+ title: Erscheinungsbild
+ light: Hell
+ dark: Dunkel
+ auto: Automatisch
destroy: Löschen
edit: Bearbeiten
actions_button: 'Andere Aktionen'
@@ -873,7 +878,7 @@ de:
privacy_policy: Datenschutzerklärung
index: Index
message_failure: Leider ist ein Fehler auf unserer Plattform aufgetreten. Bitte probieren Sie es später noch einmal.
- websocket_failure: Leider ist ein Verbindungsproblem aufgetreten. Bitte überprüfen Sie Websocket-Verbindungen mit diesem Tool und versuchen Sie es erneut.
+ websocket_failure: Leider ist ein Verbindungsproblem aufgetreten. Bitte überprüfen Sie Websocket-Verbindungen mit diesem Tool und versuchen Sie es erneut.
new: Hinzufügen
new_model: '%{model} hinzufügen'
number: Nummer
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 78495acd..b31f12f3 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -817,7 +817,7 @@ en:
runtime_output: "Runtime Output"
test_results: "Test Results"
sessions:
- expired: Your session has expired. Please reload this page before continuing.
+ expired: Your session has expired. Please reload this page before continuing.
create:
failure: Invalid email or password.
success: Successfully signed in.
@@ -859,6 +859,11 @@ en:
confirm_destroy: Are you sure?
create: 'Create %{model}'
created_at: Created At
+ color_mode:
+ title: Appearance
+ light: Light
+ dark: Dark
+ auto: Auto
destroy: Delete
edit: Edit
actions_button: 'Other actions'
@@ -873,7 +878,7 @@ en:
privacy_policy: Privacy Policy
index: Index
message_failure: 'Sorry, something went wrong.'
- websocket_failure: Sorry, a connection issue occoured. Please check WebSocket connections with this tool and try again.
+ websocket_failure: Sorry, a connection issue occoured. Please check WebSocket connections with this tool and try again.
new: Add
new_model: 'Add %{model}'
number: Number
diff --git a/lib/assets/javascripts/color_mode_picker.js b/lib/assets/javascripts/color_mode_picker.js
new file mode 100644
index 00000000..74674877
--- /dev/null
+++ b/lib/assets/javascripts/color_mode_picker.js
@@ -0,0 +1,95 @@
+/*!
+ * Color mode toggler for Bootstrap's docs (https://getbootstrap.com/)
+ * Copyright 2011-2023 The Bootstrap Authors
+ * Licensed under the Creative Commons Attribution 3.0 Unported License.
+ * Taken from https://getbootstrap.com/docs/5.3/customize/color-modes/#javascript
+ */
+
+const getStoredTheme = () => localStorage.getItem('theme')
+const setStoredTheme = theme => localStorage.setItem('theme', theme)
+
+const getPreferredTheme = () => {
+ const storedTheme = getStoredTheme()
+ if (storedTheme) {
+ return storedTheme
+ }
+
+ return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
+}
+
+const setTheme = theme => {
+ let currentTheme = theme || 'auto';
+ if (theme === 'auto') {
+ if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
+ currentTheme = 'dark'
+ } else {
+ currentTheme = 'light'
+ }
+ }
+
+ const event = new CustomEvent('theme:change', {
+ bubbles: true,
+ cancelable: false,
+ detail: {
+ preferredTheme: theme,
+ currentTheme: currentTheme,
+ }
+ })
+ document.dispatchEvent(event)
+ document.documentElement.setAttribute('data-bs-theme', currentTheme)
+}
+
+window.getCurrentTheme = () => {
+ return document.documentElement.getAttribute('data-bs-theme');
+}
+
+const showActiveTheme = (theme, focus = false) => {
+ const themeSwitcher = document.querySelector('#bd-theme')
+
+ if (!themeSwitcher) {
+ return
+ }
+
+ const themeSwitcherText = document.querySelector('#bd-theme-text')
+ const activeThemeIcon = document.querySelector('.theme-icon-active use')
+ const btnToActive = document.querySelector(`[data-bs-theme-value="${theme}"]`)
+ const svgOfActiveBtn = btnToActive.querySelector('svg use').getAttribute('href')
+
+ document.querySelectorAll('[data-bs-theme-value]').forEach(element => {
+ element.classList.remove('active')
+ element.setAttribute('aria-pressed', 'false')
+ })
+
+ btnToActive.classList.add('active')
+ btnToActive.setAttribute('aria-pressed', 'true')
+ activeThemeIcon.setAttribute('href', svgOfActiveBtn)
+ const themeSwitcherLabel = `${themeSwitcherText.textContent} (${btnToActive.dataset.bsThemeValue})`
+ themeSwitcher.setAttribute('aria-label', themeSwitcherLabel)
+
+ if (focus) {
+ themeSwitcher.focus()
+ }
+}
+
+$(document).on('turbolinks:load', function() {
+ setTheme(getPreferredTheme())
+
+ window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
+ const storedTheme = getStoredTheme()
+ if (storedTheme !== 'light' && storedTheme !== 'dark') {
+ setTheme(getPreferredTheme())
+ }
+ })
+
+ showActiveTheme(getPreferredTheme())
+
+ document.querySelectorAll('[data-bs-theme-value]')
+ .forEach(toggle => {
+ toggle.addEventListener('click', () => {
+ const theme = toggle.getAttribute('data-bs-theme-value')
+ setStoredTheme(theme)
+ setTheme(theme)
+ showActiveTheme(theme, true)
+ })
+ })
+})