Summarizer

 <!DOCTYPE html>

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Text Summarizer - Zupr.ai</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css">
    <style>
        body {
            font-family: 'Roboto', sans-serif;
            max-width: 900px;
            margin: 20px auto;
            padding: 20px;
            background: linear-gradient(135deg, #1e3c72, #2a5298);
            color: #fff;
        }
        h1 {
            text-align: center;
            color: #00ddeb;
            text-shadow: 0 0 10px rgba(0,221,235,0.5);
        }
        .container {
            background: rgba(255,255,255,0.95);
            padding: 20px;
            border-radius: 12px;
            box-shadow: 0 4px 20px rgba(0,0,0,0.2);
        }
        textarea {
            width: 100%;
            height: 200px;
            padding: 12px;
            border: 1px solid #00ddeb;
            border-radius: 6px;
            resize: vertical;
            font-size: 16px;
            color: #333;
        }
        button {
            background: #00ddeb;
            color: #1e3c72;
            padding: 10px 20px;
            border: none;
            border-radius: 6px;
            cursor: pointer;
            font-size: 16px;
            margin: 10px 5px;
            transition: transform 0.2s, background 0.2s;
            position: relative;
        }
        button:hover {
            transform: scale(1.05);
            background: #00b8c4;
        }
        button:disabled {
            background: #ccc;
            cursor: not-allowed;
        }
        .tooltip {
            position: relative;
        }
        .tooltip:hover::after {
            content: attr(data-tooltip);
            position: absolute;
            bottom: 100%;
            left: 50%;
            transform: translateX(-50%);
            background: #333;
            color: #fff;
            padding: 5px 10px;
            border-radius: 4px;
            font-size: 12px;
            white-space: nowrap;
            z-index: 10;
        }
        #summary-output, #history, #key-phrases {
            margin-top: 20px;
            padding: 15px;
            background: #f5faff;
            border-radius: 6px;
            color: #333;
            animation: fadeIn 0.5s ease-in;
        }
        #original-text {
            margin-top: 20px;
            padding: 15px;
            background: #e6f0fa;
            border-radius: 6px;
            color: #333;
        }
        .highlight {
            background: #ffeb3b;
            padding: 2px 4px;
        }
        #stats {
            font-size: 14px;
            color: #2a5298;
            margin-top: 10px;
        }
        .slider-container, .select-container {
            margin: 10px 0;
            display: flex;
            align-items: center;
            gap: 10px;
        }
        label {
            font-size: 14px;
            color: #333;
            margin-right: 10px;
        }
        select, input[type="range"] {
            padding: 5px;
            border: 1px solid #00ddeb;
            border-radius: 4px;
        }
        .loading {
            display: none;
            text-align: center;
            margin: 10px 0;
            color: #2a5298;
        }
        #history-list li {
            cursor: pointer;
            padding: 5px 0;
        }
        #history-list li:hover {
            background: #e6f0fa;
        }
        @keyframes fadeIn {
            from { opacity: 0; }
            to { opacity: 1; }
        }
        @media (max-width: 600px) {
            body { padding: 10px; }
            .container { padding: 15px; }
            button { padding: 8px 12px; font-size: 14px; }
            .slider-container, .select-container { flex-direction: column; align-items: flex-start; }
        }
    </style>
