configurable themes
This commit is contained in:
parent
7c1a97ea58
commit
020d8ba82d
52
index.html
52
index.html
|
|
@ -19,6 +19,7 @@
|
||||||
<div class="sidebar">
|
<div class="sidebar">
|
||||||
<div class="sidebar-header">
|
<div class="sidebar-header">
|
||||||
<h1 class="sidebar-title">FleetCode</h1>
|
<h1 class="sidebar-title">FleetCode</h1>
|
||||||
|
<button id="open-settings" class="settings-btn" title="Settings">⚙️</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="sidebar-content">
|
<div class="sidebar-content">
|
||||||
|
|
@ -169,6 +170,57 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Settings Modal -->
|
||||||
|
<div id="settings-modal" class="modal-overlay hidden">
|
||||||
|
<div class="modal">
|
||||||
|
<h2 class="modal-title">Terminal Settings</h2>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">Theme</label>
|
||||||
|
<select id="settings-theme" class="form-select">
|
||||||
|
<option value="macos-light">macOS Terminal (Light)</option>
|
||||||
|
<option value="macos-dark">macOS Terminal (Dark)</option>
|
||||||
|
<option value="solarized-dark">Solarized Dark</option>
|
||||||
|
<option value="dracula">Dracula</option>
|
||||||
|
<option value="one-dark">One Dark</option>
|
||||||
|
<option value="github-dark">GitHub Dark</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">Font Family</label>
|
||||||
|
<select id="settings-font-family" class="form-select">
|
||||||
|
<option value="Menlo, Monaco, 'Courier New', monospace">Menlo</option>
|
||||||
|
<option value="'SF Mono', Monaco, 'Courier New', monospace">SF Mono</option>
|
||||||
|
<option value="Monaco, 'Courier New', monospace">Monaco</option>
|
||||||
|
<option value="'Courier New', Courier, monospace">Courier New</option>
|
||||||
|
<option value="'Source Code Pro', monospace">Source Code Pro</option>
|
||||||
|
<option value="'Fira Code', monospace">Fira Code</option>
|
||||||
|
<option value="'JetBrains Mono', monospace">JetBrains Mono</option>
|
||||||
|
<option value="Consolas, monospace">Consolas</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">Font Size</label>
|
||||||
|
<input type="number" id="settings-font-size" class="form-input" min="8" max="32" placeholder="11" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="flex items-center space-x-2 cursor-pointer">
|
||||||
|
<input type="checkbox" id="settings-cursor-blink" class="form-checkbox" />
|
||||||
|
<span class="text-sm text-gray-300">Cursor Blink</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="btn-group">
|
||||||
|
<button id="cancel-settings" class="btn-secondary">Cancel</button>
|
||||||
|
<button id="reset-settings" class="btn-secondary">Reset to Default</button>
|
||||||
|
<button id="save-settings" class="btn-primary">Save</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
require('./dist/renderer.js');
|
require('./dist/renderer.js');
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
32
main.ts
32
main.ts
|
|
@ -212,7 +212,10 @@ ipcMain.on("create-session", async (event, config: SessionConfig) => {
|
||||||
let dataBuffer = "";
|
let dataBuffer = "";
|
||||||
|
|
||||||
ptyProcess.onData((data) => {
|
ptyProcess.onData((data) => {
|
||||||
mainWindow.webContents.send("session-output", sessionId, data);
|
// Only send data if window still exists and is not destroyed
|
||||||
|
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||||
|
mainWindow.webContents.send("session-output", sessionId, data);
|
||||||
|
}
|
||||||
|
|
||||||
// Detect when terminal is ready
|
// Detect when terminal is ready
|
||||||
if (!terminalReady) {
|
if (!terminalReady) {
|
||||||
|
|
@ -309,7 +312,10 @@ ipcMain.on("reopen-session", (event, sessionId: string) => {
|
||||||
let dataBuffer = "";
|
let dataBuffer = "";
|
||||||
|
|
||||||
ptyProcess.onData((data) => {
|
ptyProcess.onData((data) => {
|
||||||
mainWindow.webContents.send("session-output", sessionId, data);
|
// Only send data if window still exists and is not destroyed
|
||||||
|
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||||
|
mainWindow.webContents.send("session-output", sessionId, data);
|
||||||
|
}
|
||||||
|
|
||||||
// Detect when terminal is ready and auto-run agent
|
// Detect when terminal is ready and auto-run agent
|
||||||
if (!terminalReady) {
|
if (!terminalReady) {
|
||||||
|
|
@ -389,6 +395,15 @@ ipcMain.on("rename-session", (_event, sessionId: string, newName: string) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Terminal settings handlers
|
||||||
|
ipcMain.handle("get-terminal-settings", () => {
|
||||||
|
return (store as any).get("terminalSettings");
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle("save-terminal-settings", (_event, settings: any) => {
|
||||||
|
(store as any).set("terminalSettings", settings);
|
||||||
|
});
|
||||||
|
|
||||||
// MCP Server management functions
|
// MCP Server management functions
|
||||||
async function listMcpServers() {
|
async function listMcpServers() {
|
||||||
try {
|
try {
|
||||||
|
|
@ -529,6 +544,19 @@ const createWindow = () => {
|
||||||
const sessions = getPersistedSessions();
|
const sessions = getPersistedSessions();
|
||||||
mainWindow.webContents.send("load-persisted-sessions", sessions);
|
mainWindow.webContents.send("load-persisted-sessions", sessions);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Clean up PTY processes when window is closed
|
||||||
|
mainWindow.on("closed", () => {
|
||||||
|
// Kill all active PTY processes
|
||||||
|
activePtyProcesses.forEach((ptyProcess, sessionId) => {
|
||||||
|
try {
|
||||||
|
ptyProcess.kill();
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error killing PTY for session ${sessionId}:`, error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
activePtyProcesses.clear();
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
app.whenReady().then(() => {
|
app.whenReady().then(() => {
|
||||||
|
|
|
||||||
290
renderer.ts
290
renderer.ts
|
|
@ -40,19 +40,199 @@ interface McpServer {
|
||||||
type?: "stdio" | "sse";
|
type?: "stdio" | "sse";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface TerminalSettings {
|
||||||
|
fontFamily: string;
|
||||||
|
fontSize: number;
|
||||||
|
theme: string; // Theme preset name
|
||||||
|
cursorBlink: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ThemeColors {
|
||||||
|
background: string;
|
||||||
|
foreground: string;
|
||||||
|
cursor?: string;
|
||||||
|
cursorAccent?: string;
|
||||||
|
selection?: string;
|
||||||
|
black?: string;
|
||||||
|
red?: string;
|
||||||
|
green?: string;
|
||||||
|
yellow?: string;
|
||||||
|
blue?: string;
|
||||||
|
magenta?: string;
|
||||||
|
cyan?: string;
|
||||||
|
white?: string;
|
||||||
|
brightBlack?: string;
|
||||||
|
brightRed?: string;
|
||||||
|
brightGreen?: string;
|
||||||
|
brightYellow?: string;
|
||||||
|
brightBlue?: string;
|
||||||
|
brightMagenta?: string;
|
||||||
|
brightCyan?: string;
|
||||||
|
brightWhite?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Theme presets
|
||||||
|
const THEME_PRESETS: Record<string, ThemeColors> = {
|
||||||
|
"macos-light": {
|
||||||
|
background: "#ffffff",
|
||||||
|
foreground: "#000000",
|
||||||
|
cursor: "#000000",
|
||||||
|
selection: "#b4d5fe",
|
||||||
|
black: "#000000",
|
||||||
|
red: "#c23621",
|
||||||
|
green: "#25bc24",
|
||||||
|
yellow: "#adad27",
|
||||||
|
blue: "#492ee1",
|
||||||
|
magenta: "#d338d3",
|
||||||
|
cyan: "#33bbc8",
|
||||||
|
white: "#cbcccd",
|
||||||
|
brightBlack: "#818383",
|
||||||
|
brightRed: "#fc391f",
|
||||||
|
brightGreen: "#31e722",
|
||||||
|
brightYellow: "#eaec23",
|
||||||
|
brightBlue: "#5833ff",
|
||||||
|
brightMagenta: "#f935f8",
|
||||||
|
brightCyan: "#14f0f0",
|
||||||
|
brightWhite: "#e9ebeb",
|
||||||
|
},
|
||||||
|
"macos-dark": {
|
||||||
|
background: "#000000",
|
||||||
|
foreground: "#ffffff",
|
||||||
|
cursor: "#ffffff",
|
||||||
|
selection: "#4d4d4d",
|
||||||
|
black: "#000000",
|
||||||
|
red: "#c23621",
|
||||||
|
green: "#25bc24",
|
||||||
|
yellow: "#adad27",
|
||||||
|
blue: "#492ee1",
|
||||||
|
magenta: "#d338d3",
|
||||||
|
cyan: "#33bbc8",
|
||||||
|
white: "#cbcccd",
|
||||||
|
brightBlack: "#818383",
|
||||||
|
brightRed: "#fc391f",
|
||||||
|
brightGreen: "#31e722",
|
||||||
|
brightYellow: "#eaec23",
|
||||||
|
brightBlue: "#5833ff",
|
||||||
|
brightMagenta: "#f935f8",
|
||||||
|
brightCyan: "#14f0f0",
|
||||||
|
brightWhite: "#e9ebeb",
|
||||||
|
},
|
||||||
|
"solarized-dark": {
|
||||||
|
background: "#002b36",
|
||||||
|
foreground: "#839496",
|
||||||
|
cursor: "#839496",
|
||||||
|
selection: "#073642",
|
||||||
|
black: "#073642",
|
||||||
|
red: "#dc322f",
|
||||||
|
green: "#859900",
|
||||||
|
yellow: "#b58900",
|
||||||
|
blue: "#268bd2",
|
||||||
|
magenta: "#d33682",
|
||||||
|
cyan: "#2aa198",
|
||||||
|
white: "#eee8d5",
|
||||||
|
brightBlack: "#002b36",
|
||||||
|
brightRed: "#cb4b16",
|
||||||
|
brightGreen: "#586e75",
|
||||||
|
brightYellow: "#657b83",
|
||||||
|
brightBlue: "#839496",
|
||||||
|
brightMagenta: "#6c71c4",
|
||||||
|
brightCyan: "#93a1a1",
|
||||||
|
brightWhite: "#fdf6e3",
|
||||||
|
},
|
||||||
|
"dracula": {
|
||||||
|
background: "#282a36",
|
||||||
|
foreground: "#f8f8f2",
|
||||||
|
cursor: "#f8f8f2",
|
||||||
|
selection: "#44475a",
|
||||||
|
black: "#21222c",
|
||||||
|
red: "#ff5555",
|
||||||
|
green: "#50fa7b",
|
||||||
|
yellow: "#f1fa8c",
|
||||||
|
blue: "#bd93f9",
|
||||||
|
magenta: "#ff79c6",
|
||||||
|
cyan: "#8be9fd",
|
||||||
|
white: "#f8f8f2",
|
||||||
|
brightBlack: "#6272a4",
|
||||||
|
brightRed: "#ff6e6e",
|
||||||
|
brightGreen: "#69ff94",
|
||||||
|
brightYellow: "#ffffa5",
|
||||||
|
brightBlue: "#d6acff",
|
||||||
|
brightMagenta: "#ff92df",
|
||||||
|
brightCyan: "#a4ffff",
|
||||||
|
brightWhite: "#ffffff",
|
||||||
|
},
|
||||||
|
"one-dark": {
|
||||||
|
background: "#282c34",
|
||||||
|
foreground: "#abb2bf",
|
||||||
|
cursor: "#528bff",
|
||||||
|
selection: "#3e4451",
|
||||||
|
black: "#282c34",
|
||||||
|
red: "#e06c75",
|
||||||
|
green: "#98c379",
|
||||||
|
yellow: "#e5c07b",
|
||||||
|
blue: "#61afef",
|
||||||
|
magenta: "#c678dd",
|
||||||
|
cyan: "#56b6c2",
|
||||||
|
white: "#abb2bf",
|
||||||
|
brightBlack: "#5c6370",
|
||||||
|
brightRed: "#e06c75",
|
||||||
|
brightGreen: "#98c379",
|
||||||
|
brightYellow: "#e5c07b",
|
||||||
|
brightBlue: "#61afef",
|
||||||
|
brightMagenta: "#c678dd",
|
||||||
|
brightCyan: "#56b6c2",
|
||||||
|
brightWhite: "#ffffff",
|
||||||
|
},
|
||||||
|
"github-dark": {
|
||||||
|
background: "#0d1117",
|
||||||
|
foreground: "#c9d1d9",
|
||||||
|
cursor: "#58a6ff",
|
||||||
|
selection: "#163c61",
|
||||||
|
black: "#484f58",
|
||||||
|
red: "#ff7b72",
|
||||||
|
green: "#3fb950",
|
||||||
|
yellow: "#d29922",
|
||||||
|
blue: "#58a6ff",
|
||||||
|
magenta: "#bc8cff",
|
||||||
|
cyan: "#39c5cf",
|
||||||
|
white: "#b1bac4",
|
||||||
|
brightBlack: "#6e7681",
|
||||||
|
brightRed: "#ffa198",
|
||||||
|
brightGreen: "#56d364",
|
||||||
|
brightYellow: "#e3b341",
|
||||||
|
brightBlue: "#79c0ff",
|
||||||
|
brightMagenta: "#d2a8ff",
|
||||||
|
brightCyan: "#56d4dd",
|
||||||
|
brightWhite: "#f0f6fc",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Detect system theme
|
||||||
|
function getSystemTheme(): "light" | "dark" {
|
||||||
|
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default settings - macOS Terminal matching system theme
|
||||||
|
const DEFAULT_SETTINGS: TerminalSettings = {
|
||||||
|
fontFamily: "Menlo, Monaco, 'Courier New', monospace",
|
||||||
|
fontSize: 11,
|
||||||
|
theme: getSystemTheme() === "dark" ? "macos-dark" : "macos-light",
|
||||||
|
cursorBlink: false,
|
||||||
|
};
|
||||||
|
|
||||||
const sessions = new Map<string, Session>();
|
const sessions = new Map<string, Session>();
|
||||||
let activeSessionId: string | null = null;
|
let activeSessionId: string | null = null;
|
||||||
let mcpServers: McpServer[] = [];
|
let mcpServers: McpServer[] = [];
|
||||||
|
let terminalSettings: TerminalSettings = { ...DEFAULT_SETTINGS };
|
||||||
|
|
||||||
function createTerminalUI(sessionId: string) {
|
function createTerminalUI(sessionId: string) {
|
||||||
|
const themeColors = THEME_PRESETS[terminalSettings.theme] || THEME_PRESETS["macos-dark"];
|
||||||
|
|
||||||
const term = new Terminal({
|
const term = new Terminal({
|
||||||
cursorBlink: true,
|
cursorBlink: terminalSettings.cursorBlink,
|
||||||
fontSize: 14,
|
fontSize: terminalSettings.fontSize,
|
||||||
fontFamily: 'Menlo, Monaco, "Courier New", monospace',
|
fontFamily: terminalSettings.fontFamily,
|
||||||
theme: {
|
theme: themeColors,
|
||||||
background: "#000000",
|
|
||||||
foreground: "#ffffff",
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const fitAddon = new FitAddon();
|
const fitAddon = new FitAddon();
|
||||||
|
|
@ -986,3 +1166,99 @@ ipcRenderer.on("mcp-servers-updated", (_event, servers: McpServer[]) => {
|
||||||
|
|
||||||
// Load MCP servers on startup
|
// Load MCP servers on startup
|
||||||
loadMcpServers();
|
loadMcpServers();
|
||||||
|
|
||||||
|
// Settings Modal handling
|
||||||
|
const settingsModal = document.getElementById("settings-modal");
|
||||||
|
const openSettingsBtn = document.getElementById("open-settings");
|
||||||
|
const cancelSettingsBtn = document.getElementById("cancel-settings");
|
||||||
|
const resetSettingsBtn = document.getElementById("reset-settings");
|
||||||
|
const saveSettingsBtn = document.getElementById("save-settings");
|
||||||
|
|
||||||
|
const settingsTheme = document.getElementById("settings-theme") as HTMLSelectElement;
|
||||||
|
const settingsFontFamily = document.getElementById("settings-font-family") as HTMLSelectElement;
|
||||||
|
const settingsFontSize = document.getElementById("settings-font-size") as HTMLInputElement;
|
||||||
|
const settingsCursorBlink = document.getElementById("settings-cursor-blink") as HTMLInputElement;
|
||||||
|
|
||||||
|
// Load saved settings on startup
|
||||||
|
async function loadSettings() {
|
||||||
|
const savedSettings = await ipcRenderer.invoke("get-terminal-settings");
|
||||||
|
if (savedSettings) {
|
||||||
|
terminalSettings = { ...DEFAULT_SETTINGS, ...savedSettings };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Populate settings form
|
||||||
|
function populateSettingsForm() {
|
||||||
|
// Set theme
|
||||||
|
settingsTheme.value = terminalSettings.theme;
|
||||||
|
|
||||||
|
// Set font family - match against dropdown options
|
||||||
|
const fontOptions = Array.from(settingsFontFamily.options);
|
||||||
|
const matchingOption = fontOptions.find(opt => opt.value === terminalSettings.fontFamily);
|
||||||
|
if (matchingOption) {
|
||||||
|
settingsFontFamily.value = matchingOption.value;
|
||||||
|
} else {
|
||||||
|
// Default to first option (Menlo) if no match
|
||||||
|
settingsFontFamily.selectedIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
settingsFontSize.value = terminalSettings.fontSize.toString();
|
||||||
|
settingsCursorBlink.checked = terminalSettings.cursorBlink;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply settings to all existing terminals
|
||||||
|
function applySettingsToAllTerminals() {
|
||||||
|
const themeColors = THEME_PRESETS[terminalSettings.theme] || THEME_PRESETS["macos-dark"];
|
||||||
|
|
||||||
|
sessions.forEach((session) => {
|
||||||
|
if (session.terminal) {
|
||||||
|
session.terminal.options.fontFamily = terminalSettings.fontFamily;
|
||||||
|
session.terminal.options.fontSize = terminalSettings.fontSize;
|
||||||
|
session.terminal.options.cursorBlink = terminalSettings.cursorBlink;
|
||||||
|
session.terminal.options.theme = themeColors;
|
||||||
|
|
||||||
|
// Refresh terminal to apply changes
|
||||||
|
if (session.fitAddon) {
|
||||||
|
session.fitAddon.fit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open settings modal
|
||||||
|
openSettingsBtn?.addEventListener("click", () => {
|
||||||
|
populateSettingsForm();
|
||||||
|
settingsModal?.classList.remove("hidden");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Cancel settings
|
||||||
|
cancelSettingsBtn?.addEventListener("click", () => {
|
||||||
|
settingsModal?.classList.add("hidden");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Reset settings to default
|
||||||
|
resetSettingsBtn?.addEventListener("click", () => {
|
||||||
|
terminalSettings = { ...DEFAULT_SETTINGS };
|
||||||
|
populateSettingsForm();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Save settings
|
||||||
|
saveSettingsBtn?.addEventListener("click", async () => {
|
||||||
|
// Read values from form
|
||||||
|
terminalSettings.theme = settingsTheme.value;
|
||||||
|
terminalSettings.fontFamily = settingsFontFamily.value || DEFAULT_SETTINGS.fontFamily;
|
||||||
|
terminalSettings.fontSize = parseInt(settingsFontSize.value) || DEFAULT_SETTINGS.fontSize;
|
||||||
|
terminalSettings.cursorBlink = settingsCursorBlink.checked;
|
||||||
|
|
||||||
|
// Save to electron-store
|
||||||
|
await ipcRenderer.invoke("save-terminal-settings", terminalSettings);
|
||||||
|
|
||||||
|
// Apply to all existing terminals
|
||||||
|
applySettingsToAllTerminals();
|
||||||
|
|
||||||
|
// Close modal
|
||||||
|
settingsModal?.classList.add("hidden");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Load settings on startup
|
||||||
|
loadSettings();
|
||||||
|
|
|
||||||
10
styles.css
10
styles.css
|
|
@ -9,13 +9,17 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-header {
|
.sidebar-header {
|
||||||
@apply p-4 border-b border-gray-700;
|
@apply p-4 border-b border-gray-700 flex items-center justify-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-title {
|
.sidebar-title {
|
||||||
@apply text-white font-semibold text-lg;
|
@apply text-white font-semibold text-lg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.settings-btn {
|
||||||
|
@apply text-gray-400 hover:text-white hover:bg-gray-700 text-lg w-8 h-8 flex items-center justify-center rounded transition-all;
|
||||||
|
}
|
||||||
|
|
||||||
.sidebar-content {
|
.sidebar-content {
|
||||||
@apply flex-1 overflow-y-auto;
|
@apply flex-1 overflow-y-auto;
|
||||||
}
|
}
|
||||||
|
|
@ -177,6 +181,10 @@
|
||||||
@apply w-4 h-4 rounded border-gray-600 bg-gray-700 text-blue-600 focus:ring-blue-500 focus:ring-2 cursor-pointer;
|
@apply w-4 h-4 rounded border-gray-600 bg-gray-700 text-blue-600 focus:ring-blue-500 focus:ring-2 cursor-pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.color-input {
|
||||||
|
@apply bg-gray-700 border border-gray-600 rounded px-2 py-1 h-10 w-20 cursor-pointer;
|
||||||
|
}
|
||||||
|
|
||||||
.btn-secondary {
|
.btn-secondary {
|
||||||
@apply bg-gray-600 hover:bg-gray-700 text-white px-4 py-2 rounded text-sm font-medium transition;
|
@apply bg-gray-600 hover:bg-gray-700 text-white px-4 py-2 rounded text-sm font-medium transition;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue