Ogni side project significativo nasce da un'esigenza personale. Annotiquo non fa eccezione. È iniziato come una soluzione a una mia frustrazione, ma si è rapidamente trasformato in un banco di prova per esplorare architetture moderne e le sfide uniche dello sviluppo di estensioni browser. Questo articolo ne documenta il percorso, le scelte tecniche e le lezioni apprese.
Overview del Progetto: Architettura Monorepo
Annotiquo si basa su un'architettura monorepo che separa nettamente le due componenti principali del sistema:
annotiquo/
├── extension/ # L'estensione Chrome (Manifest V3)
└── webapp/ # La companion app in Next.js
L'estensione è il cuore del prodotto: è autonoma e si occupa dell'estrazione dei contenuti. La web app la arricchisce con funzionalità basate su AI, storage persistente e gestione degli account. Questa separazione permette di far evolvere le due parti in modo indipendente, garantendo flessibilità e manutenibilità.
L'estensione risolve il problema primario: l'estrazione dei contenuti. Funziona offline e non richiede registrazione. La web app è un layer di valore aggiunto che offre storage, ricerca e funzionalità AI.
Questo approccio offre diversi vantaggi:
- Autonomia dell'estensione: L'utente può usarla senza mai interagire con la web app.
- Evoluzione indipendente: La web app può essere aggiornata quotidianamente senza richiedere una nuova revisione sul Chrome Web Store.
- Deployment separati: Testing e rilascio delle due componenti sono completamente disaccoppiati.
Lo Stack Tecnologico: Modernità e Pragmatismo
La scelta tecnologica ha privilegiato strumenti moderni ma ampiamente collaudati, per evitare di introdurre troppe incognite contemporaneamente.
Extension
- TypeScript e Chrome Manifest V3
- Un sistema di parser modulari basato su Mozilla Readability
- Webpack come build system
Webapp
- Next.js 14 con App Router
- PostgreSQL con Drizzle ORM per l'accesso ai dati
- NextAuth.js per il sistema di autenticazione
- Stripe per la gestione dei pagamenti
- Openrouter per le funzionalità di intelligenza artificiale
Prima Estensione Browser: Lezioni sul Campo
Manifest V3: Un Cambio di Paradigma
L'adozione obbligatoria di Manifest V3 da parte di Chrome, con la sostituzione dei background script persistenti in favore dei service worker, è stata la prima sfida. Inizialmente percepita come una limitazione, si è rivelata un'opportunità per progettare in modo più efficiente.
// Il service worker gestisce gli eventi in modo non persistente
chrome.action.onClicked.addListener(async (tab) => {
await chrome.scripting.executeScript({
target: { tabId: tab.id! },
func: extractContent
});
});
Questo approccio impone una gestione dello stato stateless. Niente più variabili globali che sopravvivono a lungo termine; ogni operazione deve essere atomica e indipendente, un vincolo che porta a un codice più pulito e a un minor consumo di risorse.
Comunicazione Cross-Context: La Sfida della Sandbox
Una delle complessità intrinseche delle estensioni è la comunicazione tra contesti isolati: il popup, i content script e il service worker vivono in sandbox separate.
// Esempio di flusso: Popup → Service Worker
const response = await chrome.runtime.sendMessage({ action: 'copyText' });
// Esempio di flusso: Service Worker → Content Script
await chrome.scripting.executeScript({
target: { tabId: tab.id },
func: () => window.postMessage({ type: 'EXTRACT_CONTENT' }, '*')
});
In questo scenario, TypeScript si è rivelato cruciale. Definire interfacce chiare per i messaggi ha permesso di evitare errori di runtime e di mantenere la coerenza tra i diversi componenti.
Il Cuore dell'Architettura: un Sistema di Parser Modulari
La decisione architetturale più impattante è stata la creazione di un sistema di parser modulari. Invece di un'unica logica di estrazione, ho definito un'interfaccia che permette di aggiungere "estrattori" specializzati.
interface ContentParser {
name: string;
// Determina se questo parser è adatto per la pagina corrente
canParse(url: string, document: Document): boolean;
// Esegue l'estrazione vera e propria
parse(document: Document): Promise<ParsedContent>;
// Definisce la priorità di esecuzione
priority: number;
}
Questo sistema si basa su due livelli:
- Parser Generico: Utilizza Mozilla Readability e gestisce l'80% dei siti web (articoli, blog, ecc.).
- Parser Specializzati: Progettati per siti specifici come YouTube, Twitter o Stack Overflow. Questi parser sanno esattamente dove trovare i contenuti (es. trascrizioni di video, testo di un tweet) che il motore generico non riuscirebbe a interpretare correttamente.
Questo design rende il sistema facilmente estensibile: per supportare un nuovo sito, è sufficiente creare una nuova classe ContentParser
senza modificare il codice esistente.
La Webapp: Evoluzione dell'Ecosistema
La webapp Next.js rappresenta l'estensione naturale del concetto di Annotiquo. Mentre l'estensione si concentra sull'estrazione immediata di contenuti, la webapp introduce un layer di AI e persistenza che trasforma l'uso occasionale in un sistema di knowledge management completo.
Architettura e Stack Tecnico: La webapp è costruita con Next.js 14 e sfrutta l'App Router per una gestione ottimale del routing e del rendering. Il database PostgreSQL gestito tramite Drizzle ORM offre type safety end-to-end, mentre NextAuth.js fornisce un sistema di autenticazione robusto. L'integrazione Stripe gestisce il modello freemium in modo trasparente.
AI Integration come Game Changer: La funzionalità più impattante è l'integrazione con OpenRouter per accedere a diversi modelli AI (OpenAI, Claude) tramite un'unica API.
interface SummarizationRequest {
content: string;
contentType: 'article' | 'youtube' | 'generic';
maxTokens?: number;
model?: 'openai/gpt-4' | 'anthropic/claude-3-sonnet' | 'meta-llama/llama-3.1-8b';
}
export class OpenRouterClient {
async summarize(request: SummarizationRequest): Promise<string> {
const modelConfig = this.getModelConfig(request.contentType);
const response = await fetch('https://openrouter.ai/api/v1/chat/completions', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.OPENROUTER_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: request.model || modelConfig.defaultModel,
messages: [
{
role: 'system',
content: modelConfig.systemPrompt
},
{
role: 'user',
content: `Summarize this ${request.contentType}:\n\n${request.content}`
}
],
max_tokens: request.maxTokens || modelConfig.maxTokens
})
});
const data = await response.json();
return data.choices[0].message.content;
}
}
Deployment e Distribuzione
- Estensione: Il deployment è automatizzato con GitLab Pipelines. A ogni push sul branch principale, viene creato un pacchetto
.zip
e caricato sul Chrome Web Store per la revisione. - Webapp: L'applicazione Next.js è ospitata su Netlify, con un database PostgreSQL (su Supabase) che si integra perfettamente con la piattaforma.
- Gestione degli Ambienti: Le configurazioni per sviluppo, staging e produzione sono gestite tramite environment variables, con un secret management adeguato per le chiavi sensibili.
Lezioni Apprese e Prossimi Passi
Punti di Forza dell'Architettura
- Modularità: La capacità di iterare su una parte del sistema senza compromettere il resto.
- Parser Specifici: Un vero differenziatore per la qualità dell'estrazione dei dati.
- TypeScript End-to-End: Ha prevenuto intere categorie di bug, specialmente nell'integrazione.
- Test Granulari: La possibilità di testare ogni parser in isolamento ha garantito l'affidabilità del core.
Aree di Miglioramento
- Gestione degli Errori: Implementare una logica più granulare, soprattutto per problemi di rete e API.
- Performance: Introdurre il lazy loading per i parser più complessi.
- Nuovi parser: Integrare nuovi parser specifici per migliorare l'estrazione da specifiche fonti.
Riflessioni Finali
Sviluppare Annotiquo è stata un'immersione profonda nel mondo delle estensioni browser. L'approccio modulare si è confermato la scelta vincente, fornendo la flessibilità necessaria per sperimentare e crescere senza dover mai ricostruire dalle fondamenta.
A chiunque stia considerando di sviluppare un'estensione, il mio consiglio è questo: Manifest V3 è un'opportunità per scrivere codice migliore, TypeScript è un alleato insostituibile, e un'architettura pensata per la modularità ripagherà ogni sforzo nel lungo periodo. Il progetto è in continua evoluzione, ma la sua spina dorsale si sta dimostrando solida e affidabile.
E adesso?
Vi invito a sperimentarlo direttamente. Trovate tutto il necessario sul sito ufficiale: annotiquo.com.
L'estensione per Chrome è gratuita e funziona anche offline. Con l'iscrizione alla web app, riceverete 10 crediti gratuiti per testare la sintesi con AI, senza alcun impegno.