Automatically generates and displays AI-powered summaries for BBC articles
Size
8.1 KB
Version
1.1.3
Created
Nov 3, 2025
Updated
about 1 month ago
1// ==UserScript==
2// @name BBC Article Summarizer
3// @description Automatically generates and displays AI-powered summaries for BBC articles
4// @version 1.1.3
5// @match https://*.bbc.com/*
6// @icon https://static.files.bbci.co.uk/bbcdotcom/web/20251021-134202-667692b2c6-web-2.32.2-1/favicon.ico
7// ==/UserScript==
8(function() {
9 'use strict';
10
11 // Debounce function to prevent multiple rapid calls
12 function debounce(func, wait) {
13 let timeout;
14 return function executedFunction(...args) {
15 const later = () => {
16 clearTimeout(timeout);
17 func(...args);
18 };
19 clearTimeout(timeout);
20 timeout = setTimeout(later, wait);
21 };
22 }
23
24 async function generateAndDisplaySummary() {
25 console.log('Starting article summary generation...');
26
27 // Find the article element
28 const articleElement = document.querySelector('article');
29 if (!articleElement) {
30 console.error('Article element not found');
31 return;
32 }
33
34 // Check if summary already exists
35 if (document.getElementById('ai-article-summary')) {
36 console.log('Summary already exists');
37 return;
38 }
39
40 // Get article text
41 const articleText = articleElement.innerText;
42 if (!articleText || articleText.length < 100) {
43 console.error('Article text too short or not found');
44 return;
45 }
46
47 // Find the headline block to insert summary after it
48 const headlineBlock = document.querySelector('[data-component="headline-block"]');
49 const bylineBlock = document.querySelector('[data-component="byline-block"]');
50
51 if (!headlineBlock || !bylineBlock) {
52 console.error('Could not find insertion point for summary');
53 return;
54 }
55
56 // Create summary container
57 const summaryContainer = document.createElement('div');
58 summaryContainer.id = 'ai-article-summary';
59 summaryContainer.className = 'sc-3b6b161a-0 dPVOKT'; // Match BBC's article block classes
60 summaryContainer.style.cssText = `
61 background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 50%, #d946ef 100%);
62 color: white;
63 padding: 32px;
64 margin: 24px auto;
65 border-radius: 16px;
66 box-shadow: 0 10px 40px rgba(99, 102, 241, 0.3), 0 4px 12px rgba(0, 0, 0, 0.1);
67 font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
68 position: relative;
69 overflow: hidden;
70 max-width: 100%;
71 box-sizing: border-box;
72 `;
73
74 // Add loading state
75 summaryContainer.innerHTML = `
76 <div style="display: flex; align-items: center; gap: 16px;">
77 <div style="width: 32px; height: 32px; border: 4px solid rgba(255,255,255,0.2); border-top-color: white; border-radius: 50%; animation: spin 1s linear infinite;"></div>
78 <div>
79 <h3 style="margin: 0; font-size: 22px; font-weight: 700; letter-spacing: -0.5px;">AI Summary</h3>
80 <p style="margin: 8px 0 0 0; font-size: 15px; opacity: 0.85;">Analyzing article and generating insights...</p>
81 </div>
82 </div>
83 `;
84
85 // Add spinner animation
86 const style = document.createElement('style');
87 style.textContent = `
88 @keyframes spin {
89 to { transform: rotate(360deg); }
90 }
91 #ai-article-summary::before {
92 content: '';
93 position: absolute;
94 top: 0;
95 left: 0;
96 right: 0;
97 bottom: 0;
98 background: linear-gradient(135deg, rgba(255,255,255,0.1) 0%, rgba(255,255,255,0) 100%);
99 pointer-events: none;
100 }
101 #ai-article-summary ul li::marker {
102 color: rgba(255, 255, 255, 0.7);
103 }
104 `;
105 document.head.appendChild(style);
106
107 // Insert after byline block
108 bylineBlock.parentNode.insertBefore(summaryContainer, bylineBlock.nextSibling);
109
110 try {
111 console.log('Calling AI to generate summary...');
112
113 // Generate summary using AI
114 const summary = await RM.aiCall(
115 `Please provide a very brief 2-3 sentence summary of this BBC article. Be concise and focus only on the most important finding:\n\n${articleText}`,
116 {
117 type: 'json_schema',
118 json_schema: {
119 name: 'article_summary',
120 schema: {
121 type: 'object',
122 properties: {
123 summary: {
124 type: 'string',
125 description: 'A brief 2-3 sentence summary of the article'
126 },
127 keyPoints: {
128 type: 'array',
129 items: { type: 'string' },
130 description: '3 key points from the article',
131 minItems: 3,
132 maxItems: 3
133 }
134 },
135 required: ['summary', 'keyPoints']
136 }
137 }
138 }
139 );
140
141 console.log('Summary generated successfully:', summary);
142
143 // Display the summary
144 const keyPointsHTML = summary.keyPoints.map(point =>
145 `<li style="margin: 10px 0; padding-left: 8px; line-height: 1.5;">${point}</li>`
146 ).join('');
147
148 summaryContainer.innerHTML = `
149 <div style="position: relative; z-index: 1;">
150 <h3 style="margin: 0 0 16px 0; font-size: 24px; font-weight: 700; display: flex; align-items: center; gap: 10px; letter-spacing: -0.5px;">
151 <span style="font-size: 28px; filter: drop-shadow(0 2px 4px rgba(0,0,0,0.2));">✨</span>
152 <span>AI Summary</span>
153 </h3>
154 <p style="margin: 0 0 20px 0; font-size: 17px; line-height: 1.7; opacity: 0.95; font-weight: 400;">
155 ${summary.summary}
156 </p>
157 <div style="border-top: 2px solid rgba(255,255,255,0.25); padding-top: 20px; margin-top: 4px;">
158 <h4 style="margin: 0 0 12px 0; font-size: 18px; font-weight: 600; opacity: 0.95; letter-spacing: -0.3px;">Key Points:</h4>
159 <ul style="margin: 0; padding-left: 24px; font-size: 15px; line-height: 1.6; opacity: 0.92;">
160 ${keyPointsHTML}
161 </ul>
162 </div>
163 </div>
164 `;
165
166 } catch (error) {
167 console.error('Error generating summary:', error);
168 summaryContainer.innerHTML = `
169 <div style="position: relative; z-index: 1;">
170 <h3 style="margin: 0 0 12px 0; font-size: 22px; font-weight: 700; display: flex; align-items: center; gap: 10px;">
171 <span style="font-size: 24px;">⚠️</span> AI Summary
172 </h3>
173 <p style="margin: 0; font-size: 15px; opacity: 0.9; line-height: 1.6;">
174 Unable to generate summary at this time. Please try refreshing the page.
175 </p>
176 </div>
177 `;
178 }
179 }
180
181 // Initialize when page is ready
182 function init() {
183 console.log('BBC Article Summarizer initialized');
184
185 // Wait for article to be fully loaded
186 if (document.readyState === 'loading') {
187 document.addEventListener('DOMContentLoaded', generateAndDisplaySummary);
188 } else {
189 // DOM already loaded
190 setTimeout(generateAndDisplaySummary, 1000);
191 }
192 }
193
194 init();
195})();