mirror of
https://github.com/tiennm99/serena.git
synced 2026-10-12 12:14:19 +00:00
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:
1 parent
2e05602895
commit
d3454bc39e
4 files changed
+576
-165
No files matched your search
@@ -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:
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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">×</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">×</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">×</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">×</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">×</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">×</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">×</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>
|
||||
|
||||
Reference in new issue
Block a user