Custom Extensions
Learn from example extensions built with Anouk.
Twitter/X Reply Assistant
An extension that helps compose thoughtful replies on Twitter/X.
Features
- Analyze tweet context and thread
- Generate reply suggestions
- Adjust tone (professional, casual, witty)
- Character count awareness
Implementation
import { AIService } from 'anouk';
class TwitterReplyAssistant {
constructor() {
this.aiService = new AIService({
provider: 'together',
model: 'meta-llama/Llama-3-70b-chat-hf'
});
this.observeTweets();
}
observeTweets() {
// Watch for reply box focus
document.addEventListener('focusin', (e) => {
if (this.isReplyBox(e.target)) {
this.showAssistant(e.target);
}
});
}
isReplyBox(element) {
return element.matches('[data-testid="tweetTextarea_0"]');
}
async generateReplies(tweetContent) {
const tones = ['professional', 'casual', 'witty'];
const replies = {};
for (const tone of tones) {
replies[tone] = await this.aiService.call(
`Generate a ${tone} reply to this tweet.
Keep it under 280 characters.
Be authentic and engaging.`,
tweetContent,
this.getTweetId(),
`reply_${tone}`
);
}
return replies;
}
showAssistant(replyBox) {
// Get parent tweet content
const tweet = this.getParentTweet(replyBox);
const tweetContent = tweet?.textContent;
if (!tweetContent) return;
// Create floating assistant
const assistant = document.createElement('div');
assistant.className = 'twitter-reply-assistant';
assistant.innerHTML = `
<div class="assistant-header">
<span>Reply Assistant</span>
<button class="generate-btn">Generate Replies</button>
</div>
<div class="suggestions"></div>
`;
// Position near reply box
replyBox.parentElement.appendChild(assistant);
// Generate on click
assistant.querySelector('.generate-btn').onclick = async () => {
const suggestions = assistant.querySelector('.suggestions');
suggestions.innerHTML = '<p>Generating...</p>';
const replies = await this.generateReplies(tweetContent);
suggestions.innerHTML = Object.entries(replies)
.map(([tone, text]) => `
<div class="suggestion" data-tone="${tone}">
<span class="tone-label">${tone}</span>
<p>${text}</p>
<button class="use-btn">Use</button>
</div>
`).join('');
// Insert on click
suggestions.querySelectorAll('.use-btn').forEach(btn => {
btn.onclick = () => {
const text = btn.parentElement.querySelector('p').textContent;
this.insertReply(replyBox, text);
};
});
};
}
insertReply(replyBox, text) {
replyBox.focus();
document.execCommand('insertText', false, text);
}
}
new TwitterReplyAssistant();
Code Review Assistant
Browser extension for GitHub that provides AI-powered code review suggestions.
Features
- Analyze pull request diffs
- Suggest improvements
- Identify potential bugs
- Check for security issues
Implementation
import { AIService } from 'anouk';
class CodeReviewAssistant {
constructor() {
this.aiService = new AIService({
provider: 'openai',
model: 'gpt-4',
systemPrompt: `You are an expert code reviewer.
Focus on: bugs, security issues, performance,
code quality, and best practices.
Be constructive and specific.`
});
this.init();
}
init() {
// Check if on GitHub PR page
if (this.isGitHubPR()) {
this.addReviewButton();
}
// Watch for navigation
this.observeNavigation();
}
isGitHubPR() {
return window.location.pathname.includes('/pull/');
}
addReviewButton() {
const actionsBar = document.querySelector('.pr-review-tools');
if (!actionsBar) return;
const button = document.createElement('button');
button.className = 'btn btn-primary ml-2';
button.textContent = 'AI Review';
button.onclick = () => this.performReview();
actionsBar.appendChild(button);
}
async performReview() {
const diffs = this.extractDiffs();
for (const diff of diffs) {
const review = await this.aiService.call(
`Review this code change. Identify:
1. Potential bugs or errors
2. Security vulnerabilities
3. Performance issues
4. Code quality improvements
Format as actionable comments.`,
diff.content,
diff.file,
'review'
);
this.displayReview(diff.file, review);
}
}
extractDiffs() {
const diffContainers = document.querySelectorAll('.file');
return Array.from(diffContainers).map(container => ({
file: container.querySelector('.file-header').textContent,
content: container.querySelector('.blob-code-content').textContent
}));
}
displayReview(file, review) {
const fileHeader = Array.from(document.querySelectorAll('.file-header'))
.find(h => h.textContent.includes(file));
if (!fileHeader) return;
const reviewPanel = document.createElement('div');
reviewPanel.className = 'ai-review-panel';
reviewPanel.innerHTML = `
<div class="review-header">
<strong>AI Review</strong>
</div>
<div class="review-content">
${this.formatReview(review)}
</div>
`;
fileHeader.parentElement.insertBefore(
reviewPanel,
fileHeader.nextSibling
);
}
formatReview(review) {
return review
.split('\n')
.map(line => `<p>${line}</p>`)
.join('');
}
}
new CodeReviewAssistant();
Meeting Notes Summarizer
Extension for Google Meet that generates meeting summaries.
Features
- Capture meeting transcript
- Generate summary and action items
- Create follow-up email draft
- Export to various formats
Implementation
import { AIService } from 'anouk';
class MeetingNotesSummarizer {
constructor() {
this.aiService = new AIService({
provider: 'openai',
model: 'gpt-4-turbo',
maxTokens: 4000
});
this.transcript = [];
this.init();
}
init() {
if (this.isGoogleMeet()) {
this.addUI();
this.observeTranscript();
}
}
isGoogleMeet() {
return window.location.hostname === 'meet.google.com';
}
addUI() {
const button = document.createElement('button');
button.id = 'summarize-meeting';
button.textContent = 'Summarize';
button.onclick = () => this.generateSummary();
// Add to Meet toolbar
const toolbar = document.querySelector('[data-meeting-toolbar]');
if (toolbar) {
toolbar.appendChild(button);
}
}
observeTranscript() {
// Watch for caption updates
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
if (mutation.addedNodes.length) {
this.captureCaption(mutation.addedNodes);
}
}
});
const captionsContainer = document.querySelector('[data-captions]');
if (captionsContainer) {
observer.observe(captionsContainer, { childList: true });
}
}
captureCaption(nodes) {
nodes.forEach(node => {
if (node.textContent) {
this.transcript.push({
time: new Date().toISOString(),
text: node.textContent
});
}
});
}
async generateSummary() {
const transcriptText = this.transcript
.map(t => t.text)
.join('\n');
const summary = await this.aiService.call(
`Summarize this meeting transcript:
1. Key Discussion Points (bullet points)
2. Decisions Made
3. Action Items (with assignees if mentioned)
4. Follow-up Required
Be concise but comprehensive.`,
transcriptText,
`meeting_${Date.now()}`,
'summary'
);
this.showSummary(summary);
}
showSummary(summary) {
const modal = document.createElement('div');
modal.className = 'summary-modal';
modal.innerHTML = `
<div class="modal-content">
<h2>Meeting Summary</h2>
<div class="summary-text">${summary}</div>
<div class="modal-actions">
<button id="copy-summary">Copy</button>
<button id="email-summary">Draft Email</button>
<button id="close-modal">Close</button>
</div>
</div>
`;
document.body.appendChild(modal);
modal.querySelector('#copy-summary').onclick = () => {
navigator.clipboard.writeText(summary);
};
modal.querySelector('#email-summary').onclick = () => {
this.draftFollowUpEmail(summary);
};
modal.querySelector('#close-modal').onclick = () => {
modal.remove();
};
}
async draftFollowUpEmail(summary) {
const email = await this.aiService.call(
`Draft a professional follow-up email based on this meeting summary.
Include action items and next steps.`,
summary,
`email_${Date.now()}`,
'email'
);
// Open in Gmail compose
const gmailUrl = `https://mail.google.com/mail/?view=cm&body=${encodeURIComponent(email)}`;
window.open(gmailUrl, '_blank');
}
}
new MeetingNotesSummarizer();
Extension Ideas
Here are more ideas for Anouk-powered extensions:
| Extension | Use Case |
|---|---|
| LinkedIn Optimizer | Improve profile and posts |
| Slack Summarizer | Summarize channel activity |
| YouTube Notes | Generate video summaries |
| News Analyzer | Fact-check and summarize articles |
| Form Filler | Auto-fill applications with AI |
| Language Tutor | Real-time translation and learning |
| Shopping Assistant | Product comparisons and reviews |
| Recipe Parser | Extract and format recipes |
Best Practices
Performance
- Use caching aggressively
- Debounce user interactions
- Lazy load AI features
UX
- Show loading states
- Provide clear error messages
- Allow easy dismissal of UI
Privacy
- Don't log sensitive data
- Let users control what's analyzed
- Use local models when possible