[{"data":1,"prerenderedAt":972},["ShallowReactive",2],{"meta-profile":3,"command-palette-about":18,"meta-contacts":369,"project-frontwire":387},{"id":4,"extension":5,"logo_name":6,"meta":7,"name":8,"roles":9,"stem":16,"__hash__":17},"profile/meta/profile.yml","yml","giovanni-manara",{},"Giovanni Manara",[10,11,12,13,14,15],"Full Stack Developer","WordPress Developer","Frontend Developer","GSAP Enthusiast","Tennis Lover","Gamer Appassionato","meta/profile","3BmX-xwCGwakpBF7rjsHDYIpXj8I89zWKoP32P9_MxE",[19,83,105,127,239,292,344],{"id":20,"title":21,"body":22,"description":68,"extension":75,"meta":76,"navigation":77,"order":78,"path":79,"seo":80,"stem":81,"__hash__":82},"about/competencies/competencies.md","competencies",{"type":23,"value":24,"toc":67},"minimark",[25,30,34,37,41,44,47,51,54,57,61,64],[26,27,29],"h2",{"id":28},"problem-solving","Problem solving",[31,32,33],"p",{},"Quando qualcosa non torna, vado a fondo: studio, provo, documento e arrivo alla soluzione con costanza.",[31,35,36],{},"Nel lavoro quotidiano applico lo stesso approccio: capire il problema, scomporlo in parti gestibili, e farlo funzionare davvero — senza scorciatoie che poi costano in produzione.",[26,38,40],{"id":39},"collaborazione","Collaborazione",[31,42,43],{},"Ho fatto sport a diversi livelli fin da giovane, e questo mi ha lasciato una cosa utilissima nel lavoro: saper stare in squadra, comunicare in modo chiaro e contribuire senza \"rumore\".",[31,45,46],{},"Su progetti reali e codebase già in produzione, collaborare significa allinearsi sul problema, condividere contesto e consegnare in modo affidabile — non solo scrivere codice in isolamento.",[26,48,50],{"id":49},"qualità-del-codice","Qualità del codice",[31,52,53],{},"Mi piace scrivere codice pulito e mantenere codebase leggibili e facili da manutenere — sia su personalizzazioni WordPress (temi/plugin) sia su progetti custom.",[31,55,56],{},"La qualità, per me, non è un dettaglio estetico: è logica, ordine e attenzione ai dettagli, così che un progetto prenda forma passo dopo passo e regga in produzione.",[26,58,60],{"id":59},"ai-nel-workflow","AI nel workflow",[31,62,63],{},"Uso quotidianamente strumenti come Claude Code e Cursor, ma con regole precise: la spec e il piano vengono prima del codice, ogni modifica passa da una review, e ogni repository ha le sue convenzioni scritte che anche gli agenti devono rispettare. L'AI accelera l'esecuzione; le decisioni — architettura, priorità, cosa entra in produzione — restano mie.",[31,65,66],{},"Quando ho voluto imparare React sul serio, ho ribaltato i ruoli: sul mio progetto personale l'AI poteva solo fare da reviewer, mai scrivere. Ogni riga l'ho scritta io. Saper usare bene questi strumenti significa anche sapere quando spegnerli.",{"title":68,"searchDepth":69,"depth":69,"links":70},"",2,[71,72,73,74],{"id":28,"depth":69,"text":29},{"id":39,"depth":69,"text":40},{"id":49,"depth":69,"text":50},{"id":59,"depth":69,"text":60},"md",{},true,1,"/competencies/competencies",{"title":21,"description":68},"competencies/competencies","tSgPbmSai27j_O1iC3cKrE7Mc_0ZWTLuDrwFdiBD4vA",{"id":84,"title":85,"body":86,"description":90,"extension":75,"meta":100,"navigation":77,"order":78,"path":101,"seo":102,"stem":103,"__hash__":104},"about/experiences/megiston.md","megiston",{"type":23,"value":87,"toc":98},[88,91],[31,89,90],{},"Dal 2022 lavoro come sviluppatore web, con un focus pratico su progetti reali e codebase già in produzione.",[31,92,93,97],{},[94,95,96],"strong",{},"2025 - oggi | Megiston S.r.l."," — Full stack web developer in un team piccolo, dove si tocca un po' di tutto. Mi sono occupato del frontend di una piattaforma di biglietteria online (Nuxt su API REST, interfacce costruite da Figma con Tailwind, carrello e pagamenti online), di personalizzazioni WordPress profonde — plugin custom per preventivi multi-step, dati strutturati JSON-LD, integrazioni con booking engine, ecc. — e attualmente del backend del nuovo sito aziendale con Payload CMS.",{"title":68,"searchDepth":69,"depth":69,"links":99},[],{},"/experiences/megiston",{"title":85,"description":90},"experiences/megiston","30UBT7QbA9xQo9Vrb9OritBBGpQLwd_4g3epIiFz3LQ",{"id":106,"title":107,"body":108,"description":121,"extension":75,"meta":122,"navigation":77,"order":69,"path":123,"seo":124,"stem":125,"__hash__":126},"about/experiences/websonica.md","websonica",{"type":23,"value":109,"toc":119},[110,116],[31,111,112,115],{},[94,113,114],{},"2022 - 2025 | Websonica S.r.l."," — Prima come stage web developer (2022-2023), poi come junior web developer (2023-2025). Ho lavorato su gestione e personalizzazione di siti WordPress e Magento2, su software custom in Laravel e Next.js, e su CRM come vTiger.",[31,117,118],{},"Mi piace tenere un approccio ordinato: capire il problema, scomporlo, e farlo funzionare davvero (senza magia nera… o almeno provandoci).",{"title":68,"searchDepth":69,"depth":69,"links":120},[],"2022 - 2025 | Websonica S.r.l. — Prima come stage web developer (2022-2023), poi come junior web developer (2023-2025). Ho lavorato su gestione e personalizzazione di siti WordPress e Magento2, su software custom in Laravel e Next.js, e su CRM come vTiger.",{},"/experiences/websonica",{"title":107,"description":121},"experiences/websonica","dVGZo1BJjKJRXpZU7gBpp2M0iuKv_52-qjEPKsA1GnA",{"id":128,"title":129,"body":130,"description":68,"extension":75,"meta":234,"navigation":77,"order":78,"path":235,"seo":236,"stem":237,"__hash__":238},"about/personal-life/bio.md","bio",{"type":23,"value":131,"toc":226},[132,139,143,146,149,152,156,159,162,166,171,174,185,192,196,203],[133,134],"bio-portrait",{"alt":135,"file":136,"size":137,"src":138},"Ritratto in bianco e nero di Giovanni Manara","giovanni.jpg","400×400","/images/bio/giovanni.webp",[26,140,142],{"id":141},"about-me","About me",[31,144,145],{},"Mi chiamo Giovanni Manara e lavoro come sviluppatore web dal 2022: prima uno stage, poi un percorso da junior, oggi full stack con propensione al frontend in una piccola web agency vicino Padova.",[31,147,148],{},"Mi muovo soprattutto su due binari: applicazioni web in Vue/Nuxt, dove mi sono occupato a lungo del frontend di una piattaforma di vendita online in produzione, e WordPress fatto sul serio — temi custom e plugin scritti da zero quando i builder non bastano. Ultimamente sto costruendo il backend del nuovo sito aziendale con Payload CMS.",[31,150,151],{},"Il filo conduttore: partire dal design, tenere la logica separata dalla UI, e quando un flusso mi serve più di una volta trasformarlo in uno strumento riusabile.",[26,153,155],{"id":154},"fuori-dal-lavoro","Fuori dal lavoro",[31,157,158],{},"Mi piace la tecnologia in senso ampio: dal web alle soluzioni \"ben costruite\", fino a tutto ciò che migliora processi e prodotti.",[31,160,161],{},"Mi piace anche viaggiare e cucinare: scoprire posti nuovi e provare sapori diversi è un ottimo modo per tenere la curiosità allenata.",[163,164],"bio-photos",{":photos":165},"[{\"name\":\"pale-san-martino\",\"file\":\"pale-san-martino.jpg\",\"caption\":\"Parco Paneveggio\",\"alt\":\"Giovanni con il suo cane su un sentiero di montagna, con le Pale di San Martino sullo sfondo\"},{\"name\":\"tsavo-east\",\"file\":\"tsavo-east.jpg\",\"caption\":\"Kenya\",\"alt\":\"Elefanti nella vegetazione dello Tsavo East\"},{\"name\":\"rovigno\",\"file\":\"rovigno.jpg\",\"caption\":\"Istria\",\"alt\":\"Rovigno vista dal mare\"},{\"name\":\"louvre\",\"file\":\"louvre.jpg\",\"caption\":\"Parigi\",\"alt\":\"Il passaggio ad arco del Louvre con la piramide di vetro sullo sfondo\"},{\"name\":\"rub-al-khali\",\"file\":\"rub-al-khali.jpg\",\"caption\":\"Oman\",\"alt\":\"Dune del deserto Rub' al-Khali al tramonto\"},{\"name\":\"foro-italico\",\"file\":\"foro-italico.jpg\",\"caption\":\"finale Roma 2026\",\"alt\":\"Il campo centrale del Foro Italico gremito durante una finale\"}]",[167,168,170],"h3",{"id":169},"sport","Sport",[31,172,173],{},"Ho praticato diversi sport (con una breve parentesi tra calcio e basket), ma i due che mi hanno formato di più sono stati scherma e tennis.",[31,175,176,177,180,181,184],{},"Per ",[94,178,179],{},"sei anni"," ho fatto agonismo nella ",[94,182,183],{},"scherma",", ottenendo buoni risultati a livello regionale e nazionale. Più che i piazzamenti, mi porto dietro ciò che conta davvero: fiducia costruita nel tempo, disciplina, ambizione e la capacità di lavorare in squadra anche quando la pressione sale.",[31,186,187,188,191],{},"Oggi il mio sport è il ",[94,189,190],{},"tennis",": è l'unico che pratico con continuità e che seguo davvero, in TV, in streaming e, quando posso, dal vivo ai tornei. Federer è stato il mio riferimento, e adesso mi godo con entusiasmo Alcaraz e (inevitabilmente) Sinner.",[167,193,195],{"id":194},"videogiochi","Videogiochi",[31,197,198,199,202],{},"Nei videogiochi mi sento a casa nei ",[94,200,201],{},"GDR",", soprattutto quelli story-driven: mi piace immergermi nelle storie, nei personaggi e nelle scelte (quelle che poi ti fanno pensare \"ok, questa me la porto dietro\").",[31,204,205,206,209,210,213,214,217,218,221,222,225],{},"Il mio primo \"innesco\" è stato ",[94,207,208],{},"Final Fantasy X",", e da lì ho continuato a cercare esperienze simili. Tra i miei preferiti ci sono la trilogia di ",[94,211,212],{},"Mass Effect"," (su Andromeda sorvoliamo), ",[94,215,216],{},"Baldur's Gate 3",", la saga di ",[94,219,220],{},"Persona"," (3, 4 e 5) e ",[94,223,224],{},"Clair Obscur 33",", probabilmente il mio preferito degli ultimi anni.",{"title":68,"searchDepth":69,"depth":69,"links":227},[228,229],{"id":141,"depth":69,"text":142},{"id":154,"depth":69,"text":155,"children":230},[231,233],{"id":169,"depth":232,"text":170},3,{"id":194,"depth":232,"text":195},{},"/personal-life/bio",{"title":129,"description":68},"personal-life/bio","brL5L7zYXUzTtJtrdYJUmNbmeKw9L4JncjkmvNSN6Xk",{"id":240,"title":241,"body":242,"description":68,"extension":75,"meta":287,"navigation":77,"order":232,"path":288,"seo":289,"stem":290,"__hash__":291},"about/personal-life/education.md","education",{"type":23,"value":243,"toc":284},[244,248,251],[26,245,247],{"id":246},"educazione","Educazione",[31,249,250],{},"Un percorso non lineare: dalle biotecnologie allo sviluppo web, passando per la bioinformatica.",[252,253,254,261,267,278],"ul",{},[255,256,257,260],"li",{},[94,258,259],{},"2010-2015"," — Diploma al Liceo Scientifico Enrico Fermi (Padova).",[255,262,263,266],{},[94,264,265],{},"2015-2019"," — Laurea Triennale in Biotecnologie, Università del Piemonte Orientale (Novara).",[255,268,269,272,273,277],{},[94,270,271],{},"2019-2021"," — Laurea Magistrale in Bioinformatica, Università Tor Vergata (Roma). Qui mi sono avvicinato allo sviluppo web grazie a un corso di ",[274,275,276],"em",{},"Applicazioni web per la Bioinformatica",": con il contesto del COVID e una passione crescente, ho scelto di mettere in pausa l'università per concentrarmi sulla programmazione.",[255,279,280,283],{},[94,281,282],{},"2022"," — Corso professionalizzante in Full Stack Web Development (600 ore, riconosciuto MIUR), Accademia delle Professioni (Noventa Padovana), concluso con ottimi risultati.",{"title":68,"searchDepth":69,"depth":69,"links":285},[286],{"id":246,"depth":69,"text":247},{},"/personal-life/education",{"title":241,"description":68},"personal-life/education","zKz_U6qhxDUbh3tCI3bf5XcUlJLHsE-Ea0mDOJhN4ro",{"id":293,"title":294,"body":295,"description":299,"extension":75,"meta":339,"navigation":77,"order":78,"path":340,"seo":341,"stem":342,"__hash__":343},"about/skills/hard-skills.md","hard-skills",{"type":23,"value":296,"toc":337},[297,300],[31,298,299],{},"Mi muovo tra front-end e back-end, con il frontend come casa.",[31,301,302,305,306,309,312,313,315,318,319,321,324,325,327,330,331,333,336],{},[94,303,304],{},"Fondamentali:"," HTML, CSS, JavaScript, TypeScript.",[307,308],"br",{},[94,310,311],{},"Frontend:"," Vue 3 / Nuxt, React / Next.js, Pinia.",[307,314],{},[94,316,317],{},"Stili:"," Tailwind, Sass, DaisyUI, design token da Figma.",[307,320],{},[94,322,323],{},"WordPress:"," temi child e plugin custom OO, ACF, WooCommerce, WPML.",[307,326],{},[94,328,329],{},"Backend:"," PHP, Payload CMS, MySQL, PostgreSQL.",[307,332],{},[94,334,335],{},"Strumenti:"," Git, GSAP, Stripe, i18n, sviluppo AI-assisted (Claude Code, Cursor) con workflow spec-first.",{"title":68,"searchDepth":69,"depth":69,"links":338},[],{},"/skills/hard-skills",{"title":294,"description":299},"skills/hard-skills","eFu6VaeKB7pA6op62PTPLV3PIMfwNP9blrWwzkBQMq8",{"id":345,"title":346,"body":347,"description":363,"extension":75,"meta":364,"navigation":77,"order":69,"path":365,"seo":366,"stem":367,"__hash__":368},"about/skills/soft-skills.md","soft-skills",{"type":23,"value":348,"toc":361},[349,355],[31,350,351,354],{},[94,352,353],{},"Teamwork:"," ho fatto sport a diversi livelli fin da giovane, e questo mi ha lasciato una cosa utilissima nel lavoro: saper stare in squadra, comunicare in modo chiaro e contribuire senza \"rumore\".",[31,356,357,360],{},[94,358,359],{},"Tenacia e affidabilità:"," le sfide mi stimolano. Quando qualcosa non torna, vado a fondo: studio, provo, documento e arrivo alla soluzione con costanza. Mi piace prendermi responsabilità su quello che consegno, perché il codice in produzione non perdona (ma insegna parecchio).",{"title":68,"searchDepth":69,"depth":69,"links":362},[],"Teamwork: ho fatto sport a diversi livelli fin da giovane, e questo mi ha lasciato una cosa utilissima nel lavoro: saper stare in squadra, comunicare in modo chiaro e contribuire senza \"rumore\".",{},"/skills/soft-skills",{"title":346,"description":363},"skills/soft-skills","QyBwnFi9-ZYJc-xGJ0-L1syIeASIp61uVcI2WBJ_HZo",{"id":370,"direct":371,"extension":5,"meta":376,"social":377,"stem":385,"__hash__":386},"contacts/meta/contacts.yml",{"title":372,"sources":373},"contacts",{"email":374,"phone":375},"giovanni@megiston.com","+39 375 890 5337",{},{"github":378,"linkedin":382},{"title":379,"url":380,"user":381},"Github profile","https://github.com/","GioManara96",{"title":383,"url":384,"user":8},"Linkedin account","https://www.linkedin.com/in/giovanni-manara-80b3b324a/","meta/contacts","RRkgU5Gw5D8CgGn_1pHPxlHpY4SScHEEM5E-3oWOzdc",{"id":388,"title":389,"body":390,"description":946,"extension":75,"img":947,"liveUrl":920,"meta":948,"navigation":77,"path":949,"projectId":637,"seo":950,"slug":951,"stack":952,"stem":968,"tech":969,"url":929,"__hash__":971},"projects/projects/frontwire.md","Frontwire",{"type":23,"value":391,"toc":936},[392,396,399,406,410,413,420,424,427,450,457,460,471,475,485,699,705,711,786,796,803,817,821,830,837,840,858,865,872,876,899,903,906,909,913,932],[26,393,395],{"id":394},"cosè","Cos'è",[31,397,398],{},"Frontwire è la rassegna di notizie di web development che volevo leggere: framework frontend (Nuxt, Next, Vue, React, Vite) e l'AI che riguarda chi scrive codice — modelli, API, strumenti per sviluppatori. L'AI applicata ad altri campi, le notizie aziendali e i round di finanziamento restano fuori.",[31,400,401,402,405],{},"L'archivio è pensato per essere letto ",[94,403,404],{},"per intero",": al massimo 3 articoli per fonte, i più recenti, entro 30 giorni. Una trentina in tutto. Non è un feed infinito, è una pagina che finisce.",[26,407,409],{"id":408},"il-problema","Il problema",[31,411,412],{},"Le notizie che mi servono sono sparse su venti blog ufficiali, tre newsletter settimanali e Hacker News. Gli aggregatori generalisti annegano il frontend nel resto; i reader RSS mi restituivano il problema di partenza, cioè troppa roba.",[31,414,415,416,419],{},"Il vincolo vero però era un altro: ",[94,417,418],{},"zero costi",". Niente database, niente server, niente API a pagamento. Il che esclude in partenza la soluzione ovvia (un backend che fa polling e una tabella) e obbliga a chiedersi dove vivano i dati.",[26,421,423],{"id":422},"come-funziona","Come funziona",[31,425,426],{},"I dati vivono nel repository. Il sito è statico.",[252,428,429,432,444],{},[255,430,431],{},"Una GitHub Action pianificata ogni 6 ore scarica le fonti, normalizza gli articoli, rimuove i duplicati, assegna i tag e scarta gli articoli AI che non riguardano chi sviluppa",[255,433,434,435,439,440,443],{},"Il risultato viene scritto in ",[436,437,438],"code",{},"data/articles.json"," e committato su ",[436,441,442],{},"main"," dal bot",[255,445,446,447,449],{},"Ogni push su ",[436,448,442],{},", compresi quelli del bot, fa ripartire il deploy statico su Vercel",[31,451,452,453,456],{},"Il commit del bot ",[274,454,455],{},"è"," il trigger del deploy. Non serve nient'altro: niente webhook, niente revalidate, niente cron su un server che non ho.",[31,458,459],{},"Solo feed RSS/Atom e API ufficiali, mai scraping. Gli articoli non vengono mai ripubblicati per intero: l'archivio tiene l'estratto della fonte e linka l'originale, con le note di rilascio di GitHub come unica eccezione.",[31,461,462,463,466,467,470],{},"Il tipo ",[436,464,465],{},"Article",", il vocabolario dei tag e il registro delle fonti vivono in ",[436,468,469],{},"shared/",", importato sia dall'app che dalla pipeline: una sola fonte di verità per lo schema.",[26,472,474],{"id":473},"dettagli-di-cui-vado-fiero","Dettagli di cui vado fiero",[31,476,477,480,481,484],{},[94,478,479],{},"Lo stesso articolo da due fonti collassa in uno."," L'id è un hash dell'URL normalizzato: parametri di tracking, ",[436,482,483],{},"www.",", slash finale, fragment e ordine dei parametri non contano. Quando un post del blog di React arriva anche da This Week In React, è già lo stesso record.",[486,487,491],"pre",{"className":488,"code":489,"language":490,"meta":68,"style":68},"language-ts shiki shiki-themes one-dark-pro","const normalized = new URL(url);\nnormalized.hostname = normalized.hostname.replace(/^www\\./, \"\");\nnormalized.hash = \"\";\n// Copy the keys first: deleting while iterating searchParams skips entries.\nfor (const key of [...normalized.searchParams.keys()]) {\n  if (TRACKING_PARAM.test(key)) normalized.searchParams.delete(key);\n}\nnormalized.searchParams.sort();\n","ts",[436,492,493,527,576,593,600,635,676,682],{"__ignoreMap":68},[494,495,497,501,505,509,512,516,520,524],"span",{"class":496,"line":78},"line",[494,498,500],{"class":499},"seHd6","const",[494,502,504],{"class":503},"sU0A5"," normalized",[494,506,508],{"class":507},"sjrmR"," =",[494,510,511],{"class":499}," new",[494,513,515],{"class":514},"sVbv2"," URL",[494,517,519],{"class":518},"sn6KH","(",[494,521,523],{"class":522},"sVyAn","url",[494,525,526],{"class":518},");\n",[494,528,529,532,535,538,540,542,544,546,548,551,553,556,559,562,565,567,570,574],{"class":496,"line":69},[494,530,531],{"class":503},"normalized",[494,533,534],{"class":518},".",[494,536,537],{"class":522},"hostname",[494,539,508],{"class":507},[494,541,504],{"class":503},[494,543,534],{"class":518},[494,545,537],{"class":503},[494,547,534],{"class":518},[494,549,550],{"class":514},"replace",[494,552,519],{"class":518},[494,554,555],{"class":522},"/",[494,557,558],{"class":499},"^",[494,560,561],{"class":522},"www",[494,563,564],{"class":507},"\\.",[494,566,555],{"class":522},[494,568,569],{"class":518},", ",[494,571,573],{"class":572},"subq3","\"\"",[494,575,526],{"class":518},[494,577,578,580,582,585,587,590],{"class":496,"line":232},[494,579,531],{"class":503},[494,581,534],{"class":518},[494,583,584],{"class":522},"hash",[494,586,508],{"class":507},[494,588,589],{"class":572}," \"\"",[494,591,592],{"class":518},";\n",[494,594,596],{"class":496,"line":595},4,[494,597,599],{"class":598},"sV9Aq","// Copy the keys first: deleting while iterating searchParams skips entries.\n",[494,601,603,606,609,611,614,617,620,622,624,627,629,632],{"class":496,"line":602},5,[494,604,605],{"class":499},"for",[494,607,608],{"class":518}," (",[494,610,500],{"class":499},[494,612,613],{"class":503}," key",[494,615,616],{"class":499}," of",[494,618,619],{"class":518}," [...",[494,621,531],{"class":503},[494,623,534],{"class":518},[494,625,626],{"class":503},"searchParams",[494,628,534],{"class":518},[494,630,631],{"class":514},"keys",[494,633,634],{"class":518},"()]) {\n",[494,636,638,641,643,646,648,651,653,656,659,661,663,665,667,670,672,674],{"class":496,"line":637},6,[494,639,640],{"class":499},"  if",[494,642,608],{"class":518},[494,644,645],{"class":503},"TRACKING_PARAM",[494,647,534],{"class":518},[494,649,650],{"class":514},"test",[494,652,519],{"class":518},[494,654,655],{"class":522},"key",[494,657,658],{"class":518},")) ",[494,660,531],{"class":503},[494,662,534],{"class":518},[494,664,626],{"class":503},[494,666,534],{"class":518},[494,668,669],{"class":514},"delete",[494,671,519],{"class":518},[494,673,655],{"class":522},[494,675,526],{"class":518},[494,677,679],{"class":496,"line":678},7,[494,680,681],{"class":518},"}\n",[494,683,685,687,689,691,693,696],{"class":496,"line":684},8,[494,686,531],{"class":503},[494,688,534],{"class":518},[494,690,626],{"class":503},[494,692,534],{"class":518},[494,694,695],{"class":514},"sort",[494,697,698],{"class":518},"();\n",[31,700,701,704],{},[94,702,703],{},"Il merge non riscrive mai il passato."," Un articolo già archiviato non viene aggiornato dal feed: entra solo se l'id è nuovo. Poi valgono i due limiti — finestra di 30 giorni e tetto di 3 per fonte — e l'ordinamento è stabile, ties risolti per id. Gli stessi dati serializzano sempre negli stessi byte, quindi il bot non genera commit rumore quando non è cambiato niente.",[31,706,707,710],{},[94,708,709],{},"Il filtro di pertinenza è una regex, non un classificatore."," Un articolo AI entra solo se il titolo cita un modello con la versione o un termine da sviluppatore. È una scelta di budget diventata una scelta di design: le regole sono leggibili, testabili e si tarano in un file solo.",[486,712,714],{"className":488,"code":713,"language":490,"meta":68,"style":68},"// A model family followed by a version: `GPT-6`, `o3`, `Claude Opus 5`, `DeepSeek-R2`…\nconst MODEL_VERSION = /gpt[-‑ ]?\\d|\\bo\\d\\b|\\bclaude(?: (?:opus|sonnet|haiku))? ?\\d|…/i;\n",[436,715,716,721],{"__ignoreMap":68},[494,717,718],{"class":496,"line":78},[494,719,720],{"class":598},"// A model family followed by a version: `GPT-6`, `o3`, `Claude Opus 5`, `DeepSeek-R2`…\n",[494,722,723,725,728,730,733,737,740,743,746,749,751,753,755,758,760,763,765,768,771,774,776,778,781,784],{"class":496,"line":69},[494,724,500],{"class":499},[494,726,727],{"class":503}," MODEL_VERSION",[494,729,508],{"class":507},[494,731,732],{"class":522}," /gpt",[494,734,736],{"class":735},"sVC51","[-‑ ]?",[494,738,739],{"class":522},"\\d",[494,741,742],{"class":518},"|",[494,744,745],{"class":499},"\\b",[494,747,748],{"class":522},"o\\d",[494,750,745],{"class":499},[494,752,742],{"class":518},[494,754,745],{"class":499},[494,756,757],{"class":522},"claude(?: (?:opus",[494,759,742],{"class":518},[494,761,762],{"class":522},"sonnet",[494,764,742],{"class":518},[494,766,767],{"class":522},"haiku))",[494,769,770],{"class":735},"?",[494,772,773],{"class":735}," ?",[494,775,739],{"class":522},[494,777,742],{"class":518},[494,779,780],{"class":522},"…/",[494,782,783],{"class":499},"i",[494,785,592],{"class":518},[31,787,788,791,792,795],{},[94,789,790],{},"I filtri stanno nella query, non in uno stato locale."," Categoria e tag vivono nell'URL (",[436,793,794],{},"/?category=ai&tags=react,nextjs","), quindi ogni vista filtrata è condivisibile e il tasto Indietro riporta ai filtri precedenti. I filtri però restano vuoti fino al mount: la home è prerenderizzata e non ha query, e filtrare durante l'hydration farebbe divergere l'HTML del client da quello del server.",[31,797,798],{},[799,800],"img",{"alt":801,"src":802},"L'archivio filtrato per categoria e tag, con lo stato nella query","/icons/projects/frontwire-filters.webp",[31,804,805,808,809,812,813,816],{},[94,806,807],{},"I preferiti salvano una copia, non un riferimento."," Niente account e niente database: la lista sta nel ",[436,810,811],{},"localStorage",". Quello che si legge da lì non è di fiducia — si modifica a mano e sopravvive ai rilasci — quindi ogni voce passa da un controllo di forma contro i vocabolari reali di fonti e tag, non solo contro i tipi: un ",[436,814,815],{},"sourceId"," inventato manderebbe la card a cercare l'icona di una fonte che non esiste.",[26,818,820],{"id":819},"il-processo","Il processo",[31,822,823,824,829],{},"Qui il confronto con ",[825,826,828],"a",{"href":827},"/casi-studio/echoes","Echoes"," è la parte interessante.",[31,831,832,833,836],{},"Su Echoes il vincolo era scrivere tutto a mano: l'AI faceva da reviewer, mai da autore, perché stavo imparando React e Next.js e non volevo farmeli generare. Su Frontwire il vincolo è l'opposto: ",[94,834,835],{},"il codice lo scrive l'agente, io dirigo."," Non perché sia più comodo, ma perché è l'altra competenza da esercitare, e perché il tempo per seguire riga per riga un progetto personale non ce l'avevo.",[31,838,839],{},"Dirigere però non vuol dire delegare e sperare. Quello che ho scritto io è il contorno:",[252,841,842,849,852,855],{},[255,843,844,845,848],{},"Un working agreement nel repo (",[436,846,847],{},"CLAUDE.md","): prodotto, architettura, stile del codice, regole per i commenti, policy di git. È il documento che l'agente legge a ogni sessione, e le regole valgono anche per il codice che scrive lui",[255,850,851],{},"Una spec di design per ogni tappa, discussa prima di scrivere codice. Sette tappe, sette documenti — il perché delle scelte sta lì, non nei messaggi di commit",[255,853,854],{},"Le decisioni di prodotto e infrastruttura, sempre mie: quali fonti entrano, quale tetto per fonte, riassunti AI sì o no, come si gestisce il deploy",[255,856,857],{},"Le azioni verso l'esterno (push, merge, lancio dei workflow) solo su mio via libera",[31,859,860,861,864],{},"I commenti seguono ",[274,862,863],{},"A Philosophy of Software Design",": registrano quello che era nella testa di chi ha progettato il codice e che il codice da solo non dice. È la regola che ha alzato di più la qualità dell'output, perché costringe a spiegare le decisioni invece di descrivere le righe.",[31,866,867,868,871],{},"I test sono Vitest sulla logica pura della pipeline — parsing dei feed, normalizzazione, deduplica, tag — con fixture locali, mai chiamate di rete. Un test d'integrità su ",[436,869,870],{},"articles.json"," fa anche da cancello nell'Action: se fallisce, niente commit.",[26,873,875],{"id":874},"cosa-ho-imparato","Cosa ho imparato",[252,877,878,884,890],{},[255,879,880,883],{},[94,881,882],{},"Un vincolo di budget è un vincolo di architettura."," \"Niente servizi a pagamento\" ha deciso il filtro (regex e non classificatore), l'assenza di riassunti, la persistenza (JSON nel repo) e il modello di deploy. Non è stata una rinuncia in ognuno di quei punti: è stato il criterio che li ha resi coerenti tra loro",[255,885,886,889],{},[94,887,888],{},"Scrivere la spec prima costa poco e si ripaga subito",", e vale a prescindere da chi scrive il codice — vale con un agente, varrebbe con un collega",[255,891,892,895,896,898],{},[94,893,894],{},"Fidarsi dei dati altrui è il bug."," Feed, ",[436,897,811],{},", id delle fonti: tutto arriva da fuori e tutto va validato contro il vocabolario reale, non contro il tipo TypeScript, che a runtime non esiste",[26,900,902],{"id":901},"limiti-consapevoli","Limiti consapevoli",[31,904,905],{},"Niente riassunti AI: il provider che avevo previsto è stato ritirato, e con i feed reali quasi nessun articolo ha abbastanza testo da riassumere — i blog portano una descrizione di una riga e non faccio scraping. Il feed di Anthropic è un mirror della community, quindi una dipendenza esterna che può sparire. Il filtro di pertinenza guarda solo il titolo: qualche falso negativo passa.",[31,907,908],{},"In roadmap: nuove fonti già valutate (Astro, Tailwind, WebKit) e una pagina che spieghi al lettore perché una certa notizia è finita lì dentro.",[26,910,912],{"id":911},"link","Link",[252,914,915,924],{},[255,916,917,918],{},"Live: ",[825,919,923],{"href":920,"rel":921},"https://frontwire.giovannimanara.dev/",[922],"nofollow","frontwire.giovannimanara.dev",[255,925,926,927],{},"Codice: ",[825,928,931],{"href":929,"rel":930},"https://github.com/GioManara96/frontwire",[922],"github.com/GioManara96/frontwire",[933,934,935],"style",{},"html pre.shiki code .seHd6, html code.shiki .seHd6{--shiki-default:#C678DD}html pre.shiki code .sU0A5, html code.shiki .sU0A5{--shiki-default:#E5C07B}html pre.shiki code .sjrmR, html code.shiki .sjrmR{--shiki-default:#56B6C2}html pre.shiki code .sVbv2, html code.shiki .sVbv2{--shiki-default:#61AFEF}html pre.shiki code .sn6KH, html code.shiki .sn6KH{--shiki-default:#ABB2BF}html pre.shiki code .sVyAn, html code.shiki .sVyAn{--shiki-default:#E06C75}html pre.shiki code .subq3, html code.shiki .subq3{--shiki-default:#98C379}html pre.shiki code .sV9Aq, html code.shiki .sV9Aq{--shiki-default:#7F848E;--shiki-default-font-style:italic}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sVC51, html code.shiki .sVC51{--shiki-default:#D19A66}",{"title":68,"searchDepth":69,"depth":69,"links":937},[938,939,940,941,942,943,944,945],{"id":394,"depth":69,"text":395},{"id":408,"depth":69,"text":409},{"id":422,"depth":69,"text":423},{"id":473,"depth":69,"text":474},{"id":819,"depth":69,"text":820},{"id":874,"depth":69,"text":875},{"id":901,"depth":69,"text":902},{"id":911,"depth":69,"text":912},"Un archivio di notizie su frontend e AI per chi sviluppa, senza database e senza server — una GitHub Action ogni 6 ore importa i feed, committa un JSON nel repo e fa ripartire la build statica su Nuxt 4.","/icons/projects/frontwire-wide.webp",{},"/projects/frontwire",{"title":389,"description":946},"frontwire",[953,956,959,962,964,966],{"icon":954,"label":955},"cib:nuxt-js","Nuxt 4",{"icon":957,"label":958},"cib:vue-js","Vue 3",{"icon":960,"label":961},"cib:typescript","TypeScript",{"label":963},"Tailwind v4",{"label":965},"GitHub Actions",{"label":967},"Vitest","projects/frontwire",[970],"nuxt-js","r3J4XKhnJvXs6Z8qCO28V8QUKyStH9ghNXOFchYuXkQ",1793181022507]