Dashboard: UI polish (typography, palette, modal cleanup)

- Switch UI font to Inter, keep monospace only for code/logs/paths/
  identifiers (loaded via Google Fonts; system fallbacks)
- Refine the light/dark palette with softer borders, clearer text
  hierarchy, and a more nuanced shadow/elevation system; introduce
  --bg-elevated, --border-strong, --shadow-elevated tokens; retain the
  orange accent
- Introduce a consistent spacing scale (--space-1..6) and apply it across
  sections, modals, and controls
- Extract shared modal CSS classes (.modal-info, .modal-hint,
  .modal-prompt, .modal-field, .modal-input, .modal-select,
  .modal-textarea, .modal-actions, .btn-secondary) and strip duplicated
  inline styles from all seven modals; add an accent-colored focus ring
  for inputs/textareas and a subtle backdrop blur
- Replace the native <select> in Add Language with a filterable combobox:
  type to filter, keyboard navigation (Up/Down/Enter/Esc), substring
  highlight, click-outside to close; the typed value is validated against
  the available languages list before submission
- Update CHANGELOG accordingly

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Dominik Jain <dominik.jain@oraios-ai.de>
This commit is contained in:
authored and Dr. Dominik Jain committed 2026-05-12 16:52:13 +02:00
1 parent 2e05602895
commit d3454bc39e
4 files changed
+576 -165

No files matched your search

+5
View File
@@ -2,6 +2,11 @@
Status of the `main` branch. Changes prior to the next official version change will appear here.
* Dashboard:
- UI polish: switch UI font to Inter (with system fallbacks) and use JetBrains Mono only for code/logs/paths/identifiers; refine the light/dark palette with softer borders, clearer text hierarchy, and a more nuanced shadow/elevation system; introduce a consistent spacing scale; keep the orange accent.
- Modal markup cleanup: extract shared CSS classes (`.modal-info`, `.modal-hint`, `.modal-prompt`, `.modal-field`, `.modal-input`, `.modal-select`, `.modal-textarea`, `.modal-actions`, `.btn-secondary`) and remove duplicated inline styles from all seven modals. Inputs and textareas get an accent-colored focus ring; the modal backdrop has a subtle blur.
- Add Language: replace the native `<select>` with a filterable combobox — type to filter, keyboard navigation (Up/Down/Enter/Esc), substring highlight, click-outside to close. The typed value is validated against the available languages list before submission.
# v1.3.0 (2026-05-11)
* General:
+366 -85
View File
@@ -3,64 +3,91 @@ html {
}
:root {
/* Typography */
--font-sans: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
--font-mono: 'JetBrains Mono', 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
/* Spacing scale */
--space-1: 4px;
--space-2: 8px;
--space-3: 12px;
--space-4: 16px;
--space-5: 24px;
--space-6: 32px;
/* Light theme variables */
--bg-primary: #f5f5f5;
--bg-secondary: #ffffff;
--text-primary: #000000;
--text-secondary: #333333;
--text-muted: #666666;
--border-color: #ddd;
--bg-elevated: #ffffff;
--text-primary: #1f2328;
--text-secondary: #3f4754;
--text-muted: #6a737d;
--border-color: #e3e6ea;
--border-strong: #d0d7de;
--btn-primary: #eaa45d;
--btn-hover: #dca662;
--btn-disabled: #6c757d;
--shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
--tool-highlight: #ffff00;
--tool-highlight-text: #000000;
--log-debug: #808080;
--log-info: #000000;
--log-warning: #FF8C00;
--log-error: #FF0000;
--stats-header: #f8f9fa;
--btn-disabled: #adb5bd;
--btn-secondary-bg: #f0f2f5;
--btn-secondary-hover: #e3e6ea;
--shadow: 0 1px 2px rgba(15, 23, 42, 0.04), 0 1px 3px rgba(15, 23, 42, 0.06);
--shadow-elevated: 0 4px 12px rgba(15, 23, 42, 0.08);
--tool-highlight: #fff3bf;
--tool-highlight-text: #1f2328;
--log-debug: #8b95a1;
--log-info: #1f2328;
--log-warning: #d97706;
--log-error: #dc2626;
--stats-header: #f0f2f5;
--header-height: 150px;
--header-padding: 20px;
--header-gap-main: 25px;
--frame-padding: 25px;
--border-radius: 5px;
--header-padding: var(--space-5);
--header-gap-main: var(--space-5);
--frame-padding: var(--space-5);
--border-radius: 6px;
--border-radius-sm: 4px;
}
[data-theme="dark"] {
/* Dark theme variables */
--bg-primary: #1a1a1a;
--bg-secondary: #2d2d2d;
--text-primary: #ffffff;
--text-secondary: #e0e0e0;
--text-muted: #b0b0b0;
--border-color: #444;
--bg-elevated: #262b32;
--text-primary: #e6edf3;
--text-secondary: #c9d1d9;
--text-muted: #8b95a1;
--border-color: #2d333b;
--border-strong: #3d444d;
--btn-primary: #eaa45d;
--btn-hover: #dca662;
--btn-disabled: #6c757d;
--shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
--tool-highlight: #ffd700;
--tool-highlight-text: #000000;
--log-debug: #808080;
--log-info: #ffffff;
--log-warning: #FF8C00;
--log-error: #FF0000;
--stats-header: #3a3a3a;
--btn-disabled: #4a5159;
--btn-secondary-bg: #2d333b;
--btn-secondary-hover: #3d444d;
--shadow: 0 1px 2px rgba(0, 0, 0, 0.25), 0 1px 3px rgba(0, 0, 0, 0.35);
--shadow-elevated: 0 4px 12px rgba(0, 0, 0, 0.45);
--tool-highlight: #f6c948;
--tool-highlight-text: #15181c;
--log-debug: #8b95a1;
--log-info: #e6edf3;
--log-warning: #f59e0b;
--log-error: #f87171;
--stats-header: #262b32;
}
.news-section {
background: var(--bg-secondary);
padding: 20px;
border: 1px solid var(--border-color);
padding: var(--space-5);
border-radius: var(--border-radius);
box-shadow: var(--shadow);
margin-bottom: 25px;
margin-bottom: var(--space-4);
}
.news-section h2 {
margin: 0 0 20px 0;
font-size: 18px;
color: var(--text-primary);
margin: 0 0 var(--space-4) 0;
font-size: 15px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.04em;
color: var(--text-muted);
}
.news-item {
@@ -72,6 +99,11 @@ html {
position: relative;
}
.news-item a {
color: var(--text-muted);
text-decoration: underline;
}
.news-item:last-child {
margin-bottom: 0;
}
@@ -144,13 +176,35 @@ html {
}
body {
font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
font-family: var(--font-sans);
font-size: 14px;
line-height: 1.5;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
margin: 0;
background-color: var(--bg-primary);
color: var(--text-primary);
transition: background-color 0.3s ease, color 0.3s ease;
}
h1, h2, h3, h4, h5, h6 {
font-weight: 600;
letter-spacing: -0.005em;
}
code, pre, kbd, samp,
.log-container,
.memory-editor,
.project-path,
.tool-item,
.stat-tool-name,
.execution-name,
.execution-meta,
.last-execution-name,
.config-value-mono {
font-family: var(--font-mono);
}
#frame {
max-width: 1600px;
margin: 0 auto;
@@ -260,18 +314,20 @@ body {
background-color: var(--bg-secondary);
color: var(--text-primary);
border: 1px solid var(--border-color);
padding: 8px 16px;
border-radius: 4px;
padding: 8px 14px;
border-radius: var(--border-radius-sm);
cursor: pointer;
font-size: 16px;
transition: background-color 0.3s ease, border-color 0.3s ease;
font-size: 14px;
font-family: inherit;
transition: background-color 0.2s ease, border-color 0.2s ease;
display: flex;
align-items: center;
gap: 8px;
gap: var(--space-2);
}
.menu-button:hover {
background-color: var(--border-color);
background-color: var(--btn-secondary-hover);
border-color: var(--border-strong);
}
.menu-dropdown {
@@ -412,7 +468,7 @@ body {
.overview-container {
display: grid;
grid-template-columns: 1fr 400px;
gap: 20px;
gap: var(--space-5);
}
.overview-left {
@@ -430,23 +486,30 @@ body {
background-color: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
padding: 20px;
margin-bottom: 20px;
padding: var(--space-5);
margin-bottom: var(--space-4);
transition: background-color 0.3s ease, border-color 0.3s ease;
}
.config-section h2,
.basic-stats-section h2 {
margin-top: 0;
color: var(--text-secondary);
margin: 0 0 var(--space-4) 0;
font-size: 15px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.04em;
color: var(--text-muted);
}
/* Collapsible Headers */
.collapsible-header {
margin-top: 0;
margin-bottom: 0;
font-size: 18px;
color: var(--text-secondary);
font-size: 15px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.04em;
color: var(--text-muted);
cursor: pointer;
user-select: none;
display: flex;
@@ -476,13 +539,17 @@ body {
.config-grid {
display: grid;
grid-template-columns: 180px 1fr;
gap: 12px;
margin-bottom: 20px;
gap: var(--space-3);
margin-bottom: var(--space-4);
}
.config-label {
font-weight: bold;
color: var(--text-secondary);
font-weight: 500;
color: var(--text-muted);
font-size: 13px;
text-transform: uppercase;
letter-spacing: 0.03em;
padding-top: 2px;
}
.config-value {
@@ -587,30 +654,29 @@ body {
.basic-stats-section {
background-color: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 5px;
padding: 20px;
margin-bottom: 20px;
border-radius: var(--border-radius);
padding: var(--space-5);
margin-bottom: var(--space-4);
transition: background-color 0.3s ease, border-color 0.3s ease;
}
.basic-stats-section h2 {
margin-top: 0;
color: var(--text-secondary);
}
/* Executions Styles */
.executions-section {
background-color: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 5px;
padding: 20px;
margin-bottom: 20px;
border-radius: var(--border-radius);
padding: var(--space-5);
margin-bottom: var(--space-4);
transition: background-color 0.3s ease, border-color 0.3s ease;
}
.executions-section h2 {
margin-top: 0;
color: var(--text-secondary);
margin: 0 0 var(--space-4) 0;
font-size: 15px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.04em;
color: var(--text-muted);
}
.execution-list {
@@ -864,40 +930,49 @@ body {
.btn {
background-color: var(--btn-primary);
color: white;
color: #fff;
border: none;
padding: 8px 16px;
border-radius: 4px;
border-radius: var(--border-radius-sm);
cursor: pointer;
font-size: 14px;
transition: background-color 0.3s ease;
font-weight: 500;
font-family: inherit;
transition: background-color 0.2s ease, transform 0.05s ease;
}
.btn:hover {
background-color: var(--btn-hover);
}
.btn:active {
transform: translateY(1px);
}
.btn:disabled {
background-color: var(--btn-disabled);
cursor: not-allowed;
transform: none;
}
.theme-toggle {
display: flex;
align-items: center;
gap: 5px;
gap: var(--space-2);
background-color: var(--bg-secondary);
color: var(--text-primary);
border: 1px solid var(--border-color);
border-radius: 4px;
padding: 8px 16px;
border-radius: var(--border-radius-sm);
padding: 8px 14px;
cursor: pointer;
font-size: 16px;
transition: background-color 0.3s ease, border-color 0.3s ease;
font-size: 14px;
font-family: inherit;
transition: background-color 0.2s ease, border-color 0.2s ease;
}
.theme-toggle:hover {
background-color: var(--border-color);
background-color: var(--btn-secondary-hover);
border-color: var(--border-strong);
}
.theme-toggle span {
@@ -1084,27 +1159,28 @@ body {
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(0, 0, 0, 0.5);
background-color: rgba(15, 23, 42, 0.55);
backdrop-filter: blur(2px);
}
.modal-content {
background-color: var(--bg-primary);
margin: 10% auto;
padding: 25px;
background-color: var(--bg-elevated);
margin: 8vh auto;
padding: var(--space-5);
border: 1px solid var(--border-color);
border-radius: 8px;
border-radius: var(--border-radius);
width: 90%;
max-width: 500px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);
box-shadow: var(--shadow-elevated);
position: relative;
}
.modal-close {
color: var(--text-muted);
float: right;
font-size: 28px;
font-weight: bold;
line-height: 20px;
font-size: 24px;
font-weight: 400;
line-height: 1;
cursor: pointer;
transition: color 0.2s;
}
@@ -1116,8 +1192,213 @@ body {
.modal h3 {
margin-top: 0;
margin-bottom: 15px;
margin-bottom: var(--space-3);
color: var(--text-primary);
font-size: 17px;
font-weight: 600;
}
/* Shared modal pieces (replace inline styles) */
.modal-info {
margin: var(--space-3) 0;
color: var(--text-primary);
line-height: 1.5;
}
.modal-hint {
margin: var(--space-3) 0;
color: var(--text-muted);
font-size: 13px;
line-height: 1.5;
}
.modal-field {
margin: var(--space-4) 0;
}
.modal-field label {
display: block;
margin-bottom: var(--space-2);
font-weight: 500;
color: var(--text-primary);
}
.modal-input,
.modal-select {
box-sizing: border-box;
width: 100%;
padding: 8px 10px;
border-radius: var(--border-radius-sm);
border: 1px solid var(--border-strong);
background: var(--bg-primary);
color: var(--text-primary);
font-size: 14px;
font-family: var(--font-sans);
transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.modal-input:focus,
.modal-select:focus {
outline: none;
border-color: var(--btn-primary);
box-shadow: 0 0 0 3px rgba(234, 164, 93, 0.18);
}
.modal-textarea {
box-sizing: border-box;
width: 100%;
padding: var(--space-3);
border: 1px solid var(--border-strong);
border-radius: var(--border-radius-sm);
background: var(--bg-primary);
color: var(--text-primary);
font-family: var(--font-mono);
font-size: 13px;
line-height: 1.5;
resize: vertical;
transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.modal-textarea:focus {
outline: none;
border-color: var(--btn-primary);
box-shadow: 0 0 0 3px rgba(234, 164, 93, 0.18);
}
.modal-actions {
display: flex;
gap: var(--space-2);
justify-content: flex-end;
margin-top: var(--space-4);
}
.modal-actions .btn {
padding: 8px 16px;
}
.modal-prompt {
margin: var(--space-4) 0;
color: var(--text-primary);
line-height: 1.5;
font-size: 15px;
}
.btn-secondary {
background-color: var(--btn-secondary-bg);
color: var(--text-primary);
}
.btn-secondary:hover {
background-color: var(--btn-secondary-hover);
}
.modal-title-with-meta {
margin-bottom: var(--space-4);
display: flex;
align-items: center;
gap: var(--space-2);
flex-wrap: wrap;
}
/* Filterable combobox (used in Add Language modal) */
.combobox {
position: relative;
}
.combobox-input {
padding-right: 28px;
}
.combobox-caret {
position: absolute;
top: 50%;
right: var(--space-3);
transform: translateY(-50%);
color: var(--text-muted);
font-size: 12px;
pointer-events: none;
transition: transform 0.15s ease, color 0.15s ease;
}
.combobox.open .combobox-caret {
color: var(--text-primary);
transform: translateY(-50%) rotate(180deg);
}
.combobox-options {
position: absolute;
top: calc(100% + 4px);
left: 0;
right: 0;
margin: 0;
padding: var(--space-1) 0;
list-style: none;
background: var(--bg-elevated);
border: 1px solid var(--border-strong);
border-radius: var(--border-radius-sm);
box-shadow: var(--shadow-elevated);
max-height: 240px;
overflow-y: auto;
z-index: 20;
}
.combobox-options li {
padding: 6px var(--space-3);
color: var(--text-primary);
cursor: pointer;
font-size: 14px;
line-height: 1.4;
transition: background-color 0.1s ease;
}
.combobox-options li.no-match {
color: var(--text-muted);
font-style: italic;
cursor: default;
}
.combobox-options li:hover:not(.no-match),
.combobox-options li.highlighted:not(.no-match) {
background-color: var(--btn-secondary-hover);
}
.combobox-options li.highlighted:not(.no-match) {
color: var(--text-primary);
}
.combobox-options li mark {
background: transparent;
color: var(--btn-primary);
font-weight: 600;
}
.combobox-empty {
margin-top: var(--space-2);
color: var(--text-muted);
font-style: italic;
font-size: 13px;
}
.section-header-row {
display: flex;
justify-content: space-between;
align-items: center;
}
.section-header-row h2 {
margin: 0 0 var(--space-4) 0;
}
.stats-summary-block {
margin-bottom: var(--space-5);
text-align: center;
}
.estimator-name {
text-align: center;
margin-bottom: var(--space-3);
color: var(--text-muted);
font-size: 13px;
}
/* Language Badge Styles */
+151 -14
View File
@@ -296,6 +296,10 @@ class Dashboard {
this.$availableContextsDisplay = $('#available-contexts-display');
this.$addLanguageModal = $('#add-language-modal');
this.$modalLanguageSelect = $('#modal-language-select');
this.$modalLanguageCombobox = $('#modal-language-combobox');
this.$modalLanguageOptions = $('#modal-language-options');
this.$modalLanguageEmpty = $('#modal-language-empty');
this.modalLanguageAvailable = [];
this.$modalProjectName = $('#modal-project-name');
this.$modalAddBtn = $('#modal-add-btn');
this.$modalCancelBtn = $('#modal-cancel-btn');
@@ -360,6 +364,7 @@ class Dashboard {
this.$modalAddBtn.click(this.addLanguageFromModal.bind(this));
this.$modalCancelBtn.click(this.closeLanguageModal.bind(this));
this.$modalClose.click(this.closeLanguageModal.bind(this));
this.bindLanguageCombobox();
this.$removeModalOkBtn.click(this.confirmRemoveLanguageOk.bind(this));
this.$removeModalCancelBtn.click(this.closeRemoveLanguageModal.bind(this));
this.$modalCloseRemove.click(this.closeRemoveLanguageModal.bind(this));
@@ -843,7 +848,7 @@ class Dashboard {
let html = '';
tools.forEach(function (tool) {
html += '<div class="info-item" title="' + tool.name + '">' + tool.name + '</div>';
html += '<div class="info-item" title="' + tool.name + '"><code>' + tool.name + '</code></div>';
});
this.$availableToolsDisplay.html(html);
@@ -1730,7 +1735,10 @@ class Dashboard {
closeLanguageModal() {
this.$addLanguageModal.fadeOut(200);
this.$modalLanguageSelect.empty();
this.closeLanguageDropdown();
this.$modalLanguageSelect.val('');
this.$modalLanguageOptions.empty();
this.modalLanguageAvailable = [];
this.$modalAddBtn.prop('disabled', false).text('Add Language');
}
@@ -1738,19 +1746,19 @@ class Dashboard {
let self = this;
$.ajax({
url: '/get_available_languages', type: 'GET', success: function (response) {
const languages = response.languages || [];
// Clear all existing options
self.$modalLanguageSelect.empty();
const languages = (response.languages || []).slice().sort();
self.modalLanguageAvailable = languages;
self.$modalLanguageSelect.val('');
if (languages.length === 0) {
// Show message if no languages available
self.$modalLanguageSelect.append($('<option>').val('').text('No languages available to add'));
self.$modalLanguageOptions.empty().hide();
self.$modalLanguageEmpty.show();
self.$modalLanguageSelect.prop('disabled', true).attr('placeholder', 'No languages available');
self.$modalAddBtn.prop('disabled', true);
} else {
// Add language options
languages.forEach(function (language) {
self.$modalLanguageSelect.append($('<option>').val(language).text(language));
});
self.$modalLanguageEmpty.hide();
self.$modalLanguageSelect.prop('disabled', false).attr('placeholder', 'Type to filter…');
self.renderLanguageOptions('');
self.$modalAddBtn.prop('disabled', false);
}
}, error: function (xhr, status, error) {
@@ -1759,12 +1767,141 @@ class Dashboard {
});
}
renderLanguageOptions(filter) {
const self = this;
const needle = (filter || '').toLowerCase();
const matches = this.modalLanguageAvailable.filter(function (lang) {
return lang.toLowerCase().includes(needle);
});
this.$modalLanguageOptions.empty();
if (matches.length === 0) {
this.$modalLanguageOptions.append(
$('<li class="no-match"></li>').text('No matches')
);
} else {
matches.forEach(function (lang, idx) {
let label = lang;
if (needle) {
const i = lang.toLowerCase().indexOf(needle);
if (i !== -1) {
const before = lang.slice(0, i);
const hit = lang.slice(i, i + needle.length);
const after = lang.slice(i + needle.length);
label = $('<span></span>')
.append(document.createTextNode(before))
.append($('<mark></mark>').text(hit))
.append(document.createTextNode(after))
.html();
}
}
const $li = $('<li role="option"></li>')
.attr('data-value', lang)
.html(label);
if (idx === 0) {
$li.addClass('highlighted');
}
$li.on('mousedown', function (e) {
e.preventDefault(); // keep input focus
self.selectLanguage(lang);
});
self.$modalLanguageOptions.append($li);
});
}
}
openLanguageDropdown() {
if (this.modalLanguageAvailable.length === 0) return;
this.renderLanguageOptions(this.$modalLanguageSelect.val());
this.$modalLanguageOptions.show();
this.$modalLanguageCombobox.addClass('open');
}
closeLanguageDropdown() {
this.$modalLanguageOptions.hide();
this.$modalLanguageCombobox.removeClass('open');
}
selectLanguage(lang) {
this.$modalLanguageSelect.val(lang);
this.closeLanguageDropdown();
}
moveLanguageHighlight(delta) {
const $items = this.$modalLanguageOptions.find('li:not(.no-match)');
if ($items.length === 0) return;
let idx = $items.index($items.filter('.highlighted'));
idx = (idx + delta + $items.length) % $items.length;
$items.removeClass('highlighted');
const $target = $items.eq(idx).addClass('highlighted');
const target = $target[0];
if (target && target.scrollIntoView) {
target.scrollIntoView({ block: 'nearest' });
}
}
bindLanguageCombobox() {
const self = this;
this.$modalLanguageSelect
.off('focus.combobox click.combobox input.combobox keydown.combobox')
.on('focus.combobox click.combobox', function () { self.openLanguageDropdown(); })
.on('input.combobox', function () {
self.renderLanguageOptions(self.$modalLanguageSelect.val());
self.openLanguageDropdown();
})
.on('keydown.combobox', function (e) {
const key = e.key;
if (key === 'ArrowDown') {
e.preventDefault();
if (self.$modalLanguageOptions.is(':visible')) {
self.moveLanguageHighlight(1);
} else {
self.openLanguageDropdown();
}
} else if (key === 'ArrowUp') {
e.preventDefault();
self.moveLanguageHighlight(-1);
} else if (key === 'Enter') {
const $hl = self.$modalLanguageOptions.find('li.highlighted:not(.no-match)');
if ($hl.length) {
e.preventDefault();
self.selectLanguage($hl.attr('data-value'));
}
} else if (key === 'Escape') {
if (self.$modalLanguageOptions.is(':visible')) {
e.stopPropagation(); // don't close the modal
self.closeLanguageDropdown();
}
}
});
this.$modalLanguageCombobox
.off('click.caret')
.on('click.caret', '.combobox-caret', function () {
self.$modalLanguageSelect.trigger('focus');
self.openLanguageDropdown();
});
$(document).off('mousedown.langCombobox').on('mousedown.langCombobox', function (e) {
if (!self.$modalLanguageCombobox.is(':visible')) return;
if (!$.contains(self.$modalLanguageCombobox[0], e.target)) {
self.closeLanguageDropdown();
}
});
}
addLanguageFromModal() {
const selectedLanguage = this.$modalLanguageSelect.val();
if (!selectedLanguage) {
alert('No language selected or no languages available to add');
const typed = (this.$modalLanguageSelect.val() || '').trim();
const matched = this.modalLanguageAvailable.find(function (l) {
return l.toLowerCase() === typed.toLowerCase();
});
if (!matched) {
alert(typed
? 'Unknown language: ' + typed + '. Pick one from the dropdown.'
: 'No language selected.');
return;
}
const selectedLanguage = matched;
const self = this;
+54 -66
View File
@@ -8,6 +8,9 @@
<link rel="icon" type="image/png" sizes="16x16" href="serena-icon-16.png">
<link rel="icon" type="image/png" sizes="32x32" href="serena-icon-32.png">
<link rel="icon" type="image/png" sizes="48x48" href="serena-icon-48.png">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<link rel="stylesheet" href="dashboard.css">
<script src="jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
@@ -69,8 +72,8 @@
</section>
<section class="config-section">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px;">
<h2 style="margin: 0;">Current Configuration</h2>
<div class="section-header-row">
<h2>Current Configuration</h2>
</div>
<div id="config-display">
<div class="loading">Loading configuration...</div>
@@ -201,9 +204,9 @@
<button id="clear-stats" class="btn">Clear Stats</button>
</div>
<div id="stats-summary" style="margin-bottom:20px; text-align:center;"></div>
<div id="estimator-name" style="text-align:center; margin-bottom:10px;"></div>
<div id="no-stats-message" style="text-align:center; color:var(--text-muted); font-style:italic; display:none;">
<div id="stats-summary" class="stats-summary-block"></div>
<div id="estimator-name" class="estimator-name"></div>
<div id="no-stats-message" class="no-stats-message" style="display:none;">
No tool stats collected yet.
</div>
@@ -232,25 +235,26 @@
<div class="modal-content">
<span class="modal-close">&times;</span>
<h3>Add Language</h3>
<p id="modal-project-info" style="margin: 15px 0; color: var(--text-primary); line-height: 1.5;">
<p id="modal-project-info" class="modal-info">
Adding a language to serena config of project <strong id="modal-project-name"></strong>.
</p>
<p style="margin: 15px 0; color: var(--text-muted); font-size: 13px; line-height: 1.5;">
<p class="modal-hint">
Note that this may download some dependencies needed for the language server and then start it,
it may take a few seconds before the LS is responsive.
</p>
<div style="margin: 20px 0;">
<label for="modal-language-select" style="display: block; margin-bottom: 8px; font-weight: 500;">Select
Language:</label>
<select id="modal-language-select" class="language-select"
style="width: 100%; padding: 8px 10px; border-radius: 4px; border: 1px solid var(--border-color); background: var(--bg-primary); color: var(--text-primary); font-size: 14px;">
</select>
<div class="modal-field">
<label for="modal-language-select">Select Language:</label>
<div class="combobox" id="modal-language-combobox">
<input type="text" id="modal-language-select" class="modal-input combobox-input"
placeholder="Type to filter…" autocomplete="off" spellcheck="false">
<span class="combobox-caret" aria-hidden="true">▾</span>
<ul id="modal-language-options" class="combobox-options" role="listbox" style="display:none"></ul>
<div id="modal-language-empty" class="combobox-empty" style="display:none">No languages available to add</div>
</div>
</div>
<div style="display: flex; gap: 10px; justify-content: flex-end; margin-top: 20px;">
<button id="modal-cancel-btn" class="btn"
style="background: var(--bg-secondary); color: var(--text-primary); padding: 8px 16px;">Cancel
</button>
<button id="modal-add-btn" class="btn" style="padding: 8px 16px;">Add Language</button>
<div class="modal-actions">
<button id="modal-cancel-btn" class="btn btn-secondary">Cancel</button>
<button id="modal-add-btn" class="btn">Add Language</button>
</div>
</div>
</div>
@@ -259,14 +263,12 @@
<div id="remove-language-modal" class="modal" style="display:none">
<div class="modal-content">
<span class="modal-close modal-close-remove">&times;</span>
<p style="margin: 20px 0; color: var(--text-primary); line-height: 1.5; font-size: 15px;">
<p class="modal-prompt">
Remove language <strong id="remove-language-name"></strong> from configuration?
</p>
<div style="display: flex; gap: 10px; justify-content: flex-end; margin-top: 20px;">
<button id="remove-modal-cancel-btn" class="btn"
style="background: var(--bg-secondary); color: var(--text-primary); padding: 8px 16px;">Cancel
</button>
<button id="remove-modal-ok-btn" class="btn" style="padding: 8px 16px;">OK</button>
<div class="modal-actions">
<button id="remove-modal-cancel-btn" class="btn btn-secondary">Cancel</button>
<button id="remove-modal-ok-btn" class="btn">OK</button>
</div>
</div>
</div>
@@ -275,7 +277,7 @@
<div id="edit-memory-modal" class="modal" style="display:none">
<div class="modal-content modal-content-large">
<span class="modal-close modal-close-edit-memory">&times;</span>
<h3 style="margin-bottom: 20px; display: flex; align-items: center; gap: 8px;">
<h3 class="modal-title-with-meta">
Memory:
<span id="edit-memory-name" class="memory-name-display"></span>
<span id="edit-memory-rename-btn" class="memory-rename-btn" title="Rename memory">
@@ -286,13 +288,10 @@
</span>
<input type="text" id="edit-memory-rename-input" class="memory-rename-input" style="display: none;">
</h3>
<textarea id="edit-memory-content" class="memory-editor" rows="20"
style="width: 100%; padding: 10px; border: 1px solid var(--border-color); border-radius: 4px; background: var(--bg-primary); color: var(--text-primary); font-family: 'Courier New', monospace; font-size: 13px; resize: vertical;"></textarea>
<div style="display: flex; gap: 10px; justify-content: flex-end; margin-top: 20px;">
<button id="edit-memory-cancel-btn" class="btn"
style="background: var(--bg-secondary); color: var(--text-primary); padding: 8px 16px;">Cancel
</button>
<button id="edit-memory-save-btn" class="btn" style="padding: 8px 16px;">Save</button>
<textarea id="edit-memory-content" class="memory-editor modal-textarea" rows="20"></textarea>
<div class="modal-actions">
<button id="edit-memory-cancel-btn" class="btn btn-secondary">Cancel</button>
<button id="edit-memory-save-btn" class="btn">Save</button>
</div>
</div>
</div>
@@ -301,14 +300,12 @@
<div id="delete-memory-modal" class="modal" style="display:none">
<div class="modal-content">
<span class="modal-close modal-close-delete-memory">&times;</span>
<p style="margin: 20px 0; color: var(--text-primary); line-height: 1.5; font-size: 15px;">
<p class="modal-prompt">
Delete memory <strong id="delete-memory-name"></strong>?
</p>
<div style="display: flex; gap: 10px; justify-content: flex-end; margin-top: 20px;">
<button id="delete-memory-cancel-btn" class="btn"
style="background: var(--bg-secondary); color: var(--text-primary); padding: 8px 16px;">Cancel
</button>
<button id="delete-memory-ok-btn" class="btn" style="padding: 8px 16px;">OK</button>
<div class="modal-actions">
<button id="delete-memory-cancel-btn" class="btn btn-secondary">Cancel</button>
<button id="delete-memory-ok-btn" class="btn">OK</button>
</div>
</div>
</div>
@@ -317,26 +314,22 @@
<div id="create-memory-modal" class="modal" style="display:none">
<div class="modal-content">
<span class="modal-close modal-close-create-memory">&times;</span>
<p id="create-memory-project-info" style="margin: 15px 0; color: var(--text-primary); line-height: 1.5;">
<p id="create-memory-project-info" class="modal-info">
Create a new memory for project <strong id="create-memory-project-name"></strong>.
</p>
<p style="margin: 15px 0; color: var(--text-muted); font-size: 13px; line-height: 1.5;">
<p class="modal-hint">
Memory names should be descriptive and use underscores instead of spaces (e.g., "api_architecture",
"testing_guidelines"). Use "/" to organize memories into subdirectories (e.g., "architecture/api_design"),
use the "global/" prefix to write memories that are shared across projects (e.g., "global/java/style_guide").
</p>
<div style="margin: 20px 0;">
<label for="create-memory-name-input" style="display: block; margin-bottom: 8px; font-weight: 500;">Memory
Name:</label>
<input type="text" id="create-memory-name-input" class="memory-name-input"
placeholder="e.g., project_overview or topic/memory_name"
style="width: 100%; padding: 8px 10px; border-radius: 4px; border: 1px solid var(--border-color); background: var(--bg-primary); color: var(--text-primary); font-size: 14px;">
<div class="modal-field">
<label for="create-memory-name-input">Memory Name:</label>
<input type="text" id="create-memory-name-input" class="memory-name-input modal-input"
placeholder="e.g., project_overview or topic/memory_name">
</div>
<div style="display: flex; gap: 10px; justify-content: flex-end; margin-top: 20px;">
<button id="create-memory-cancel-btn" class="btn"
style="background: var(--bg-secondary); color: var(--text-primary); padding: 8px 16px;">Cancel
</button>
<button id="create-memory-create-btn" class="btn" style="padding: 8px 16px;">Create</button>
<div class="modal-actions">
<button id="create-memory-cancel-btn" class="btn btn-secondary">Cancel</button>
<button id="create-memory-create-btn" class="btn">Create</button>
</div>
</div>
</div>
@@ -345,15 +338,13 @@
<div id="cancel-execution-modal" class="modal" style="display:none">
<div class="modal-content">
<span class="modal-close modal-close-cancel-execution">&times;</span>
<p style="margin: 20px 0; color: var(--text-primary); line-height: 1.5; font-size: 15px;">
<p class="modal-prompt">
Are you sure? The execution will continue running until timeout, it will simply no longer be in the queue.
Abandoning a running execution is only advised as a measure for unblocking Serena.
</p>
<div style="display: flex; gap: 10px; justify-content: flex-end; margin-top: 20px;">
<button id="cancel-execution-cancel-btn" class="btn"
style="background: var(--bg-secondary); color: var(--text-primary); padding: 8px 16px;">Cancel
</button>
<button id="cancel-execution-ok-btn" class="btn" style="padding: 8px 16px;">OK</button>
<div class="modal-actions">
<button id="cancel-execution-cancel-btn" class="btn btn-secondary">Cancel</button>
<button id="cancel-execution-ok-btn" class="btn">OK</button>
</div>
</div>
</div>
@@ -362,17 +353,14 @@
<div id="edit-serena-config-modal" class="modal" style="display:none">
<div class="modal-content modal-content-large">
<span class="modal-close modal-close-edit-serena-config">&times;</span>
<h3 style="margin-bottom: 10px;">Global Serena Configuration</h3>
<p style="margin: 10px 0 20px 0; color: var(--text-muted); font-size: 13px; line-height: 1.5;">
<h3>Global Serena Configuration</h3>
<p class="modal-hint">
Note: Changes to the configuration will only take effect after Serena is restarted.
</p>
<textarea id="edit-serena-config-content" class="memory-editor" rows="20"
style="width: 100%; padding: 10px; border: 1px solid var(--border-color); border-radius: 4px; background: var(--bg-primary); color: var(--text-primary); font-family: 'Courier New', monospace; font-size: 13px; resize: vertical;"></textarea>
<div style="display: flex; gap: 10px; justify-content: flex-end; margin-top: 20px;">
<button id="edit-serena-config-cancel-btn" class="btn"
style="background: var(--bg-secondary); color: var(--text-primary); padding: 8px 16px;">Cancel
</button>
<button id="edit-serena-config-save-btn" class="btn" style="padding: 8px 16px;">Save</button>
<textarea id="edit-serena-config-content" class="memory-editor modal-textarea" rows="20"></textarea>
<div class="modal-actions">
<button id="edit-serena-config-cancel-btn" class="btn btn-secondary">Cancel</button>
<button id="edit-serena-config-save-btn" class="btn">Save</button>
</div>
</div>
</div>