-
Step 2: Generate Visualization
-
Create visual representation of the extracted DocTags
-
-
+
+
+
+
+ Step 1: Analyze PDF
+
+
Extract DocTags structure from the selected PDF page
+
+
+
+
+
+
+
+ Step 2: Generate Visualization
+
+
Create visual representation of the extracted DocTags
+
+
+
+
+
+
+
+ Step 3: Extract Pictures
+
+
Extract images from the document based on DocTags
+
+
+
-
-
Step 3: Extract Pictures
-
Extract images from the document based on DocTags
-
-
+
+
+
+
Visualization Result
+
-
-
+
-
-
Visualization Result
-
![Visualization]()
-
-
-
-
-
+
+
+
+
diff --git a/static/app.js b/static/app.js
index a733be2..84d08ed 100644
--- a/static/app.js
+++ b/static/app.js
@@ -2,10 +2,47 @@
const activeTasks = {};
let pollingInterval = null;
+// Helper functions for UI
+const UI = {
+ showLoader: (elementId) => {
+ const el = document.getElementById(elementId);
+ el.innerHTML = '
Processing...';
+ },
+ showSuccess: (elementId, message = 'Completed') => {
+ const el = document.getElementById(elementId);
+ el.innerHTML = `
✓ ${message}`;
+ },
+ showError: (elementId, message = 'Failed') => {
+ const el = document.getElementById(elementId);
+ el.innerHTML = `
✗ ${message}`;
+ },
+ showOutput: (content) => {
+ const outputDiv = document.getElementById('output');
+ outputDiv.textContent = content;
+ outputDiv.classList.remove('hidden');
+ // Scroll to show output
+ outputDiv.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
+ },
+ showImage: (src) => {
+ const img = document.getElementById('result-image');
+ const container = document.getElementById('image-container');
+ img.src = src;
+ container.classList.remove('hidden');
+ // Scroll to show image
+ container.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
+ },
+ enableButton: (buttonId) => {
+ document.getElementById(buttonId).disabled = false;
+ },
+ disableButton: (buttonId) => {
+ document.getElementById(buttonId).disabled = true;
+ }
+};
+
// Load PDF files on page load
window.addEventListener('DOMContentLoaded', function() {
const pdfStatus = document.getElementById('pdf-load-status');
- pdfStatus.innerHTML = '
Loading PDF files...';
+ pdfStatus.innerHTML = '
Loading PDF files...';
fetch('/pdf-files')
.then(response => {
@@ -17,7 +54,7 @@ window.addEventListener('DOMContentLoaded', function() {
.then(data => {
const select = document.getElementById('pdf_file');
if (data.length === 0) {
- pdfStatus.innerHTML = '
No PDF files found in the current directory';
+ UI.showError('pdf-load-status', 'No PDF files found in the current directory');
return;
}
@@ -27,11 +64,11 @@ window.addEventListener('DOMContentLoaded', function() {
option.textContent = file;
select.appendChild(option);
});
- pdfStatus.innerHTML = '
Loaded ' + data.length + ' PDF files';
+ UI.showSuccess('pdf-load-status', `Loaded ${data.length} PDF files`);
})
.catch(error => {
console.error('Error loading PDFs:', error);
- pdfStatus.innerHTML = '
Error: ' + error.message + '';
+ UI.showError('pdf-load-status', `Error: ${error.message}`);
});
// Run an environment check on startup
@@ -44,55 +81,59 @@ function checkEnvironment() {
const envDetails = document.getElementById('env-details');
envDiv.classList.remove('hidden');
- envDetails.innerHTML = '
Checking environment...';
+ envDetails.innerHTML = '
Checking environment...';
fetch('/check-environment')
.then(response => response.json())
.then(data => {
- let html = '
';
+ let html = '';
// Current working directory
- html += '
- Working directory:
' + data.cwd + ' ';
+ html += `
Working directory: ${data.cwd}
`;
// Python version
- html += '
- Python version:
' + data.python_version + ' ';
+ html += `
Python version: ${data.python_version}
`;
// Required scripts check
if (data.missing_scripts.length === 0) {
- html += '
- ✓ All required scripts found
';
+ html += '
All required scripts found
';
} else {
- html += '
- ✗ Missing scripts:
' + data.missing_scripts.join(', ') + ' ';
+ html += `
Missing scripts: ${data.missing_scripts.join(', ')}
`;
}
// PDF files check
if (data.pdf_files.length > 0) {
- html += '
- ✓ Found ' + data.pdf_files.length + ' PDF files:
' + data.pdf_files.join(', ') + ' ';
+ html += `
Found ${data.pdf_files.length} PDF files
`;
+ html += '
PDF Files
';
+ data.pdf_files.forEach(file => {
+ html += `${file} `;
+ });
+ html += '
';
} else {
- html += '
- ✗ No PDF files found in the working directory
';
+ html += '
No PDF files found in the working directory
';
}
// Results directory check
if (data.results_dir_exists) {
- html += '
- ✓ Results directory exists
';
+ html += '
Results directory exists
';
if (data.results_dir_writable) {
- html += '
- ✓ Results directory is writable
';
+ html += '
Results directory is writable
';
} else {
- html += '
- ✗ Results directory is not writable
';
+ html += '
Results directory is not writable
';
}
} else {
- html += '
- ✗ Results directory does not exist
';
+ html += '
Results directory does not exist
';
}
- html += '';
+ html += '
';
- // List of all files for debugging
- html += 'All files in directory (' + data.files.length + ' files)
' +
- data.files.join('\n') + ' ';
+ // List of all files for debugging (collapsible)
+ html += `All files in directory (${data.files.length} files)
${data.files.join('\n')} `;
envDetails.innerHTML = html;
})
.catch(error => {
- envDetails.innerHTML = 'Error checking environment: ' + error.message + '
';
+ envDetails.innerHTML = `Error checking environment: ${error.message}
`;
});
}
@@ -101,9 +142,7 @@ function manuallyRunScript() {
const command = prompt("Enter command to run (e.g., 'python analyzer.py --image document.pdf --page 1')");
if (!command) return;
- const outputDiv = document.getElementById('output');
- outputDiv.textContent = 'Running command: ' + command + '\nPlease wait...';
- outputDiv.classList.remove('hidden');
+ UI.showOutput(`Running command: ${command}\nPlease wait...`);
// Create form data
const formData = new FormData();
@@ -116,13 +155,12 @@ function manuallyRunScript() {
})
.then(response => response.json())
.then(data => {
- outputDiv.textContent = 'Command: ' + command + '\n\n' +
- (data.success ? 'Success!\n\n' : 'Failed!\n\n') +
- (data.output || '') +
- (data.error ? '\n\nError: ' + data.error : '');
+ UI.showOutput(
+ `Command: ${command}\n\n${data.success ? 'Success!\n\n' : 'Failed!\n\n'}${data.output || ''}${data.error ? '\n\nError: ' + data.error : ''}`
+ );
})
.catch(error => {
- outputDiv.textContent = 'Error running command: ' + error.message;
+ UI.showOutput(`Error running command: ${error.message}`);
});
}
@@ -162,27 +200,24 @@ function pollTasks() {
if (data.done) {
if (data.success) {
// Task completed successfully
- statusElement.innerHTML = '✓ Completed';
+ UI.showSuccess(`${taskInfo.type}-status`, 'Completed');
// Enable button
- document.getElementById(`${taskInfo.type}-btn`).disabled = false;
+ UI.enableButton(`${taskInfo.type}-btn`);
// Display output
- const outputDiv = document.getElementById('output');
- outputDiv.textContent = data.output;
- outputDiv.classList.remove('hidden');
+ UI.showOutput(data.output);
// Show image if available for visualizer
if (taskInfo.type === 'visualizer' && data.image_file) {
- document.getElementById('result-image').src = '/' + data.image_file + '?t=' + new Date().getTime();
- document.getElementById('image-container').classList.remove('hidden');
+ UI.showImage(`/${data.image_file}?t=${new Date().getTime()}`);
}
// Enable next step button if applicable
if (taskInfo.type === 'analyzer') {
- document.getElementById('visualizer-btn').disabled = false;
+ UI.enableButton('visualizer-btn');
} else if (taskInfo.type === 'visualizer') {
- document.getElementById('extractor-btn').disabled = false;
+ UI.enableButton('extractor-btn');
}
// Remove from active tasks
@@ -190,13 +225,11 @@ function pollTasks() {
checkAndStopPolling();
} else {
// Task failed
- statusElement.innerHTML = '✗ Failed: ' + (data.error || 'Unknown error') + '';
- document.getElementById(`${taskInfo.type}-btn`).disabled = false;
+ UI.showError(`${taskInfo.type}-status`, data.error || 'Unknown error');
+ UI.enableButton(`${taskInfo.type}-btn`);
// Display error output
- const outputDiv = document.getElementById('output');
- outputDiv.textContent = 'Error: ' + (data.error || 'Unknown error');
- outputDiv.classList.remove('hidden');
+ UI.showOutput(`Error: ${data.error || 'Unknown error'}`);
// Remove from active tasks
delete activeTasks[taskId];
@@ -204,11 +237,17 @@ function pollTasks() {
}
} else {
// Still running
- statusElement.innerHTML = 'Running...';
+ statusElement.innerHTML = 'Processing...';
}
})
.catch(error => {
console.error('Error checking task status:', error);
+ UI.showError(`${taskInfo.type}-status`, 'Failed to check status');
+ UI.enableButton(`${taskInfo.type}-btn`);
+
+ // Remove from active tasks to prevent endless error messages
+ delete activeTasks[taskId];
+ checkAndStopPolling();
});
}
}
@@ -224,8 +263,7 @@ function runScript(script) {
}
// Disable button
- const button = document.getElementById(`${script}-btn`);
- button.disabled = true;
+ UI.disableButton(`${script}-btn`);
// Create form data
const formData = new FormData();
@@ -234,8 +272,7 @@ function runScript(script) {
formData.append('adjust', adjust);
// Show running status
- const statusElement = document.getElementById(`${script}-status`);
- statusElement.innerHTML = 'Starting...';
+ UI.showLoader(`${script}-status`);
// Clear previous output
document.getElementById('output').classList.add('hidden');
@@ -251,13 +288,13 @@ function runScript(script) {
case 'analyzer':
endpoint = '/run-analyzer';
// Disable next step buttons
- document.getElementById('visualizer-btn').disabled = true;
- document.getElementById('extractor-btn').disabled = true;
+ UI.disableButton('visualizer-btn');
+ UI.disableButton('extractor-btn');
break;
case 'visualizer':
endpoint = '/run-visualizer';
// Disable next step button
- document.getElementById('extractor-btn').disabled = true;
+ UI.disableButton('extractor-btn');
break;
case 'extractor':
endpoint = '/run-extractor';
@@ -287,31 +324,25 @@ function runScript(script) {
startPolling();
// Update status
- statusElement.innerHTML = 'Running...';
+ UI.showLoader(`${script}-status`);
// Show initial output
- const outputDiv = document.getElementById('output');
- outputDiv.textContent = data.message || 'Task started, please wait...';
- outputDiv.classList.remove('hidden');
+ UI.showOutput(data.message || 'Task started, please wait...');
} else {
// Failed to start task
- statusElement.innerHTML = '✗ Failed to start task';
- button.disabled = false;
+ UI.showError(`${script}-status`, 'Failed to start task');
+ UI.enableButton(`${script}-btn`);
// Show error
- const outputDiv = document.getElementById('output');
- outputDiv.textContent = 'Error: ' + (data.error || 'Failed to start task');
- outputDiv.classList.remove('hidden');
+ UI.showOutput(`Error: ${data.error || 'Failed to start task'}`);
}
})
.catch(error => {
console.error('Error starting task:', error);
- statusElement.innerHTML = '✗ ' + error.message + '';
- button.disabled = false;
+ UI.showError(`${script}-status`, error.message);
+ UI.enableButton(`${script}-btn`);
// Show error
- const outputDiv = document.getElementById('output');
- outputDiv.textContent = 'Error: ' + error.message;
- outputDiv.classList.remove('hidden');
+ UI.showOutput(`Error: ${error.message}`);
});
}
\ No newline at end of file
diff --git a/static/styles.css b/static/styles.css
index 021b2c9..c0c38bb 100644
--- a/static/styles.css
+++ b/static/styles.css
@@ -1,140 +1,431 @@
+/* Modern stylesheet for DocTags Analyzer and Visualizer */
+
+:root {
+ /* Color palette */
+ --bg-color: #f8f1ee;
+ --primary-color: #195162;
+ --secondary-color: #618794;
+ --light-color: #e6eaf1;
+ --mint: #b9e9da;
+ --amber: #ffdb96;
+ --pale-rose: #ffc9c9;
+ --white: #ffffff;
+ --error: #e74c3c;
+ --success: #2ecc71;
+ --warning: #f39c12;
+ --shadow: rgba(25, 81, 98, 0.1);
+
+ /* Typography */
+ --font-sans: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
+ --font-mono: 'Fira Code', 'SF Mono', Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;
+}
+
+/* Base styles */
body {
- font-family: Arial, sans-serif;
- max-width: 800px;
+ font-family: var(--font-sans);
+ background-color: var(--bg-color);
+ color: var(--primary-color);
+ line-height: 1.6;
+ margin: 0;
+ padding: 0;
+}
+
+#app-container {
+ max-width: 1200px;
margin: 0 auto;
- padding: 20px;
+ padding: 2rem;
+}
+
+h1, h2, h3, h4, h5, h6 {
+ color: var(--primary-color);
+ margin-top: 0;
+ font-weight: 600;
}
h1 {
- color: #333;
+ font-size: 2.25rem;
+ margin-bottom: 1.5rem;
+ position: relative;
+ display: inline-block;
}
+h1::after {
+ content: '';
+ position: absolute;
+ width: 50%;
+ height: 4px;
+ background-color: var(--secondary-color);
+ bottom: -8px;
+ left: 0;
+ border-radius: 2px;
+}
+
+h3 {
+ font-size: 1.35rem;
+ color: var(--secondary-color);
+ margin-bottom: 1rem;
+}
+
+/* Card Component */
+.card {
+ background-color: var(--white);
+ border-radius: 12px;
+ box-shadow: 0 8px 16px var(--shadow);
+ padding: 1.5rem;
+ margin-bottom: 1.5rem;
+ transition: transform 0.2s ease, box-shadow 0.2s ease;
+}
+
+.card:hover {
+ transform: translateY(-4px);
+ box-shadow: 0 10px 20px var(--shadow);
+}
+
+/* Form Elements */
.form-group {
- margin-bottom: 15px;
+ margin-bottom: 1.5rem;
}
label {
display: block;
- margin-bottom: 5px;
- font-weight: bold;
+ margin-bottom: 0.5rem;
+ font-weight: 500;
+ color: var(--primary-color);
}
-select, input {
- padding: 8px;
+select, input[type="number"], input[type="text"] {
width: 100%;
- max-width: 300px;
+ padding: 12px 16px;
+ border: 2px solid var(--light-color);
+ border-radius: 8px;
+ background-color: var(--white);
+ font-size: 1rem;
+ color: var(--primary-color);
+ transition: border-color 0.2s ease, box-shadow 0.2s ease;
+ -webkit-appearance: none;
+ appearance: none;
}
+select {
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23618794' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
+ background-repeat: no-repeat;
+ background-position: right 16px center;
+ padding-right: 48px;
+}
+
+select:focus, input:focus {
+ outline: none;
+ border-color: var(--secondary-color);
+ box-shadow: 0 0 0 3px rgba(97, 135, 148, 0.25);
+}
+
+.checkbox-container {
+ display: flex;
+ align-items: center;
+ position: relative;
+ cursor: pointer;
+ user-select: none;
+}
+
+.checkbox-container input {
+ position: absolute;
+ opacity: 0;
+ cursor: pointer;
+ height: 0;
+ width: 0;
+}
+
+.checkmark {
+ height: 24px;
+ width: 24px;
+ background-color: var(--white);
+ border: 2px solid var(--light-color);
+ border-radius: 4px;
+ margin-right: 10px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: all 0.2s ease;
+}
+
+.checkbox-container:hover input ~ .checkmark {
+ background-color: var(--light-color);
+}
+
+.checkbox-container input:checked ~ .checkmark {
+ background-color: var(--secondary-color);
+ border-color: var(--secondary-color);
+}
+
+.checkmark:after {
+ content: "";
+ display: none;
+ width: 5px;
+ height: 10px;
+ border: solid white;
+ border-width: 0 2px 2px 0;
+ transform: rotate(45deg);
+}
+
+.checkbox-container input:checked ~ .checkmark:after {
+ display: block;
+}
+
+/* Buttons */
button {
- padding: 10px 15px;
- background-color: #4CAF50;
+ background-color: var(--secondary-color);
color: white;
border: none;
- border-radius: 4px;
+ border-radius: 8px;
+ padding: 12px 24px;
+ font-size: 1rem;
+ font-weight: 500;
cursor: pointer;
- margin-right: 10px;
- margin-bottom: 10px;
+ transition: background-color 0.2s ease, transform 0.1s ease;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
}
-button:hover {
- background-color: #45a049;
+button:hover:not(:disabled) {
+ background-color: var(--primary-color);
+ transform: translateY(-2px);
+}
+
+button:active:not(:disabled) {
+ transform: translateY(0);
}
button:disabled {
- background-color: #cccccc;
+ background-color: var(--light-color);
+ color: var(--secondary-color);
cursor: not-allowed;
+ opacity: 0.7;
}
-.output {
- margin-top: 20px;
- padding: 15px;
- border: 1px solid #ddd;
- border-radius: 4px;
- background-color: #f9f9f9;
+button svg {
+ margin-right: 8px;
+}
+
+/* Status indicators */
+.status {
+ margin-top: 1rem;
+ font-weight: 500;
+ display: flex;
+ align-items: center;
+}
+
+.error {
+ color: var(--error);
+}
+
+.success {
+ color: var(--success);
+}
+
+.working {
+ color: var(--secondary-color);
+}
+
+.loader {
+ display: inline-block;
+ width: 20px;
+ height: 20px;
+ margin-right: 10px;
+ border: 3px solid var(--light-color);
+ border-radius: 50%;
+ border-top-color: var(--secondary-color);
+ animation: spin 1s ease-in-out infinite;
+}
+
+@keyframes spin {
+ to { transform: rotate(360deg); }
+}
+
+/* Pipeline Container */
+.pipeline-container {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
+ gap: 1.5rem;
+ margin: 2rem 0;
+}
+
+/* Step Cards */
+.step-card {
+ background-color: var(--white);
+ border-radius: 12px;
+ border-left: 5px solid var(--mint);
+ box-shadow: 0 4px 12px var(--shadow);
+ padding: 1.5rem;
+ transition: transform 0.2s ease, box-shadow 0.2s ease;
+ display: flex;
+ flex-direction: column;
+}
+
+.step-card:nth-child(2) {
+ border-left-color: var(--amber);
+}
+
+.step-card:nth-child(3) {
+ border-left-color: var(--pale-rose);
+}
+
+.step-card:hover {
+ transform: translateY(-4px);
+ box-shadow: 0 8px 16px var(--shadow);
+}
+
+.step-card h3 {
+ margin-top: 0;
+ display: flex;
+ align-items: center;
+}
+
+.step-card h3 svg {
+ margin-right: 10px;
+}
+
+.step-card p {
+ color: var(--secondary-color);
+ margin-bottom: 1.5rem;
+ flex-grow: 1;
+}
+
+/* Environment Check Panel */
+#environment-check {
+ background-color: var(--white);
+ border-radius: 12px;
+ box-shadow: 0 4px 12px var(--shadow);
+ padding: 1.5rem;
+ margin-top: 2rem;
+}
+
+.env-success {
+ color: var(--success);
+ display: flex;
+ align-items: center;
+}
+
+.env-error {
+ color: var(--error);
+ display: flex;
+ align-items: center;
+}
+
+.env-success::before, .env-error::before {
+ content: '';
+ display: inline-block;
+ width: 16px;
+ height: 16px;
+ margin-right: 8px;
+ border-radius: 50%;
+}
+
+.env-success::before {
+ background-color: var(--success);
+}
+
+.env-error::before {
+ background-color: var(--error);
+}
+
+/* Output Container */
+#output {
+ background-color: var(--white);
+ border-radius: 12px;
+ padding: 1.5rem;
white-space: pre-wrap;
+ font-family: var(--font-mono);
+ font-size: 0.9rem;
+ line-height: 1.6;
+ color: var(--primary-color);
+ overflow-x: auto;
+ box-shadow: 0 4px 12px var(--shadow);
max-height: 300px;
overflow-y: auto;
}
-.hidden {
- display: none;
+/* Image Container */
+#image-container {
+ background-color: var(--white);
+ border-radius: 12px;
+ padding: 1.5rem;
+ box-shadow: 0 4px 12px var(--shadow);
+ margin-top: 2rem;
}
-img {
- max-width: 100%;
- margin-top: 10px;
- border: 1px solid #ddd;
-}
-
-.container {
- display: flex;
- flex-direction: column;
- gap: 20px;
-}
-
-.step {
- background-color: #f5f5f5;
- padding: 15px;
- border-radius: 5px;
- border-left: 5px solid #4CAF50;
-}
-
-.step h3 {
+#image-container h2 {
margin-top: 0;
+ margin-bottom: 1rem;
+ display: flex;
+ align-items: center;
}
-.status {
- font-weight: bold;
- margin-top: 5px;
-}
-
-.error {
- color: #d32f2f;
-}
-
-.success {
- color: #43a047;
-}
-
-.working {
- color: #1976d2;
-}
-
-.loader {
- border: 4px solid #f3f3f3;
- border-top: 4px solid #3498db;
- border-radius: 50%;
- width: 20px;
- height: 20px;
- animation: spin 2s linear infinite;
+#image-container h2::before {
+ content: '';
display: inline-block;
- vertical-align: middle;
+ width: 24px;
+ height: 24px;
margin-right: 10px;
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23618794' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='3' width='18' height='18' rx='2' ry='2'/%3E%3Ccircle cx='8.5' cy='8.5' r='1.5'/%3E%3Cpolyline points='21 15 16 10 5 21'/%3E%3C/svg%3E");
+ background-repeat: no-repeat;
}
-@keyframes spin {
- 0% { transform: rotate(0deg); }
- 100% { transform: rotate(360deg); }
-}
-
-#environment-check {
- margin-top: 20px;
- padding: 10px;
- background-color: #fff3cd;
- border: 1px solid #ffeeba;
- border-radius: 4px;
-}
-
-.env-success {
- color: green;
-}
-
-.env-error {
- color: red;
+#result-image {
+ max-width: 100%;
+ border-radius: 8px;
+ box-shadow: 0 2px 8px var(--shadow);
}
+/* Debug Section */
.debug-section {
- margin-top: 30px;
- border-top: 1px solid #ccc;
- padding-top: 20px;
+ margin-top: 3rem;
+ padding-top: 1.5rem;
+ border-top: 1px dashed var(--light-color);
+ display: flex;
+ gap: 1rem;
+}
+
+/* Utility Classes */
+.hidden {
+ display: none !important;
+}
+
+.mt-1 {
+ margin-top: 0.5rem;
+}
+
+.mt-2 {
+ margin-top: 1rem;
+}
+
+.mt-4 {
+ margin-top: 2rem;
+}
+
+.mb-1 {
+ margin-bottom: 0.5rem;
+}
+
+.mb-2 {
+ margin-bottom: 1rem;
+}
+
+.mb-4 {
+ margin-bottom: 2rem;
+}
+
+/* Responsive adjustments */
+@media (max-width: 768px) {
+ #app-container {
+ padding: 1rem;
+ }
+
+ .pipeline-container {
+ grid-template-columns: 1fr;
+ }
+
+ button {
+ width: 100%;
+ }
}
\ No newline at end of file