Skip to content

Commit 4fe5222

Browse files
committed
Add OpenAI API integration for text summarization and API key management
1 parent 313cc06 commit 4fe5222

File tree

1 file changed

+163
-0
lines changed

1 file changed

+163
-0
lines changed

index.html

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@
88
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Ccircle cx='50' cy='50' r='45' fill='%23ff0000'/%3E%3C/svg%3E" type="image/svg+xml">
99
<!-- Add lamejs library -->
1010
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lame.min.js"></script>
11+
<!-- Import OpenAI library -->
12+
<script type="module">
13+
import OpenAI from 'https://esm.sh/[email protected]?target=es2020';
14+
window.OpenAI = OpenAI;
15+
</script>
1116
<style>
1217
/* Base styles for dark theme */
1318
body {
@@ -112,6 +117,48 @@
112117
text-align: center;
113118
transition: opacity 0.5s;
114119
}
120+
121+
/* API Key section */
122+
.api-key-section {
123+
margin: 10px 0;
124+
padding: 10px;
125+
border: 1px dashed #555;
126+
border-radius: 4px;
127+
background: #222;
128+
}
129+
130+
.api-key-toggle {
131+
cursor: pointer;
132+
color: #007bff;
133+
user-select: none;
134+
}
135+
136+
.api-key-input {
137+
display: none; /* Hidden by default */
138+
margin-top: 8px;
139+
}
140+
141+
.api-key-input input {
142+
width: calc(100% - 125px);
143+
margin-right: 10px;
144+
}
145+
146+
/* Summary section */
147+
.summary-container {
148+
margin-top: 15px;
149+
padding: 10px;
150+
border: 1px solid #444;
151+
border-radius: 4px;
152+
background: #2c2c2c;
153+
display: none; /* Hidden until there's a summary */
154+
}
155+
156+
.summary-content {
157+
white-space: pre-wrap;
158+
font-family: Consolas, monospace;
159+
font-size: 0.9rem;
160+
color: #a0e0a0;
161+
}
115162
</style>
116163
</head>
117164

@@ -133,20 +180,136 @@
133180
<option value="zh-CN">Chinese (Simplified)</option>
134181
</select>
135182
</div>
183+
<button id="summarize">Summarize Text</button>
184+
</div>
185+
186+
<!-- API Key Section -->
187+
<div class="api-key-section">
188+
<div class="api-key-toggle">⚙️ OpenAI API Settings (click to expand)</div>
189+
<div class="api-key-input">
190+
<input type="password" id="api-key" placeholder="Enter your OpenAI API key (sk-...)">
191+
<button id="save-api-key">Save</button>
192+
</div>
136193
</div>
137194

138195
<h3>Transcription:</h3>
139196
<div id="transcription-container">
140197
<!-- The transcription element is contenteditable (editable by the user) -->
141198
<div id="transcription" contenteditable="true"></div>
142199
</div>
200+
201+
<!-- Summary Section -->
202+
<h3 id="summary-heading" style="display:none;">Summary:</h3>
203+
<div id="summary-container" class="summary-container">
204+
<div id="summary-content" class="summary-content"></div>
205+
</div>
143206
</div>
144207

145208
<script>
146209
// Global variable to track recording state
147210
let isRecording = false;
148211
let exitWarningEnabled = false;
149212
let mp3Encoder = null;
213+
let apiKey = localStorage.getItem('openai_api_key') || '';
214+
215+
// Initialize API key section
216+
document.addEventListener('DOMContentLoaded', () => {
217+
const apiKeyToggle = document.querySelector('.api-key-toggle');
218+
const apiKeyInput = document.querySelector('.api-key-input');
219+
const apiKeyField = document.getElementById('api-key');
220+
const saveApiKeyBtn = document.getElementById('save-api-key');
221+
const summarizeBtn = document.getElementById('summarize');
222+
223+
// Set the saved API key if available
224+
if (apiKey) {
225+
apiKeyField.value = apiKey;
226+
}
227+
228+
// Toggle API key input visibility
229+
apiKeyToggle.addEventListener('click', () => {
230+
apiKeyInput.style.display = apiKeyInput.style.display === 'none' ? 'block' : 'none';
231+
});
232+
233+
// Save API key to localStorage
234+
saveApiKeyBtn.addEventListener('click', () => {
235+
apiKey = apiKeyField.value.trim();
236+
localStorage.setItem('openai_api_key', apiKey);
237+
apiKeyInput.style.display = 'none';
238+
showNotification('API key saved');
239+
});
240+
241+
// Summarize button event handler
242+
summarizeBtn.addEventListener('click', async () => {
243+
const transcriptionText = document.getElementById('transcription').textContent;
244+
if (!transcriptionText.trim()) {
245+
showNotification('No transcription text to summarize');
246+
return;
247+
}
248+
249+
if (!apiKey) {
250+
showNotification('Please provide an OpenAI API key first');
251+
apiKeyInput.style.display = 'block';
252+
return;
253+
}
254+
255+
await summarizeText(transcriptionText);
256+
});
257+
});
258+
259+
// Function to show a notification
260+
function showNotification(message, duration = 3000) {
261+
const notification = document.createElement('div');
262+
notification.className = 'notification';
263+
notification.textContent = message;
264+
document.body.appendChild(notification);
265+
266+
setTimeout(() => {
267+
notification.style.opacity = '0';
268+
setTimeout(() => notification.remove(), 500);
269+
}, duration);
270+
}
271+
272+
// Function to summarize text using OpenAI API
273+
async function summarizeText(text) {
274+
const summaryHeading = document.getElementById('summary-heading');
275+
const summaryContainer = document.getElementById('summary-container');
276+
const summaryContent = document.getElementById('summary-content');
277+
278+
summaryHeading.style.display = 'block';
279+
summaryContainer.style.display = 'block';
280+
summaryContent.textContent = 'Generating summary...';
281+
282+
try {
283+
// Create OpenAI client
284+
const client = new window.OpenAI({
285+
apiKey: apiKey,
286+
dangerouslyAllowBrowser: true,
287+
});
288+
289+
// Call the API
290+
const response = await client.chat.completions.create({
291+
model: 'gpt-3.5-turbo', // Using a less expensive model for summaries
292+
messages: [
293+
{
294+
role: 'system',
295+
content: 'You are a helpful assistant that summarizes text concisely.'
296+
},
297+
{
298+
role: 'user',
299+
content: `Please summarize the following transcription in a clear, concise manner:\n\n${text}`
300+
}
301+
],
302+
max_tokens: 500
303+
});
304+
305+
// Display the summary
306+
const summary = response.choices[0].message.content;
307+
summaryContent.textContent = summary;
308+
} catch (error) {
309+
console.error('Error generating summary:', error);
310+
summaryContent.textContent = `Error generating summary: ${error.message}`;
311+
}
312+
}
150313

151314
// Check if the browser supports the required APIs
152315
if (!window.MediaRecorder || !(window.webkitSpeechRecognition || window.SpeechRecognition)) {

0 commit comments

Comments
 (0)