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:

  1. Parser Generico: Utilizza Mozilla Readability e gestisce l'80% dei siti web (articoli, blog, ecc.).
  2. 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.