The commit introduces a new application for browsing Gitea issues, consisting of a FastAPI backend with a GiteaClient class for API interactions, a WebSocket endpoint for real-time updates, and an HTML frontend with login form and issue display. The .gitignore file is also added to exclude .env files from version control.
315 lines
8.2 KiB
HTML
315 lines
8.2 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Mr. Issue</title>
|
|
<style>
|
|
* {
|
|
margin: 0;
|
|
padding: 0;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
body {
|
|
font-family: Arial, sans-serif;
|
|
background: #fff;
|
|
color: #202124;
|
|
padding: 20px;
|
|
}
|
|
|
|
.container {
|
|
max-width: 900px;
|
|
margin: 0 auto;
|
|
}
|
|
|
|
h1 {
|
|
text-align: center;
|
|
color: #4285f4;
|
|
margin-bottom: 30px;
|
|
font-size: 48px;
|
|
font-weight: normal;
|
|
}
|
|
|
|
.login-form {
|
|
background: #fff;
|
|
border: 1px solid #dfe1e5;
|
|
border-radius: 8px;
|
|
padding: 40px;
|
|
margin-bottom: 30px;
|
|
}
|
|
|
|
.form-group {
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
label {
|
|
display: block;
|
|
margin-bottom: 8px;
|
|
color: #5f6368;
|
|
font-size: 14px;
|
|
}
|
|
|
|
input[type="text"],
|
|
input[type="password"] {
|
|
width: 100%;
|
|
padding: 12px;
|
|
border: 1px solid #dfe1e5;
|
|
border-radius: 4px;
|
|
font-size: 14px;
|
|
}
|
|
|
|
input[type="text"]:focus,
|
|
input[type="password"]:focus {
|
|
outline: none;
|
|
border-color: #4285f4;
|
|
}
|
|
|
|
button {
|
|
background: #4285f4;
|
|
color: #fff;
|
|
border: none;
|
|
padding: 12px 24px;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
}
|
|
|
|
button:hover {
|
|
background: #3367d6;
|
|
}
|
|
|
|
button:disabled {
|
|
background: #dadce0;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
.editor-container {
|
|
display: none;
|
|
}
|
|
|
|
.editor-container.active {
|
|
display: block;
|
|
}
|
|
|
|
.progress-log {
|
|
background: #f8f9fa;
|
|
border: 1px solid #dfe1e5;
|
|
border-radius: 8px;
|
|
padding: 20px;
|
|
margin-bottom: 20px;
|
|
max-height: 300px;
|
|
overflow-y: auto;
|
|
display: none;
|
|
}
|
|
|
|
.progress-log.active {
|
|
display: block;
|
|
}
|
|
|
|
.log-entry {
|
|
padding: 8px 0;
|
|
border-bottom: 1px solid #e8eaed;
|
|
font-size: 14px;
|
|
color: #5f6368;
|
|
}
|
|
|
|
.log-entry:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.log-entry.error {
|
|
color: #d93025;
|
|
}
|
|
|
|
.log-entry.complete {
|
|
color: #1e8e3e;
|
|
font-weight: 500;
|
|
}
|
|
|
|
textarea {
|
|
width: 100%;
|
|
min-height: 400px;
|
|
padding: 16px;
|
|
border: 1px solid #dfe1e5;
|
|
border-radius: 8px;
|
|
font-size: 14px;
|
|
font-family: 'Courier New', monospace;
|
|
resize: vertical;
|
|
}
|
|
|
|
textarea:focus {
|
|
outline: none;
|
|
border-color: #4285f4;
|
|
}
|
|
|
|
.submit-btn {
|
|
margin-top: 16px;
|
|
width: 100%;
|
|
}
|
|
|
|
.preview {
|
|
background: #f8f9fa;
|
|
border: 1px solid #dfe1e5;
|
|
border-radius: 8px;
|
|
padding: 20px;
|
|
margin-top: 20px;
|
|
min-height: 100px;
|
|
font-size: 14px;
|
|
line-height: 1.6;
|
|
}
|
|
|
|
.logout-btn {
|
|
background: #5f6368;
|
|
float: right;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.logout-btn:hover {
|
|
background: #3c4043;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<h1>Mr. Issue</h1>
|
|
|
|
<div class="login-form" id="loginForm">
|
|
<div class="form-group">
|
|
<label for="username">Username</label>
|
|
<input type="text" id="username" required>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="password">Password</label>
|
|
<input type="password" id="password" required>
|
|
</div>
|
|
<button onclick="login()">Sign In</button>
|
|
</div>
|
|
|
|
<div class="editor-container" id="editorContainer">
|
|
<button class="logout-btn" onclick="logout()">Sign Out</button>
|
|
<div style="clear: both;"></div>
|
|
|
|
<div class="progress-log" id="progressLog"></div>
|
|
|
|
<textarea id="promptEditor" placeholder="Describe the issues you want to create...
|
|
|
|
Example:
|
|
Create issues for project 'my-app' with the following tasks:
|
|
- Fix login bug
|
|
- Add dark mode
|
|
- Update documentation"></textarea>
|
|
|
|
<div class="preview" id="preview"></div>
|
|
|
|
<button class="submit-btn" id="submitBtn" onclick="submitPrompt()">Create Issues</button>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
let ws = null;
|
|
let credentials = null;
|
|
|
|
const loginForm = document.getElementById('loginForm');
|
|
const editorContainer = document.getElementById('editorContainer');
|
|
const progressLog = document.getElementById('progressLog');
|
|
const promptEditor = document.getElementById('promptEditor');
|
|
const preview = document.getElementById('preview');
|
|
const submitBtn = document.getElementById('submitBtn');
|
|
|
|
promptEditor.addEventListener('input', () => {
|
|
preview.textContent = promptEditor.value;
|
|
});
|
|
|
|
function login() {
|
|
const username = document.getElementById('username').value;
|
|
const password = document.getElementById('password').value;
|
|
|
|
if (!username || !password) {
|
|
alert('Please enter username and password');
|
|
return;
|
|
}
|
|
|
|
credentials = { username, password };
|
|
loginForm.style.display = 'none';
|
|
editorContainer.classList.add('active');
|
|
}
|
|
|
|
function logout() {
|
|
credentials = null;
|
|
if (ws) {
|
|
ws.close();
|
|
ws = null;
|
|
}
|
|
loginForm.style.display = 'block';
|
|
editorContainer.classList.remove('active');
|
|
progressLog.classList.remove('active');
|
|
progressLog.innerHTML = '';
|
|
promptEditor.value = '';
|
|
preview.textContent = '';
|
|
}
|
|
|
|
function addLog(message, type = 'log') {
|
|
const entry = document.createElement('div');
|
|
entry.className = `log-entry ${type}`;
|
|
entry.textContent = message;
|
|
progressLog.appendChild(entry);
|
|
progressLog.scrollTop = progressLog.scrollHeight;
|
|
}
|
|
|
|
function submitPrompt() {
|
|
const prompt = promptEditor.value.trim();
|
|
if (!prompt) {
|
|
alert('Please enter a prompt');
|
|
return;
|
|
}
|
|
|
|
progressLog.innerHTML = '';
|
|
progressLog.classList.add('active');
|
|
submitBtn.disabled = true;
|
|
|
|
ws = new WebSocket('ws://127.0.0.1:8590/ws');
|
|
|
|
ws.onopen = () => {
|
|
addLog('Connected to server');
|
|
ws.send(JSON.stringify(credentials));
|
|
};
|
|
|
|
ws.onmessage = (event) => {
|
|
const data = JSON.parse(event.data);
|
|
|
|
if (data.type === 'log') {
|
|
addLog(data.message);
|
|
} else if (data.type === 'error') {
|
|
addLog(data.message, 'error');
|
|
submitBtn.disabled = false;
|
|
} else if (data.type === 'complete') {
|
|
addLog(data.message, 'complete');
|
|
submitBtn.disabled = false;
|
|
ws.close();
|
|
}
|
|
};
|
|
|
|
ws.onerror = () => {
|
|
addLog('Connection error', 'error');
|
|
submitBtn.disabled = false;
|
|
};
|
|
|
|
ws.onclose = () => {
|
|
if (submitBtn.disabled) {
|
|
submitBtn.disabled = false;
|
|
}
|
|
};
|
|
|
|
setTimeout(() => {
|
|
if (ws.readyState === WebSocket.OPEN) {
|
|
ws.send(JSON.stringify({ prompt }));
|
|
}
|
|
}, 100);
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|
|
|