parser-2 / index.html
MarkTheArtist's picture
undefined - Initial Deployment
cc79a8c verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Text Parser & Clipboard Utility</title>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
}
.container {
background: white;
border-radius: 16px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.15);
padding: 40px;
width: 100%;
max-width: 800px;
backdrop-filter: blur(10px);
}
h1 {
text-align: center;
color: #333;
margin-bottom: 30px;
font-size: 2.2rem;
font-weight: 600;
}
.input-section {
margin-bottom: 30px;
}
.input-label {
display: block;
margin-bottom: 10px;
color: #555;
font-weight: 500;
font-size: 1.1rem;
}
#textInput {
width: 100%;
height: 200px;
padding: 15px;
border: 2px solid #e1e5e9;
border-radius: 10px;
font-family: 'Courier New', monospace;
font-size: 14px;
line-height: 1.5;
resize: vertical;
transition: border-color 0.3s ease;
}
#textInput:focus {
outline: none;
border-color: #667eea;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
}
.controls {
display: flex;
gap: 15px;
align-items: center;
margin-bottom: 20px;
}
#nextButton {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
padding: 12px 30px;
border-radius: 8px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3);
}
#nextButton:hover:not(:disabled) {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4);
}
#nextButton:active:not(:disabled) {
transform: translateY(0);
}
#nextButton:disabled {
background: #ccc;
cursor: not-allowed;
box-shadow: none;
}
#resetButton {
background: #f8f9fa;
color: #666;
border: 2px solid #e1e5e9;
padding: 12px 20px;
border-radius: 8px;
font-size: 16px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
}
#resetButton:hover {
background: #e9ecef;
border-color: #adb5bd;
}
.status {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 20px;
}
.status-text {
font-size: 16px;
font-weight: 500;
padding: 8px 16px;
border-radius: 6px;
transition: all 0.3s ease;
}
.status-ready {
background: #e3f2fd;
color: #1976d2;
border: 1px solid #bbdefb;
}
.status-copying {
background: #e8f5e8;
color: #2e7d32;
border: 1px solid #c8e6c9;
}
.status-complete {
background: #fff3e0;
color: #f57c00;
border: 1px solid #ffcc02;
}
.status-error {
background: #ffebee;
color: #d32f2f;
border: 1px solid #ffcdd2;
}
.preview-section {
margin-top: 20px;
}
.preview-label {
display: block;
margin-bottom: 10px;
color: #555;
font-weight: 500;
}
#textPreview {
background: #f8f9fa;
border: 1px solid #e1e5e9;
border-radius: 8px;
padding: 15px;
max-height: 100px;
overflow-y: auto;
font-family: 'Courier New', monospace;
font-size: 14px;
line-height: 1.5;
white-space: pre-wrap;
}
.line-number {
color: #666;
margin-right: 10px;
user-select: none;
}
.current-line {
background: #fff3cd;
border-left: 4px solid #ffc107;
margin: -2px -15px;
padding: 2px 15px;
}
.copied-line {
background: #d4edda;
border-left: 4px solid #28a745;
margin: -2px -15px;
padding: 2px 15px;
opacity: 0.7;
}
.info-section {
margin-top: 20px;
padding: 15px;
background: #f8f9fa;
border-radius: 8px;
border-left: 4px solid #17a2b8;
}
.info-section h3 {
color: #17a2b8;
margin-bottom: 10px;
}
.info-section p {
color: #666;
line-height: 1.5;
}
@media (max-width: 768px) {
.container {
padding: 20px;
margin: 10px;
}
h1 {
font-size: 1.8rem;
}
.controls {
flex-direction: column;
align-items: stretch;
}
#nextButton, #resetButton {
width: 100%;
}
}
</style>
</head>
<body>
<div class="container">
<h1>📋 Text Parser & Clipboard Utility</h1>
<div class="input-section">
<label for="textInput" class="input-label">Paste your text here:</label>
<textarea id="textInput" placeholder="Paste your multi-line text here. Each non-empty line will be copied individually when you click 'Next'."></textarea>
</div>
<div class="controls">
<button id="nextButton" disabled>Next</button>
<button id="resetButton">Reset</button>
<div class="status">
<span id="statusText" class="status-text status-ready">Ready - Paste text to begin</span>
</div>
</div>
<div class="preview-section">
<label class="preview-label">Text Preview:</label>
<div id="textPreview">No text loaded</div>
</div>
<div class="info-section">
<h3>📝 Instructions</h3>
<p>
1. Paste your text in the textarea above<br>
2. Click "Next" to copy each line to your clipboard sequentially<br>
3. Empty lines are automatically skipped<br>
4. Use "Reset" to start over with the same text<br>
<strong>Note:</strong> This tool requires HTTPS or localhost for clipboard access.
</p>
</div>
</div>
<script>
class TextParserClipboard {
constructor() {
this.lines = [];
this.currentIndex = 0;
this.isProcessing = false;
// Get DOM elements
this.textInput = document.getElementById('textInput');
this.nextButton = document.getElementById('nextButton');
this.resetButton = document.getElementById('resetButton');
this.statusText = document.getElementById('statusText');
this.textPreview = document.getElementById('textPreview');
this.initializeEventListeners();
this.checkClipboardSupport();
}
initializeEventListeners() {
this.textInput.addEventListener('input', () => this.processText());
this.textInput.addEventListener('paste', () => {
// Small delay to allow paste content to be processed
setTimeout(() => this.processText(), 10);
});
this.nextButton.addEventListener('click', () => this.copyNextLine());
this.resetButton.addEventListener('click', () => this.reset());
// Handle keyboard shortcuts
document.addEventListener('keydown', (e) => {
if (e.ctrlKey || e.metaKey) {
if (e.key === 'Enter') {
e.preventDefault();
if (!this.nextButton.disabled) {
this.copyNextLine();
}
} else if (e.key === 'r') {
e.preventDefault();
this.reset();
}
}
});
}
checkClipboardSupport() {
if (!navigator.clipboard || !navigator.clipboard.writeText) {
this.updateStatus('Clipboard API not supported. Use HTTPS or localhost.', 'error');
this.nextButton.disabled = true;
}
}
processText() {
const text = this.textInput.value.trim();
if (!text) {
this.lines = [];
this.currentIndex = 0;
this.nextButton.disabled = true;
this.updateStatus('Ready - Paste text to begin', 'ready');
this.updatePreview();
return;
}
// Split text into lines and filter out empty lines
this.lines = text
.split('\n')
.map(line => line.trim())
.filter(line => line.length > 0);
this.currentIndex = 0;
this.nextButton.disabled = this.lines.length === 0;
if (this.lines.length === 0) {
this.updateStatus('No valid lines found', 'error');
} else {
this.updateStatus(`Ready - ${this.lines.length} lines loaded`, 'ready');
}
this.updatePreview();
}
async copyNextLine() {
if (this.currentIndex >= this.lines.length || this.isProcessing) {
return;
}
this.isProcessing = true;
const lineText = this.lines[this.currentIndex];
try {
await navigator.clipboard.writeText(lineText);
this.currentIndex++;
this.updateStatus(`Copied line ${this.currentIndex}/${this.lines.length}`, 'copying');
if (this.currentIndex >= this.lines.length) {
this.nextButton.disabled = true;
this.updateStatus('All lines copied! Click Reset to start over.', 'complete');
}
this.updatePreview();
} catch (error) {
console.error('Failed to copy text:', error);
this.updateStatus('Failed to copy to clipboard', 'error');
} finally {
this.isProcessing = false;
}
}
reset() {
// Clear the textarea
this.textInput.value = '';
// Reset all variables
this.lines = [];
this.currentIndex = 0;
this.isProcessing = false;
// Reset UI state
this.nextButton.disabled = true;
this.updateStatus('Ready - Paste text to begin', 'ready');
this.updatePreview();
}
updateStatus(message, type) {
this.statusText.textContent = message;
this.statusText.className = `status-text status-${type}`;
}
updatePreview() {
if (this.lines.length === 0) {
this.textPreview.textContent = 'No text loaded';
return;
}
const previewLines = this.lines.map((line, index) => {
const lineNumber = (index + 1).toString().padStart(2, '0');
let cssClass = '';
if (index < this.currentIndex) {
cssClass = 'copied-line';
} else if (index === this.currentIndex) {
cssClass = 'current-line';
}
return `<div class="${cssClass}"><span class="line-number">${lineNumber}:</span>${this.escapeHtml(line)}</div>`;
});
this.textPreview.innerHTML = previewLines.join('');
// Scroll current line into view
const currentElement = this.textPreview.querySelector('.current-line');
if (currentElement) {
currentElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
}
escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
}
// Initialize the application when DOM is loaded
document.addEventListener('DOMContentLoaded', () => {
new TextParserClipboard();
});
</script>
</body>
</html>