</head>
<body>
    <h1>Zupr.ai Text Summarizer</h1>
    <div class="container">
        <textarea id="input-text" placeholder="Paste or type your text here..." aria-label="Input text for summarization"></textarea>
        <div class="slider-container">
            <label for="summary-length">Summary Length: <span id="length-value">5</span> sentences</label>
            <input type="range" id="summary-length" min="1" max="10" value="5" aria-label="Adjust summary length">
        </div>
        <div class="slider-container">
            <label for="word-limit">Word Limit: <span id="word-limit-value">100</span> words</label>
            <input type="range" id="word-limit" min="50" max="300" value="100" step="10" aria-label="Adjust word limit">
        </div>
        <div class="select-container">
            <label for="tone">Tone:</label>
            <select id="tone" aria-label="Select summary tone">
                <option value="neutral">Neutral</option>
                <option value="formal">Formal</option>
                <option value="informal">Informal</option>
            </select>
            <label for="style">Style:</label>
            <select id="style" aria-label="Select summary style">
                <option value="bullets">Bullet Points</option>
                <option value="paragraph">Paragraph</option>
            </select>
        </div>
        <div class="loading" id="loading"><i class="fas fa-spinner fa-spin"></i> Summarizing...</div>
        <button onclick="summarizeText()" class="tooltip" data-tooltip="Generate summary" id="summarize-btn"><i class="fas fa-compress-alt"></i> Summarize</button>
        <button onclick="copySummary()" class="tooltip" data-tooltip="Copy summary to clipboard"><i class="fas fa-copy"></i> Copy</button>
        <button onclick="downloadSummary()" class="tooltip" data-tooltip="Download summary as text file"><i class="fas fa-download"></i> Download</button>
        <button onclick="clearText()" class="tooltip" data-tooltip="Clear all fields"><i class="fas fa-trash"></i> Clear</button>
        <div id="summary-output" role="region" aria-live="polite">Your summary will appear here...</div>
        <div id="key-phrases" role="region" aria-live="polite">Key phrases will appear here...</div>
        <div id="original-text" role="region" aria-live="polite">Highlighted original text will appear here...</div>
        <div id="stats"></div>
        <div id="history">
            <h3>Recent Summaries</h3>
            <ul id="history-list"></ul>
        </div>
    </div>

    <script>
        async function summarizeText() {
            const inputText = document.getElementById('input-text').value.trim();
            const summaryLength = parseInt(document.getElementById('summary-length').value);
            const wordLimit = parseInt(document.getElementById('word-limit').value);
            const tone = document.getElementById('tone').value;
            const style = document.getElementById('style').value;
            const summarizeBtn = document.getElementById('summarize-btn');
            const loading = document.getElementById('loading');

            if (!inputText) {
                document.getElementById('summary-output').innerHTML = 'Please enter some text to summarize.';
                document.getElementById('original-text').innerHTML = '';
                document.getElementById('key-phrases').innerHTML = '';
                return;
            }

            summarizeBtn.disabled = true;
            loading.style.display = 'block';

            // Split text into sentences
            const sentences = inputText.match(/[^.!?]+[.!?]+/g)?.map(s => s.trim()) || [inputText];
            if (sentences.length < 2) {
                document.getElementById('summary-output').innerHTML = 'Text is too short to summarize.';
                document.getElementById('original-text').innerHTML = '';
                document.getElementById('key-phrases').innerHTML = '';
                summarizeBtn.disabled = false;
                loading.style.display = 'none';
                return;
            }

            // Calculate stats
            const words = inputText.split(/\s+/).length;
            const readingTime = Math.ceil(words / 200);
            const sentenceCount = sentences.length;

            // Word and phrase frequency (TF-IDF inspired)
            const wordFreq = {};
            const phraseFreq = {};
            inputText.toLowerCase().split(/\s+/).forEach(word => {
                if (word.length > 3 && !['the', 'and', 'is', 'are', 'in', 'to', 'of', 'for'].includes(word)) {
                    wordFreq[word] = (wordFreq[word] || 0) + 1;
                }
            });

            // Extract key phrases (2-3 word combinations)
            const wordsArray = inputText.toLowerCase().split(/\s+/);
            for (let i = 0; i < wordsArray.length - 1; i++) {
                const phrase = wordsArray.slice(i, i + 2).join(' ');
                if (!phrase.includes(' the ') && !phrase.includes(' and ')) {
                    phraseFreq[phrase] = (phraseFreq[phrase] || 0) + 1;
                }
                if (i < wordsArray.length - 2) {
                    const triPhrase = wordsArray.slice(i, i + 3).join(' ');
                    if (!triPhrase.includes(' the ') && !triPhrase.includes(' and ')) {
                        phraseFreq[triPhrase] = (phraseFreq[triPhrase] || 0) + 1;
                    }
                }
            }

            // Score sentences
            const sentenceScores = sentences.map((sentence, index) => {
                const words = sentence.toLowerCase().split(/\s+/);
                let score = words.reduce((sum, word) => sum + (wordFreq[word] || 0) / (1 + Math.log(words.length)), 0);
                score += (sentences.length - index) * 0.1; // Position boost
                score *= (words.length > 5 && words.length < 30) ? 1 : 0.5; // Length penalty
                return { sentence, score, index };
            });

            // Get top sentences
            let topSentences = sentenceScores
                .sort((a, b) => b.score - a.score)
                .slice(0, Math.min(summaryLength, sentences.length));

            // Adjust for word limit
            let currentWords = topSentences.reduce((sum, item) => sum + item.sentence.split(/\s+/).length, 0);
            while (currentWords > wordLimit && topSentences.length > 1) {
                topSentences.pop();
                currentWords = topSentences.reduce((sum, item) => sum + item.sentence.split(/\s+/).length, 0);
            }

            // Apply tone
            topSentences = topSentences.map(item => ({
                ...item,
                sentence: adjustTone(item.sentence, tone)
            }));

            // Format summary
            let output = '';
            if (style === 'bullets') {
                output = '<ul>' + topSentences.map(item => `<li>${item.sentence}</li>`).join('') + '</ul>';
            } else {
                output = topSentences.map(item => item.sentence).join(' ');
            }

            // Highlight original text
            let highlightedText = inputText;
            const topSentenceIndices = topSentences.map(item => item.index);
            topSentenceIndices.forEach(index => {
                const escapedSentence = sentences[index].replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
                const regex = new RegExp(`(${escapedSentence})`, 'g');
                highlightedText = highlightedText.replace(regex, '<span class="highlight">$1</span>');
            });

            // Extract key phrases
            const keyPhrases = Object.entries(phraseFreq)
                .sort((a, b) => b[1] - a[1])
                .slice(0, 5)
                .map(([phrase]) => phrase)
                .join(', ');

            // Calculate compression ratio
            const summaryWords = topSentences.reduce((sum, item) => sum + item.sentence.split(/\s+/).length, 0);
            const compressionRatio = ((words - summaryWords) / words * 100).toFixed(1);

            // Update UI
            document.getElementById('summary-output').innerHTML = output || 'No summary generated.';
            document.getElementById('key-phrases').innerHTML = `<strong>Key Phrases:</strong> ${keyPhrases || 'None identified.'}`;
            document.getElementById('original-text').innerHTML = highlightedText.replace(/\n/g, '<br>');
            document.getElementById('stats').innerHTML = `Word Count: ${words} | Sentences: ${sentenceCount} | Est. Reading Time: ${readingTime} min | Compression: ${compressionRatio}%`;

            // Save to history
            saveToHistory(output, keyPhrases);

            summarizeBtn.disabled = false;
            loading.style.display = 'none';
        }

        function adjustTone(sentence, tone) {
            if (tone === 'formal') {
                return sentence.replace(/gonna|kinda|sorta/gi, 'going to').replace(/ ain't/gi, ' is not');
            } else if (tone === 'informal') {
                return sentence.replace(/going to/gi, 'gonna').replace(/is not/gi, "ain't");
            }
            return sentence;
        }

        function copySummary() {
            const summary = document.getElementById('summary-output').innerText;
            navigator.clipboard.writeText(summary).then(() => {
                alert('Summary copied to clipboard!');
            }).catch(() => {
                alert('Failed to copy summary.');
            });
        }

        function downloadSummary() {
            const summary = document.getElementById('summary-output').innerText;
            const blob = new Blob([summary], { type: 'text/plain' });
            const url = URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.href = url;
            a.download = 'Zupr_Summary.txt';
            a.click();
            URL.revokeObjectURL(url);
        }

        function clearText() {
            document.getElementById('input-text').value = '';
            document.getElementById('summary-output').innerHTML = 'Your summary will appear here...';
            document.getElementById('key-phrases').innerHTML = 'Key phrases will appear here...';
            document.getElementById('original-text').innerHTML = 'Highlighted original text will appear here...';
            document.getElementById('stats').innerHTML = '';
        }

        function saveToHistory(summary, keyPhrases) {
            let history = JSON.parse(localStorage.getItem('summaryHistory') || '[]');
            history.unshift({ summary, keyPhrases, date: new Date().toLocaleString() });
            history = history.slice(0, 5); // Keep last 5
            localStorage.setItem('summaryHistory', JSON.stringify(history));
            updateHistory();
        }

        function updateHistory() {
            const historyList = document.getElementById('history-list');
            const history = JSON.parse(localStorage.getItem('summaryHistory') || '[]');
            historyList.innerHTML = history.map((item, index) => `
                <li onclick="restoreSummary(${index})">
                    ${item.date}: ${item.summary.substring(0, 100)}...
                    <br><small>Key Phrases: ${item.keyPhrases.substring(0, 50)}...</small>
                </li>
            `).join('');
        }

        function restoreSummary(index) {
            const history = JSON.parse(localStorage.getItem('summaryHistory') || '[]');
            const item = history[index];
            document.getElementById('summary-output').innerHTML = item.summary;
            document.getElementById('key-phrases').innerHTML = `<strong>Key Phrases:</strong> ${item.keyPhrases}`;
        }

        // Initialize history
        updateHistory();

        // Slider value displays
        document.getElementById('summary-length').addEventListener('input', function() {
            document.getElementById('length-value').innerText = this.value;
        });
        document.getElementById('word-limit').addEventListener('input', function() {
            document.getElementById('word-limit-value').innerText = this.value;
        });
    </script>
</body>
</html>

Comments

Popular posts from this blog

Text Rewriter

Calculator