898 lines
32 KiB
HTML
898 lines
32 KiB
HTML
<!doctype html>
|
|
<html lang="pl">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title>QL Editor - {{ project }} - {{ selected_file }}</title>
|
|
|
|
<!-- Bootstrap 5 -->
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css" rel="stylesheet">
|
|
|
|
<style>
|
|
body {
|
|
background-color: #f8f9fa;
|
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
}
|
|
|
|
.navbar {
|
|
box-shadow: 0 2px 4px rgba(0, 0, 0, .1);
|
|
}
|
|
|
|
.editor-container {
|
|
background: white;
|
|
border-radius: 10px;
|
|
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
|
overflow: hidden;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.file-list {
|
|
background: #f8f9fa;
|
|
border-right: 1px solid #dee2e6;
|
|
min-height: 500px;
|
|
}
|
|
|
|
.file-item {
|
|
padding: 10px 15px;
|
|
border-bottom: 1px solid #eee;
|
|
cursor: pointer;
|
|
transition: all 0.2s;
|
|
}
|
|
|
|
.file-item:hover {
|
|
background-color: #e9ecef;
|
|
}
|
|
|
|
.file-item.active {
|
|
background-color: #007bff;
|
|
color: white;
|
|
}
|
|
|
|
#codeEditor {
|
|
font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
|
|
font-size: 14px;
|
|
line-height: 1.6;
|
|
border: none;
|
|
padding: 20px;
|
|
width: 100%;
|
|
min-height: 400px;
|
|
resize: vertical;
|
|
background: #f8f9fa;
|
|
}
|
|
|
|
#codeEditor:focus {
|
|
outline: none;
|
|
background: white;
|
|
box-shadow: inset 0 0 0 1px #007bff;
|
|
}
|
|
|
|
.console-output {
|
|
font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
|
|
font-size: 13px;
|
|
background: #1e1e1e;
|
|
color: #d4d4d4;
|
|
padding: 15px;
|
|
border-radius: 5px;
|
|
min-height: 200px;
|
|
max-height: 300px;
|
|
overflow-y: auto;
|
|
white-space: pre-wrap;
|
|
word-break: break-word;
|
|
border: 1px solid #333;
|
|
}
|
|
|
|
.console-command {
|
|
color: #4ec9b0;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.console-response {
|
|
color: #ce9178;
|
|
}
|
|
|
|
.console-error {
|
|
color: #f44747;
|
|
}
|
|
|
|
.console-success {
|
|
color: #6a9955;
|
|
}
|
|
|
|
.console-info {
|
|
color: #9cdcfe;
|
|
}
|
|
|
|
.status-badge {
|
|
font-size: 0.75rem;
|
|
padding: 2px 8px;
|
|
}
|
|
|
|
.editor-toolbar {
|
|
background: linear-gradient(to right, #f8f9fa, #e9ecef);
|
|
border-bottom: 1px solid #dee2e6;
|
|
padding: 10px 15px;
|
|
}
|
|
|
|
.btn-editor {
|
|
padding: 5px 12px;
|
|
font-size: 0.875rem;
|
|
border-radius: 4px;
|
|
transition: all 0.2s;
|
|
}
|
|
|
|
.btn-editor:hover {
|
|
transform: translateY(-1px);
|
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<!-- Nawigacja -->
|
|
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
|
|
<div class="container-fluid">
|
|
<a class="navbar-brand" href="{{ url_for('dashboard') }}">
|
|
<i class="bi bi-code-square me-2"></i>QL Platform
|
|
</a>
|
|
<div class="navbar-text text-light">
|
|
<span class="badge bg-light text-dark me-2">
|
|
<i class="bi bi-person"></i> {{ username }}
|
|
</span>
|
|
<span class="badge bg-light text-dark me-2">
|
|
<i class="bi bi-folder"></i> {{ project }}
|
|
</span>
|
|
<span class="badge bg-light text-dark">
|
|
<i class="bi bi-file-earmark-code"></i> {{ selected_file }}
|
|
</span>
|
|
</div>
|
|
<div class="d-flex">
|
|
<a href="{{ url_for('dashboard') }}" class="btn btn-outline-light btn-sm me-2">
|
|
<i class="bi bi-grid"></i> Dashboard
|
|
</a>
|
|
<a href="{{ url_for('logout') }}" class="btn btn-outline-light btn-sm">
|
|
<i class="bi bi-box-arrow-right"></i> Wyloguj
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
|
|
<div class="container-fluid mt-3">
|
|
<div class="row">
|
|
<!-- Lewy panel - pliki -->
|
|
<div class="col-md-3">
|
|
<div class="card shadow-sm mb-3">
|
|
<div class="card-header bg-dark text-white d-flex justify-content-between align-items-center">
|
|
<h6 class="mb-0">
|
|
<i class="bi bi-files"></i> Pliki projektu
|
|
</h6>
|
|
<span class="badge bg-secondary">{{ files|length }}</span>
|
|
</div>
|
|
<div class="list-group list-group-flush" id="fileList">
|
|
{% for file in files %}
|
|
<a href="/editor/{{ username }}/{{ project }}?file={{ file }}"
|
|
class="list-group-item list-group-item-action d-flex justify-content-between align-items-center {% if file == selected_file %}active bg-primary{% endif %}"
|
|
data-filename="{{ file }}">
|
|
<div>
|
|
<i class="bi
|
|
{% if file.endswith('.ql') %}bi-file-earmark-code text-primary
|
|
{% elif file.endswith('.md') %}bi-file-earmark-text text-success
|
|
{% elif file.endswith('.css') %}bi-file-earmark-text text-info
|
|
{% elif file.endswith('.js') %}bi-file-earmark-code text-warning
|
|
{% elif file.endswith('.json') %}bi-file-earmark-text text-secondary
|
|
{% else %}bi-file-earmark text-muted{% endif %} me-2">
|
|
</i>
|
|
{{ file }}
|
|
</div>
|
|
<small class="{% if file == selected_file %}text-white{% else %}text-muted{% endif %}">
|
|
{{ file_size(file) }}
|
|
</small>
|
|
</a>
|
|
{% endfor %}
|
|
</div>
|
|
<div class="card-footer bg-light">
|
|
<div class="d-grid gap-2">
|
|
<button id="newFileBtn" class="btn btn-outline-primary btn-sm">
|
|
<i class="bi bi-plus-circle"></i> Nowy plik
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Statystyki -->
|
|
<div class="card shadow-sm">
|
|
<div class="card-body">
|
|
<h6 class="card-title"><i class="bi bi-graph-up"></i> Statystyki</h6>
|
|
<div class="small">
|
|
<div class="d-flex justify-content-between">
|
|
<span>Pliki:</span>
|
|
<span class="badge bg-secondary">{{ files|length }}</span>
|
|
</div>
|
|
<div class="d-flex justify-content-between mt-1">
|
|
<span>Rozmiar projektu:</span>
|
|
<span class="badge bg-info">-</span>
|
|
</div>
|
|
<div class="d-flex justify-content-between mt-1">
|
|
<span>Ostatnia zmiana:</span>
|
|
<span class="badge bg-success">teraz</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Główny obszar - edytor -->
|
|
<div class="col-md-9">
|
|
<div class="editor-container">
|
|
<!-- Toolbar edytora -->
|
|
<div class="editor-toolbar d-flex justify-content-between align-items-center">
|
|
<div>
|
|
<h5 class="mb-0">
|
|
<i class="bi bi-file-code text-primary me-2"></i>
|
|
{{ selected_file }}
|
|
<span id="unsavedIndicator"
|
|
class="badge bg-warning text-dark ms-2 d-none">Nie zapisano</span>
|
|
</h5>
|
|
</div>
|
|
<div class="btn-group">
|
|
<button id="saveBtn" class="btn btn-success btn-editor">
|
|
<i class="bi bi-save"></i> Zapisz
|
|
</button>
|
|
<button id="runBtn" class="btn btn-primary btn-editor">
|
|
<i class="bi bi-play"></i> Uruchom
|
|
</button>
|
|
<button id="formatBtn" class="btn btn-secondary btn-editor">
|
|
<i class="bi bi-braces"></i> Formatuj
|
|
</button>
|
|
<button id="downloadBtn" class="btn btn-outline-dark btn-editor">
|
|
<i class="bi bi-download"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Edytor kodu -->
|
|
<div>
|
|
<textarea id="codeEditor" class="form-control border-0" rows="20"
|
|
placeholder="Wpisz swój kod QL tutaj...">{{ content }}</textarea>
|
|
</div>
|
|
|
|
<!-- Status bar -->
|
|
<div class="border-top bg-light p-2 d-flex justify-content-between align-items-center">
|
|
<div>
|
|
<small class="text-muted">
|
|
<span id="lineCount">Linie: {{ content.split('\n')|length }}</span> |
|
|
<span id="charCount">Znaki: {{ content|length }}</span>
|
|
</small>
|
|
</div>
|
|
<div>
|
|
<small class="text-muted" id="statusText">Gotowy</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Konsola -->
|
|
<div class="card shadow-sm mt-4">
|
|
<div class="card-header bg-dark text-white d-flex justify-content-between align-items-center">
|
|
<h6 class="mb-0">
|
|
<i class="bi bi-terminal me-2"></i>Konsola
|
|
</h6>
|
|
<div>
|
|
<button id="clearConsoleBtn" class="btn btn-sm btn-outline-light me-2">
|
|
<i class="bi bi-trash"></i> Wyczyść
|
|
</button>
|
|
<button id="helpConsoleBtn" class="btn btn-sm btn-outline-light">
|
|
<i class="bi bi-question-circle"></i> Pomoc
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="card-body p-0">
|
|
<div id="consoleOutput" class="console-output">
|
|
<div class="console-success">=== QL Console v1.0 ===</div>
|
|
<div class="console-info">Projekt: {{ project }}</div>
|
|
<div class="console-info">Wpisz 'help' aby uzyskać pomoc</div>
|
|
<div class="console-info">Wpisz 'ls' aby zobaczyć pliki</div>
|
|
</div>
|
|
<div class="input-group p-3 bg-secondary bg-opacity-10">
|
|
<span class="input-group-text bg-dark text-success border-dark">
|
|
<i class="bi bi-terminal"></i> >
|
|
</span>
|
|
<input type="text" id="consoleInput" class="form-control bg-dark text-light border-dark"
|
|
placeholder="Wpisz komendę (help, ls, run, clear...)" autocomplete="off">
|
|
<button id="consoleSend" class="btn btn-success">
|
|
<i class="bi bi-arrow-return-left"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Modal do tworzenia nowego pliku -->
|
|
<div class="modal fade" id="newFileModal" tabindex="-1">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title"><i class="bi bi-file-earmark-plus"></i> Nowy plik</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="mb-3">
|
|
<label for="fileName" class="form-label">Nazwa pliku:</label>
|
|
<input type="text" class="form-control" id="fileName"
|
|
placeholder="np. program.ql, utils.js, style.css">
|
|
<div class="form-text">
|
|
Użyj odpowiedniego rozszerzenia: .ql, .js, .css, .md, .txt
|
|
</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="fileContent" class="form-label">Zawartość (opcjonalnie):</label>
|
|
<textarea class="form-control" id="fileContent" rows="5" placeholder="// Twój kod..."></textarea>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Anuluj</button>
|
|
<button type="button" class="btn btn-primary" id="createFileBtn">
|
|
<i class="bi bi-plus-circle"></i> Utwórz
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Bootstrap -->
|
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
|
|
|
<script>
|
|
// =================== GLOBALNE ZMIENNE ===================
|
|
const username = '{{ username }}';
|
|
const project = '{{ project }}';
|
|
const currentFile = '{{ selected_file }}';
|
|
let isDirty = false;
|
|
let autoSaveTimer = null;
|
|
|
|
// =================== INICJALIZACJA ===================
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|
console.log('QL Editor initialized for:', username, project, currentFile);
|
|
initEditor();
|
|
initConsole();
|
|
initEventListeners();
|
|
updateStats();
|
|
});
|
|
|
|
// =================== FUNKCJE EDYTORA ===================
|
|
function initEditor() {
|
|
const editor = document.getElementById('codeEditor');
|
|
|
|
// Śledź zmiany w edytorze
|
|
editor.addEventListener('input', function () {
|
|
isDirty = true;
|
|
document.getElementById('unsavedIndicator').classList.remove('d-none');
|
|
updateStats();
|
|
updateStatus('Masz niezapisane zmiany');
|
|
});
|
|
|
|
// Skróty klawiaturowe
|
|
editor.addEventListener('keydown', function (e) {
|
|
// Ctrl+S - zapisz
|
|
if (e.ctrlKey && e.key === 's') {
|
|
e.preventDefault();
|
|
saveFile();
|
|
}
|
|
// Ctrl+Enter - uruchom
|
|
if (e.ctrlKey && e.key === 'Enter') {
|
|
e.preventDefault();
|
|
runCode();
|
|
}
|
|
// Ctrl+F - formatuj
|
|
if (e.ctrlKey && e.key === 'f') {
|
|
e.preventDefault();
|
|
formatCode();
|
|
}
|
|
});
|
|
|
|
// Auto-zapis co 30 sekund
|
|
autoSaveTimer = setInterval(function () {
|
|
if (isDirty) {
|
|
autoSave();
|
|
}
|
|
}, 30000);
|
|
|
|
console.log('Editor initialized');
|
|
}
|
|
|
|
function updateStats() {
|
|
const editor = document.getElementById('codeEditor');
|
|
const lines = editor.value.split('\n').length;
|
|
const chars = editor.value.length;
|
|
|
|
document.getElementById('lineCount').textContent = `Linie: ${lines}`;
|
|
document.getElementById('charCount').textContent = `Znaki: ${chars}`;
|
|
}
|
|
|
|
function updateStatus(message) {
|
|
document.getElementById('statusText').textContent = message;
|
|
setTimeout(() => {
|
|
if (isDirty) {
|
|
document.getElementById('statusText').textContent = 'Masz niezapisane zmiany';
|
|
} else {
|
|
document.getElementById('statusText').textContent = 'Gotowy';
|
|
}
|
|
}, 3000);
|
|
}
|
|
|
|
// =================== FUNKCJE ZAPISU ===================
|
|
async function saveFile() {
|
|
const content = document.getElementById('codeEditor').value;
|
|
const filename = currentFile;
|
|
|
|
if (!content.trim()) {
|
|
showNotification('Plik jest pusty!', 'warning');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
updateStatus('Zapisywanie...');
|
|
|
|
const response = await fetch(`/editor/${username}/${project}/save`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
filename: filename,
|
|
content: content
|
|
})
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (response.ok) {
|
|
showNotification('✓ Plik zapisany pomyślnie!', 'success');
|
|
isDirty = false;
|
|
document.getElementById('unsavedIndicator').classList.add('d-none');
|
|
updateStatus('Plik zapisany');
|
|
addConsoleMessage('✓ Plik zapisany', 'success');
|
|
} else {
|
|
throw new Error(data.error || 'Błąd zapisu');
|
|
}
|
|
} catch (error) {
|
|
showNotification(`✗ Błąd: ${error.message}`, 'danger');
|
|
updateStatus(`Błąd: ${error.message}`);
|
|
addConsoleMessage(`✗ Błąd zapisu: ${error.message}`, 'error');
|
|
}
|
|
}
|
|
|
|
function autoSave() {
|
|
if (!isDirty) return;
|
|
|
|
const content = document.getElementById('codeEditor').value;
|
|
const filename = currentFile;
|
|
|
|
fetch(`/editor/${username}/${project}/save`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
filename: filename,
|
|
content: content
|
|
})
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.status === 'ok') {
|
|
addConsoleMessage('✓ Auto-zapisano plik', 'success');
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Auto-save error:', error);
|
|
});
|
|
}
|
|
|
|
async function runCode() {
|
|
const code = document.getElementById('codeEditor').value;
|
|
|
|
if (!code.trim()) {
|
|
showNotification('Brak kodu do uruchomienia!', 'warning');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
// Użyj prawdziwego backendu zamiast symulacji
|
|
const response = await fetch(`/editor/${username}/${project}/execute`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({code: code})
|
|
});
|
|
|
|
const data = await response.json();
|
|
console.log('Backend response:', data); // DODAJ TE LINIĘ DO DEBUGOWANIA
|
|
|
|
if (data.status === 'success') {
|
|
// WYŚWIETL TYLKO OUTPUT Z PRINT() - bez dodatkowych komunikatów
|
|
if (data.output && data.output.length > 0) {
|
|
console.log('Output array:', data.output); // DODAJ TE LINIĘ
|
|
|
|
data.output.forEach(line => {
|
|
console.log('Processing line:', line); // DODAJ TE LINIĘ
|
|
if (line && line.trim()) {
|
|
addConsoleMessage(line, 'response');
|
|
}
|
|
});
|
|
} else {
|
|
addConsoleMessage('(brak output)', 'info');
|
|
}
|
|
} else {
|
|
// Tylko błędy
|
|
if (data.output && data.output.length > 0) {
|
|
addConsoleMessage(data.output[0], 'error');
|
|
}
|
|
}
|
|
} catch (error) {
|
|
addConsoleMessage(`Błąd: ${error.message}`, 'error');
|
|
}
|
|
}
|
|
function formatCode() {
|
|
const editor = document.getElementById('codeEditor');
|
|
let code = editor.value;
|
|
|
|
// Proste formatowanie kodu QL
|
|
code = code
|
|
.replace(/\t/g, ' ') // Zamień taby na 4 spacje
|
|
.replace(/\n\s*\n\s*\n/g, '\n\n') // Usuń nadmiarowe puste linie
|
|
.replace(/\s+$/gm, ''); // Usuń białe znaki na końcu linii
|
|
|
|
// Wcięcia dla bloków
|
|
let indent = 0;
|
|
const lines = code.split('\n');
|
|
const formattedLines = [];
|
|
|
|
for (let line of lines) {
|
|
line = line.trim();
|
|
|
|
// Zmniejsz wcięcie przed zamknięciem bloku
|
|
if (line.includes('}')) {
|
|
indent = Math.max(0, indent - 1);
|
|
}
|
|
|
|
// Dodaj wcięcie
|
|
const indentedLine = ' '.repeat(indent) + line;
|
|
formattedLines.push(indentedLine);
|
|
|
|
// Zwiększ wcięcie po otwarciu bloku
|
|
if (line.includes('{')) {
|
|
indent++;
|
|
}
|
|
}
|
|
|
|
editor.value = formattedLines.join('\n');
|
|
isDirty = true;
|
|
updateStats();
|
|
addConsoleMessage('✓ Kod sformatowany', 'success');
|
|
showNotification('Kod sformatowany!', 'info');
|
|
}
|
|
|
|
// =================== FUNKCJE PLIKÓW ===================
|
|
async function createFile() {
|
|
const filename = document.getElementById('fileName').value.trim();
|
|
const content = document.getElementById('fileContent').value;
|
|
|
|
if (!filename) {
|
|
showNotification('Podaj nazwę pliku!', 'warning');
|
|
return;
|
|
}
|
|
|
|
// Sprawdź rozszerzenie
|
|
if (!filename.includes('.')) {
|
|
showNotification('Dodaj rozszerzenie pliku (np. .ql, .js, .css)', 'warning');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const response = await fetch(`/editor/${username}/${project}/create`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
filename: filename,
|
|
content: content || `# ${filename}\n# Utworzono: ${new Date().toLocaleString()}\n`
|
|
})
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (response.ok) {
|
|
showNotification(`✓ Utworzono plik: ${filename}`, 'success');
|
|
addConsoleMessage(`✓ Utworzono plik: ${filename}`, 'success');
|
|
|
|
// Zamknij modal
|
|
const modal = bootstrap.Modal.getInstance(document.getElementById('newFileModal'));
|
|
modal.hide();
|
|
|
|
// Odśwież stronę z nowym plikiem
|
|
setTimeout(() => {
|
|
window.location.href = `/editor/${username}/${project}?file=${filename}`;
|
|
}, 500);
|
|
} else {
|
|
throw new Error(data.error || 'Błąd tworzenia pliku');
|
|
}
|
|
} catch (error) {
|
|
showNotification(`✗ Błąd: ${error.message}`, 'danger');
|
|
}
|
|
}
|
|
|
|
function downloadFile() {
|
|
const content = document.getElementById('codeEditor').value;
|
|
const blob = new Blob([content], {type: 'text/plain'});
|
|
const url = URL.createObjectURL(blob);
|
|
const a = document.createElement('a');
|
|
a.href = url;
|
|
a.download = currentFile;
|
|
document.body.appendChild(a);
|
|
a.click();
|
|
document.body.removeChild(a);
|
|
URL.revokeObjectURL(url);
|
|
|
|
addConsoleMessage(`✓ Pobrano plik: ${currentFile}`, 'success');
|
|
showNotification(`Pobrano plik: ${currentFile}`, 'info');
|
|
}
|
|
|
|
// =================== KONSOLA ===================
|
|
function initConsole() {
|
|
const consoleInput = document.getElementById('consoleInput');
|
|
const consoleOutput = document.getElementById('consoleOutput');
|
|
|
|
// Komendy konsoli
|
|
const commands = {
|
|
'help': () => {
|
|
return `Dostępne komendy:
|
|
help - wyświetla tę pomoc
|
|
ls - lista plików w projekcie
|
|
clear - czyści konsolę
|
|
run - uruchamia aktualny plik
|
|
status - pokazuje status projektu
|
|
time - pokazuje aktualny czas
|
|
echo [text] - wyświetla tekst
|
|
save - zapisuje aktualny plik
|
|
format - formatuje kod`;
|
|
},
|
|
|
|
'ls': async () => {
|
|
try {
|
|
const response = await fetch(`/editor/${username}/${project}/console`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
command: 'ls'
|
|
})
|
|
});
|
|
|
|
const data = await response.json();
|
|
return data.output || 'Brak odpowiedzi';
|
|
} catch (error) {
|
|
return `Błąd: ${error.message}`;
|
|
}
|
|
},
|
|
|
|
'clear': () => {
|
|
consoleOutput.innerHTML = '';
|
|
addConsoleMessage('=== QL Console v1.0 ===', 'success');
|
|
return 'Konsola wyczyszczona';
|
|
},
|
|
|
|
'run': () => {
|
|
runCode();
|
|
return 'Uruchamianie kodu...';
|
|
},
|
|
|
|
'status': () => {
|
|
const editor = document.getElementById('codeEditor');
|
|
const lines = editor.value.split('\n').length;
|
|
const chars = editor.value.length;
|
|
return `Projekt: ${project}
|
|
Plik: ${currentFile}
|
|
Linie: ${lines}
|
|
Znaki: ${chars}
|
|
Zmiany: ${isDirty ? 'TAK' : 'NIE'}
|
|
Rozmiar: ${fileSizeString(chars)}`;
|
|
},
|
|
|
|
'time': () => new Date().toLocaleString(),
|
|
|
|
'echo': (args) => args.join(' '),
|
|
|
|
'save': () => {
|
|
saveFile();
|
|
return 'Zapisywanie pliku...';
|
|
},
|
|
|
|
'format': () => {
|
|
formatCode();
|
|
return 'Formatowanie kodu...';
|
|
}
|
|
};
|
|
|
|
function fileSizeString(bytes) {
|
|
if (bytes < 1024) return bytes + ' B';
|
|
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB';
|
|
return (bytes / (1024 * 1024)).toFixed(1) + ' MB';
|
|
}
|
|
|
|
async function executeCommand(input) {
|
|
const parts = input.trim().split(' ');
|
|
const cmd = parts[0].toLowerCase();
|
|
const args = parts.slice(1);
|
|
|
|
if (commands[cmd]) {
|
|
try {
|
|
const result = await commands[cmd](args);
|
|
if (typeof result === 'string') {
|
|
return result;
|
|
}
|
|
} catch (error) {
|
|
return `Błąd wykonania: ${error.message}`;
|
|
}
|
|
} else if (cmd) {
|
|
return `Nieznana komenda: ${cmd}\nWpisz 'help' aby zobaczyć dostępne komendy`;
|
|
}
|
|
|
|
return '';
|
|
}
|
|
|
|
function processCommand() {
|
|
const input = consoleInput.value.trim();
|
|
if (!input) return;
|
|
|
|
// Wyświetl komendę
|
|
addConsoleLine(`> ${input}`, 'command');
|
|
|
|
// Wykonaj komendę
|
|
executeCommand(input).then(output => {
|
|
if (output) {
|
|
addConsoleLine(output);
|
|
}
|
|
});
|
|
|
|
// Wyczyść input
|
|
consoleInput.value = '';
|
|
}
|
|
|
|
// Event listeners dla konsoli
|
|
consoleInput.addEventListener('keypress', function (e) {
|
|
if (e.key === 'Enter') {
|
|
processCommand();
|
|
}
|
|
});
|
|
|
|
document.getElementById('consoleSend').addEventListener('click', processCommand);
|
|
document.getElementById('clearConsoleBtn').addEventListener('click', () => {
|
|
consoleOutput.innerHTML = '';
|
|
addConsoleLine('=== QL Console v1.0 ===', 'success');
|
|
addConsoleLine('Konsola wyczyszczona', 'success');
|
|
});
|
|
|
|
document.getElementById('helpConsoleBtn').addEventListener('click', () => {
|
|
addConsoleLine(`> help`, 'command');
|
|
addConsoleLine(commands.help());
|
|
});
|
|
|
|
// Auto-focus na konsolę
|
|
setTimeout(() => {
|
|
consoleInput.focus();
|
|
}, 100);
|
|
|
|
console.log('Console initialized');
|
|
}
|
|
|
|
function addConsoleLine(text, type = 'response') {
|
|
const consoleOutput = document.getElementById('consoleOutput');
|
|
const line = document.createElement('div');
|
|
line.className = `console-${type}`;
|
|
line.textContent = text;
|
|
consoleOutput.appendChild(line);
|
|
consoleOutput.scrollTop = consoleOutput.scrollHeight;
|
|
}
|
|
|
|
function addConsoleMessage(message, type = 'info') {
|
|
addConsoleLine(message, type);
|
|
}
|
|
|
|
// =================== EVENT LISTENERS ===================
|
|
function initEventListeners() {
|
|
// Zapisz plik
|
|
document.getElementById('saveBtn').addEventListener('click', saveFile);
|
|
|
|
// Uruchom kod
|
|
document.getElementById('runBtn').addEventListener('click', runCode);
|
|
|
|
// Formatuj kod
|
|
document.getElementById('formatBtn').addEventListener('click', formatCode);
|
|
|
|
// Nowy plik
|
|
document.getElementById('newFileBtn').addEventListener('click', () => {
|
|
const modal = new bootstrap.Modal(document.getElementById('newFileModal'));
|
|
modal.show();
|
|
|
|
// Ustaw focus na input
|
|
setTimeout(() => {
|
|
document.getElementById('fileName').focus();
|
|
}, 500);
|
|
});
|
|
|
|
// Utwórz plik (w modal)
|
|
document.getElementById('createFileBtn').addEventListener('click', createFile);
|
|
|
|
// Pobierz plik
|
|
document.getElementById('downloadBtn').addEventListener('click', downloadFile);
|
|
|
|
// Enter w modal
|
|
document.getElementById('fileName').addEventListener('keypress', function (e) {
|
|
if (e.key === 'Enter') {
|
|
e.preventDefault();
|
|
createFile();
|
|
}
|
|
});
|
|
|
|
console.log('Event listeners initialized');
|
|
}
|
|
|
|
// =================== POMOCNICZE ===================
|
|
function showNotification(message, type = 'info') {
|
|
// Utwórz powiadomienie Bootstrap
|
|
const alert = document.createElement('div');
|
|
alert.className = `alert alert-${type} alert-dismissible fade show position-fixed`;
|
|
alert.style.cssText = `
|
|
position: fixed;
|
|
top: 20px;
|
|
right: 20px;
|
|
z-index: 9999;
|
|
min-width: 300px;
|
|
max-width: 400px;
|
|
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
|
`;
|
|
alert.innerHTML = `
|
|
<div class="d-flex align-items-center">
|
|
<i class="bi ${type === 'success' ? 'bi-check-circle' :
|
|
type === 'warning' ? 'bi-exclamation-triangle' :
|
|
type === 'danger' ? 'bi-x-circle' : 'bi-info-circle'}
|
|
me-2"></i>
|
|
<span>${message}</span>
|
|
</div>
|
|
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
|
`;
|
|
|
|
document.body.appendChild(alert);
|
|
|
|
// Auto-ukrywanie
|
|
setTimeout(() => {
|
|
if (alert.parentNode) {
|
|
bootstrap.Alert.getOrCreateInstance(alert).close();
|
|
}
|
|
}, 3000);
|
|
}
|
|
|
|
// Ostrzeżenie przed opuszczeniem strony
|
|
window.addEventListener('beforeunload', function (e) {
|
|
if (isDirty) {
|
|
e.preventDefault();
|
|
e.returnValue = 'Masz niezapisane zmiany. Czy na pewno chcesz opuścić stronę?';
|
|
return e.returnValue;
|
|
}
|
|
});
|
|
|
|
// Czyszczenie interwału przy zamknięciu
|
|
window.addEventListener('unload', function () {
|
|
if (autoSaveTimer) {
|
|
clearInterval(autoSaveTimer);
|
|
}
|
|
});
|
|
|
|
// =================== INIT LOG ===================
|
|
console.log('QL Editor script loaded successfully');
|
|
</script>
|
|
</body>
|
|
</html> |