[{"data":1,"prerenderedAt":2090},["ShallowReactive",2],{"\u002Fblog\u002Fgrafiki-artykulow-i-obrazy-og":3,"\u002Fblog\u002Fgrafiki-artykulow-i-obrazy-og-surround":2085},{"id":4,"title":5,"author":6,"body":7,"date":2073,"description":2074,"draft":2075,"extension":2076,"image":2077,"meta":2078,"minRead":2079,"navigation":560,"path":2080,"seo":2081,"sitemap":2082,"stem":2083,"__hash__":2084},"blog\u002Fblog\u002Fgrafiki-artykulow-i-obrazy-og.md","Grafiki i obrazy OG — nowe SKILL-e","Artur Wilczek",{"type":8,"value":9,"toc":2055},"minimark",[10,15,42,72,85,89,116,262,283,301,305,308,338,375,396,402,444,463,467,474,481,503,522,528,531,821,828,843,850,855,873,881,895,902,906,920,957,960,1000,1003,1189,1216,1222,1241,1512,1530,1536,1543,1550,1554,1588,1689,1698,1702,1712,1728,1744,1765,1846,1853,1857,1871,1911,1914,1936,1954,1995,2009,2013,2016,2044,2051],[11,12,14],"h2",{"id":13},"wstęp","Wstęp",[16,17,18,19,24,25,29,30,33,34,37,38,41],"p",{},"W ",[20,21,23],"a",{"href":22},"\u002Fblog\u002Fai-driven-development-workflow","poprzednim artykule"," opisałem, jak zbudowałem zespół wyspecjalizowanych ",[26,27,28],"strong",{},"SKILL-i Claude Code"," do prowadzenia projektu — od planowania zadań po dokumentację. Tamten workflow dotyczył kodu. Tym razem chcę pokazać, że ta sama filozofia — ",[26,31,32],{},"wyspecjalizowany agent + skrypt pomocniczy"," — świetnie sprawdza się też tam, gdzie zwykle sięga się po grafika albo po godzinę grzebania w edytorze: przy ",[26,35,36],{},"grafikach nagłówkowych artykułów"," i ",[26,39,40],{},"obrazach Open Graph",".",[16,43,44,45,49,50,49,53,49,56,59,60,63,64,67,68,71],{},"Problem był prozaiczny. Każdy nowy artykuł potrzebuje grafiki w nagłówku. Każda podstrona — ",[46,47,48],"code",{},"\u002F",", ",[46,51,52],{},"\u002Fabout",[46,54,55],{},"\u002Fcontact",[46,57,58],{},"\u002Fblog"," — potrzebuje sensownego obrazu, który pokaże się, gdy ktoś wrzuci link na ",[26,61,62],{},"LinkedIn"," czy do ",[26,65,66],{},"Slack-a",". Robienie tego ręcznie jest trudne i czasochłonne i — co gorsza — ",[26,69,70],{},"niespójne",": za każdym razem trochę inny styl, inny font, inny kadr. A spójność wizualna to jedyne, co odróżnia stronę wyglądającą profesjonalnie od zlepka przypadkowych obrazków.",[16,73,74,75,37,80,41],{},"Postanowiłem to zautomatyzować. Efektem są dwa SKILL-e: ",[26,76,77],{},[46,78,79],{},"generate-article-picture",[26,81,82],{},[46,83,84],{},"generate-page-og-picture",[11,86,88],{"id":87},"czym-jest-open-graph-i-po-co-go-stosować","Czym jest Open Graph i po co go stosować",[16,90,91,99,100,103,104,107,108,111,112,115],{},[26,92,93],{},[20,94,98],{"href":95,"rel":96},"https:\u002F\u002Fogp.me\u002F",[97],"nofollow","Open Graph"," (w skrócie ",[26,101,102],{},"OG",") to protokół zaproponowany pierwotnie przez Facebooka, który mówi serwisom społecznościowym i komunikatorom, ",[26,105,106],{},"jak wyświetlić podgląd linku",". Kiedy wklejasz adres strony na LinkedIn, X, Facebooku, w Slack-u czy iMessage, to nie magia podpowiada tytuł, opis i obrazek — robią to znaczniki ",[46,109,110],{},"\u003Cmeta>"," w sekcji ",[46,113,114],{},"\u003Chead>"," strony:",[117,118,123],"pre",{"className":119,"code":120,"language":121,"meta":122,"style":122},"language-html shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u003Cmeta property=\"og:title\" content=\"Spójne grafiki bloga i obrazy OG\" \u002F>\n\u003Cmeta property=\"og:description\" content=\"Dwa SKILL-e Claude Code…\" \u002F>\n\u003Cmeta property=\"og:image\" content=\"https:\u002F\u002Fwww.awfs.dev\u002Fog\u002F0005.png\" \u002F>\n\u003Cmeta property=\"og:url\" content=\"https:\u002F\u002Fwww.awfs.dev\u002Fblog\u002F…\" \u002F>\n","html","",[46,124,125,169,200,231],{"__ignoreMap":122},[126,127,130,134,138,142,145,148,152,154,157,159,161,164,166],"span",{"class":128,"line":129},"line",1,[126,131,133],{"class":132},"sMK4o","\u003C",[126,135,137],{"class":136},"swJcz","meta",[126,139,141],{"class":140},"spNyl"," property",[126,143,144],{"class":132},"=",[126,146,147],{"class":132},"\"",[126,149,151],{"class":150},"sfazB","og:title",[126,153,147],{"class":132},[126,155,156],{"class":140}," content",[126,158,144],{"class":132},[126,160,147],{"class":132},[126,162,163],{"class":150},"Spójne grafiki bloga i obrazy OG",[126,165,147],{"class":132},[126,167,168],{"class":132}," \u002F>\n",[126,170,172,174,176,178,180,182,185,187,189,191,193,196,198],{"class":128,"line":171},2,[126,173,133],{"class":132},[126,175,137],{"class":136},[126,177,141],{"class":140},[126,179,144],{"class":132},[126,181,147],{"class":132},[126,183,184],{"class":150},"og:description",[126,186,147],{"class":132},[126,188,156],{"class":140},[126,190,144],{"class":132},[126,192,147],{"class":132},[126,194,195],{"class":150},"Dwa SKILL-e Claude Code…",[126,197,147],{"class":132},[126,199,168],{"class":132},[126,201,203,205,207,209,211,213,216,218,220,222,224,227,229],{"class":128,"line":202},3,[126,204,133],{"class":132},[126,206,137],{"class":136},[126,208,141],{"class":140},[126,210,144],{"class":132},[126,212,147],{"class":132},[126,214,215],{"class":150},"og:image",[126,217,147],{"class":132},[126,219,156],{"class":140},[126,221,144],{"class":132},[126,223,147],{"class":132},[126,225,226],{"class":150},"https:\u002F\u002Fwww.awfs.dev\u002Fog\u002F0005.png",[126,228,147],{"class":132},[126,230,168],{"class":132},[126,232,234,236,238,240,242,244,247,249,251,253,255,258,260],{"class":128,"line":233},4,[126,235,133],{"class":132},[126,237,137],{"class":136},[126,239,141],{"class":140},[126,241,144],{"class":132},[126,243,147],{"class":132},[126,245,246],{"class":150},"og:url",[126,248,147],{"class":132},[126,250,156],{"class":140},[126,252,144],{"class":132},[126,254,147],{"class":132},[126,256,257],{"class":150},"https:\u002F\u002Fwww.awfs.dev\u002Fblog\u002F…",[126,259,147],{"class":132},[126,261,168],{"class":132},[16,263,264,265,269,270,274,275,278,279,282],{},"Najważniejszy z perspektywy „klikalności” jest ",[26,266,267],{},[46,268,215],{}," — to ten duży obrazek w karcie podglądu. Dobrze dobrana grafika potrafi zauważalnie podnieść współczynnik kliknięć (",[271,272,273],"em",{},"CTR","), bo link przestaje być nagą linijką tekstu, a staje się ",[26,276,277],{},"kafelkiem przyciągającym wzrok",". Rekomendowany rozmiar to ",[26,280,281],{},"1200 × 630 px"," (proporcje 1.91:1) — i tego rozmiaru trzymam się wszędzie.",[16,284,285,286,289,290,49,293,296,297,300],{},"Warto wiedzieć, że OG ma „kuzyna” — ",[26,287,288],{},"Twitter Cards"," (",[46,291,292],{},"twitter:card",[46,294,295],{},"twitter:image","). W praktyce większość platform potrafi awaryjnie sięgnąć po znaczniki OG, więc poprawny zestaw ",[46,298,299],{},"og:*"," załatwia większość przypadków.",[11,302,304],{"id":303},"jak-sprawdzić-czy-strona-ma-poprawny-og","Jak sprawdzić, czy strona ma poprawny OG",[16,306,307],{},"Są dwa sposoby — ręczny i przez dedykowane narzędzia.",[16,309,310,313,314,317,318,321,322,325,326,329,330,333,334,337],{},[26,311,312],{},"Ręcznie."," Najprościej otworzyć ",[26,315,316],{},"źródło strony"," (w przeglądarce ",[46,319,320],{},"Ctrl\u002FCmd + U"," lub ",[271,323,324],{},"View Source",") i wyszukać ",[46,327,328],{},"og:",". Zobaczysz wszystkie znaczniki tak, jak trafiają do robotów. Z konsoli można to samo zrobić jednym poleceniem — używam ",[46,331,332],{},"grep -o"," z wzorcem na sam tag, bo HTML bywa zminifikowany do jednej linii i zwykły ",[46,335,336],{},"grep"," wypisałby wtedy całą stronę zamiast samych meta tagów:",[117,339,343],{"className":340,"code":341,"language":342,"meta":122,"style":122},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","curl -s https:\u002F\u002Fwww.awfs.dev\u002Fcontact | grep -o '\u003Cmeta[^>]*og:[^>]*>'\n","bash",[46,344,345],{"__ignoreMap":122},[126,346,347,351,354,357,360,363,366,369,372],{"class":128,"line":129},[126,348,350],{"class":349},"sBMFI","curl",[126,352,353],{"class":150}," -s",[126,355,356],{"class":150}," https:\u002F\u002Fwww.awfs.dev\u002Fcontact",[126,358,359],{"class":132}," |",[126,361,362],{"class":349}," grep",[126,364,365],{"class":150}," -o",[126,367,368],{"class":132}," '",[126,370,371],{"class":150},"\u003Cmeta[^>]*og:[^>]*>",[126,373,374],{"class":132},"'\n",[16,376,377,378,380,381,384,385,388,389,392,393,41],{},"Pamiętaj tylko, że ",[46,379,215],{}," powinien być ",[26,382,383],{},"adresem absolutnym"," (z ",[46,386,387],{},"https:\u002F\u002F…",") — część crawler-ów odrzuca względne ścieżki. Na stronie pilnuje tego helper ",[46,390,391],{},"absoluteUrl"," z ",[46,394,395],{},"app\u002Futils\u002Fseo.ts",[16,397,398,401],{},[26,399,400],{},"Narzędziami."," Wygodniej sprawdzić podgląd „oczami platformy”. Używam głównie:",[403,404,405,416,430],"ul",{},[406,407,408,415],"li",{},[26,409,410],{},[20,411,414],{"href":412,"rel":413},"https:\u002F\u002Fwww.opengraph.xyz",[97],"opengraph.xyz"," — pokazuje podgląd dla wielu serwisów naraz i wypisuje ostrzeżenia (np. zbyt krótki tytuł, zbyt długi opis, brak obrazu).",[406,417,418,425,426,429],{},[26,419,420],{},[20,421,424],{"href":422,"rel":423},"https:\u002F\u002Fwww.linkedin.com\u002Fpost-inspector\u002F",[97],"LinkedIn Post Inspector"," — kluczowy, bo publikuję głównie na LinkedIn; dodatkowo ",[26,427,428],{},"odświeża cache"," podglądu (platformy buforują OG i bez tego widzą starą wersję).",[406,431,432,439,440,443],{},[26,433,434],{},[20,435,438],{"href":436,"rel":437},"https:\u002F\u002Fdevelopers.facebook.com\u002Ftools\u002Fdebug\u002F",[97],"Meta Sharing Debugger"," i podgląd kart ",[26,441,442],{},"X"," — gdy chcę zweryfikować konkretną platformę.",[16,445,446,447,449,450,452,453,455,456,459,460,41],{},"To właśnie ostrzeżenia z ",[26,448,414],{}," (zbyt krótki ",[46,451,151],{},", zbyt długi ",[46,454,184],{},", brak ",[271,457,458],{},"call-to-action"," na obrazie) pchnęły mnie wcześniej do dopracowania metadanych podstron — ale to temat na osobną historię. Tutaj skupiam się na samych ",[26,461,462],{},"obrazach",[11,464,466],{"id":465},"pomysł-skill-dostaje-pomysł-skrypt-pilnuje-rygoru","Pomysł: SKILL dostaje pomysł, skrypt pilnuje rygoru",[16,468,469,470,473],{},"Generowanie obrazu przez model AI ma jedną zaletę i jedną wadę. Zaleta: modele potrafią narysować ładne, klimatyczne tło w dowolnym stylu. Wada: są ",[26,471,472],{},"niedeterministyczne"," — poproś dwa razy o to samo, dostaniesz dwa różne kadry, inne wymiary, a jeśli każesz dopisać tekst, to z dużym prawdopodobieństwem przekręci polskie znaki albo „wymyśli” litery.",[16,475,476,477,480],{},"Dlatego oba SKILL-e zbudowałem na ",[26,478,479],{},"wyraźnym podziale ról",":",[403,482,483,493],{},[406,484,485,488,489,492],{},[26,486,487],{},"Część kreatywna"," (tło, klimat, motyw) → ",[26,490,491],{},"model AI",". To jedyne, w czym model jest naprawdę dobry, a niedeterminizm tu nie przeszkadza.",[406,494,495,498,499,502],{},[26,496,497],{},"Część deterministyczna"," (dokładne wymiary, napisy, czcionka, logo, nazwa pliku) → ",[26,500,501],{},"skrypt pomocniczy"," w Node.js. Tu liczy się powtarzalność co do piksela.",[16,504,505,506,509,510,513,514,517,518,521],{},"W praktyce wygląda to tak, że SKILL (czyli ",[26,507,508],{},"Claude",") odpowiada tylko za ",[26,511,512],{},"brief"," — krótki opis sceny pasującej do treści — a całą resztę egzekwuje skrypt ",[46,515,516],{},".mjs",". Skrypt jest ",[26,519,520],{},"źródłem prawdy",": ma w sobie zaszyty stały prompt stylu, stałą czcionkę i stały rozmiar, więc agent nie ma jak „popłynąć”.",[11,523,525,526],{"id":524},"skill-1-grafiki-artykułów-generate-article-picture","SKILL #1: grafiki artykułów — ",[46,527,79],{},[16,529,530],{},"Pierwszy SKILL zlecam Claude'owi tak samo, jak wszystkie poprzednie — opisuję, czego chcę, a on przygotowuje gotowego SKILL-a wraz ze skryptem. Mój prompt brzmiał:",[117,532,536],{"className":533,"code":534,"language":535,"meta":122,"style":122},"language-markdown shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","Przygotuj SKILL o nazwie **generate-article-picture** wraz ze skryptem pomocniczym:\n\n- SKILL przyjmuje jeden parametr, który jest _slug-iem_ artykułu, którego plik MD znajduje się w folderze `.\u002Fcontent\u002Fblog\u002F`.\n- Rolą jest przygotowanie obrazu o zadanym stylu i pasującego do treści artykułu.\n- Czyta artykuł i przygotowuje streszczenie (opis czego ma dotyczyć obraz) dla modelu AI do generowania obrazów.\n- Obraz musi być DOKŁADNIE o wymiarach **1200 x 630 pikseli**.\n- Obraz musi być w stylu nowoczesnym, nawet futurystycznym, związany z programowaniem i technologiami AI.\n- Obraz musi być w ciemniejszych kolorach.\n- Jedyne napisy na obrazie to:\n  - Numer artykułu w formie **#N**, gdzie `N` to wartość z pola `order` metadanych, kolor jasno szary, półprzeźroczysty, prawy dolny róg obrazu (dla 4 to #4).\n  - Tytuł artykułu w formie, czyli wartość z pola `title` metadanych, kolor ciemniejszy szary, półprzeźroczysty, prawy dolny róg nad **#N**.\n  - Zawsze ta sama czcionka dla wszystkich obrazów generowanych dla różnych artykułów.\n- Prompt z wszystkimi wytycznymi dla modelu AI wysyłany jest przez portal _OpenRouter_:\n  - Domyślny model: `google\u002Fgemini-3.1-flash-image-preview`.\n  - Model i klucz API konfigurowane w pliku `.env`.\n- Wygenerowany obraz trafia do folderu `.\u002Fpublic\u002Fpics\u002F`, do pliku `NNNN.png`, gdzie `NNNN` to numer z pola `order` artykułu z zerami wiodącymi (dla 4 to 0004.png).\n","markdown",[46,537,538,556,562,594,601,609,626,634,642,650,689,715,723,741,758,775],{"__ignoreMap":122},[126,539,540,544,548,551,553],{"class":128,"line":129},[126,541,543],{"class":542},"sTEyZ","Przygotuj SKILL o nazwie ",[126,545,547],{"class":546},"sHepR","**",[126,549,79],{"class":550},"so75L",[126,552,547],{"class":546},[126,554,555],{"class":542}," wraz ze skryptem pomocniczym:\n",[126,557,558],{"class":128,"line":171},[126,559,561],{"emptyLinePlaceholder":560},true,"\n",[126,563,564,567,570,574,578,580,583,586,589,591],{"class":128,"line":202},[126,565,566],{"class":132},"-",[126,568,569],{"class":542}," SKILL przyjmuje jeden parametr, który jest ",[126,571,573],{"class":572},"s7zQu","_",[126,575,577],{"class":576},"s5tWE","slug-iem",[126,579,573],{"class":572},[126,581,582],{"class":542}," artykułu, którego plik MD znajduje się w folderze ",[126,584,585],{"class":132},"`",[126,587,588],{"class":150},".\u002Fcontent\u002Fblog\u002F",[126,590,585],{"class":132},[126,592,593],{"class":542},".\n",[126,595,596,598],{"class":128,"line":233},[126,597,566],{"class":132},[126,599,600],{"class":542}," Rolą jest przygotowanie obrazu o zadanym stylu i pasującego do treści artykułu.\n",[126,602,604,606],{"class":128,"line":603},5,[126,605,566],{"class":132},[126,607,608],{"class":542}," Czyta artykuł i przygotowuje streszczenie (opis czego ma dotyczyć obraz) dla modelu AI do generowania obrazów.\n",[126,610,612,614,617,619,622,624],{"class":128,"line":611},6,[126,613,566],{"class":132},[126,615,616],{"class":542}," Obraz musi być DOKŁADNIE o wymiarach ",[126,618,547],{"class":546},[126,620,621],{"class":550},"1200 x 630 pikseli",[126,623,547],{"class":546},[126,625,593],{"class":542},[126,627,629,631],{"class":128,"line":628},7,[126,630,566],{"class":132},[126,632,633],{"class":542}," Obraz musi być w stylu nowoczesnym, nawet futurystycznym, związany z programowaniem i technologiami AI.\n",[126,635,637,639],{"class":128,"line":636},8,[126,638,566],{"class":132},[126,640,641],{"class":542}," Obraz musi być w ciemniejszych kolorach.\n",[126,643,645,647],{"class":128,"line":644},9,[126,646,566],{"class":132},[126,648,649],{"class":542}," Jedyne napisy na obrazie to:\n",[126,651,653,656,659,661,664,666,669,671,674,676,679,681,684,686],{"class":128,"line":652},10,[126,654,655],{"class":132},"  -",[126,657,658],{"class":542}," Numer artykułu w formie ",[126,660,547],{"class":546},[126,662,663],{"class":550},"#N",[126,665,547],{"class":546},[126,667,668],{"class":542},", gdzie ",[126,670,585],{"class":132},[126,672,673],{"class":150},"N",[126,675,585],{"class":132},[126,677,678],{"class":542}," to wartość z pola ",[126,680,585],{"class":132},[126,682,683],{"class":150},"order",[126,685,585],{"class":132},[126,687,688],{"class":542}," metadanych, kolor jasno szary, półprzeźroczysty, prawy dolny róg obrazu (dla 4 to #4).\n",[126,690,692,694,697,699,702,704,707,709,711,713],{"class":128,"line":691},11,[126,693,655],{"class":132},[126,695,696],{"class":542}," Tytuł artykułu w formie, czyli wartość z pola ",[126,698,585],{"class":132},[126,700,701],{"class":150},"title",[126,703,585],{"class":132},[126,705,706],{"class":542}," metadanych, kolor ciemniejszy szary, półprzeźroczysty, prawy dolny róg nad ",[126,708,547],{"class":546},[126,710,663],{"class":550},[126,712,547],{"class":546},[126,714,593],{"class":542},[126,716,718,720],{"class":128,"line":717},12,[126,719,655],{"class":132},[126,721,722],{"class":542}," Zawsze ta sama czcionka dla wszystkich obrazów generowanych dla różnych artykułów.\n",[126,724,726,728,731,733,736,738],{"class":128,"line":725},13,[126,727,566],{"class":132},[126,729,730],{"class":542}," Prompt z wszystkimi wytycznymi dla modelu AI wysyłany jest przez portal ",[126,732,573],{"class":572},[126,734,735],{"class":576},"OpenRouter",[126,737,573],{"class":572},[126,739,740],{"class":542},":\n",[126,742,744,746,749,751,754,756],{"class":128,"line":743},14,[126,745,655],{"class":132},[126,747,748],{"class":542}," Domyślny model: ",[126,750,585],{"class":132},[126,752,753],{"class":150},"google\u002Fgemini-3.1-flash-image-preview",[126,755,585],{"class":132},[126,757,593],{"class":542},[126,759,761,763,766,768,771,773],{"class":128,"line":760},15,[126,762,655],{"class":132},[126,764,765],{"class":542}," Model i klucz API konfigurowane w pliku ",[126,767,585],{"class":132},[126,769,770],{"class":150},".env",[126,772,585],{"class":132},[126,774,593],{"class":542},[126,776,778,780,783,785,788,790,793,795,798,800,802,804,807,809,812,814,816,818],{"class":128,"line":777},16,[126,779,566],{"class":132},[126,781,782],{"class":542}," Wygenerowany obraz trafia do folderu ",[126,784,585],{"class":132},[126,786,787],{"class":150},".\u002Fpublic\u002Fpics\u002F",[126,789,585],{"class":132},[126,791,792],{"class":542},", do pliku ",[126,794,585],{"class":132},[126,796,797],{"class":150},"NNNN.png",[126,799,585],{"class":132},[126,801,668],{"class":542},[126,803,585],{"class":132},[126,805,806],{"class":150},"NNNN",[126,808,585],{"class":132},[126,810,811],{"class":542}," to numer z pola ",[126,813,585],{"class":132},[126,815,683],{"class":150},[126,817,585],{"class":132},[126,819,820],{"class":542}," artykułu z zerami wiodącymi (dla 4 to 0004.png).\n",[16,822,823,824,827],{},"Pierwsze efekty były technicznie poprawne, ale zbyt ",[26,825,826],{},"ponure"," — same szarości. Wystarczyła jedna korekta promptu, żeby ożywić paletę:",[117,829,831],{"className":533,"code":830,"language":535,"meta":122,"style":122},"Generowane obrazy są dość ponure, mają być w ciemniejszych barwach,\nale niekoniecznie tylko odcienie szarości. Mogą być bardziej pozytywne.\n",[46,832,833,838],{"__ignoreMap":122},[126,834,835],{"class":128,"line":129},[126,836,837],{"class":542},"Generowane obrazy są dość ponure, mają być w ciemniejszych barwach,\n",[126,839,840],{"class":128,"line":171},[126,841,842],{"class":542},"ale niekoniecznie tylko odcienie szarości. Mogą być bardziej pozytywne.\n",[16,844,845,846,849],{},"Od tej pory bazą jest głęboka ciemność (granat, grafit, czerń), ale ",[26,847,848],{},"rozświetlona żywymi akcentami"," — turkus, zieleń, magenta, bursztyn, coral. Ciemno, ale nie ponuro.",[851,852,854],"h3",{"id":853},"rola-ai-i-sekret-spójnego-stylu","Rola AI i sekret spójnego stylu",[16,856,857,858,861,862,865,866,869,870,480],{},"Mogłoby się wydawać, że spójność stylu zależy od modelu. Nie zależy. ",[26,859,860],{},"Spójność bierze się ze stałego promptu zaszytego w skrypcie",", a nie z briefu, który zmienia się przy każdym artykule. W skrypcie znajduje się sekcja ",[46,863,864],{},"STYLE"," (ten sam klimat dla całej serii) i ",[46,867,868],{},"STRICT CONSTRAINTS",", a brief od Claude'a dokleja się tylko jako sekcja ",[46,871,872],{},"SCENE",[117,874,879],{"className":875,"code":877,"language":878,"meta":122},[876],"language-text","STYLE (identical mood for every article in this series):\n- Modern, even futuristic; theme: software development and AI technologies.\n- Dark but NOT gloomy … vibrant, optimistic accent colors …\n\nSTRICT CONSTRAINTS:\n- Do NOT render ANY text, letters, numbers, words, captions, logos …\n- Keep the LOWER-RIGHT quadrant calmer and darker … so caption text stays legible.\n\nSCENE (what this specific article is about):\n\u003Cbrief od Claude'a, np. „a pipeline turning AI-generated artwork into branded social cards\">\n","text",[46,880,877],{"__ignoreMap":122},[16,882,883,884,886,887,890,891,894],{},"Zwróć uwagę na dwie rzeczy w ",[46,885,868],{},". Po pierwsze: model ma ",[26,888,889],{},"kategoryczny zakaz rysowania jakiegokolwiek tekstu"," — bo napisy nałoży skrypt. Po drugie: proszę model, żeby ",[26,892,893],{},"prawy dolny róg był ciemniejszy i spokojniejszy"," — bo właśnie tam ląduje tytuł i numer artykułu, które muszą pozostać czytelne.",[16,896,897,898,901],{},"Rolą Claude'a jest więc tylko jedno: przeczytać artykuł i napisać ",[26,899,900],{},"2–4 zdania po angielsku"," opisujące scenę. Cała tożsamość wizualna serii jest poza modelem.",[851,903,905],{"id":904},"co-dzieje-się-po-wygenerowaniu-obrazu","Co dzieje się po wygenerowaniu obrazu",[16,907,908,909,912,913,480],{},"Surowy obraz z modelu to dopiero półprodukt. Reszta to ",[26,910,911],{},"deterministyczny post-processing"," w Node.js przy użyciu biblioteki ",[26,914,915],{},[20,916,919],{"href":917,"rel":918},"https:\u002F\u002Fsharp.pixelplumbing.com\u002F",[97],"sharp",[921,922,923,940],"ol",{},[406,924,925,928,929,931,932,935,936,939],{},[26,926,927],{},"Przycięcie do dokładnych wymiarów."," Model bywa kapryśny co do proporcji, więc ",[46,930,919],{}," skaluje wynik do ",[26,933,934],{},"1200 × 630"," przez ",[46,937,938],{},"resize(WIDTH, HEIGHT, { fit: 'cover' })",". To gwarantuje, że każdy obraz w serii ma identyczny rozmiar.",[406,941,942,945,946,948,949,952,953,956],{},[26,943,944],{},"Nałożenie napisów."," Tytuł i numer ",[46,947,663],{}," renderuję jako ",[26,950,951],{},"nakładkę SVG"," złożoną na obraz przez ",[46,954,955],{},"sharp.composite()",". Tekst w SVG jest „twardy” — wpisany wprost w XML, więc nie ma mowy o przekręceniu liter.",[16,958,959],{},"Dlaczego napisy renderuje skrypt, a nie AI? Trzy powody, wszystkie praktyczne:",[403,961,962,972,981],{},[406,963,964,967,968,971],{},[26,965,966],{},"Poprawne polskie diakrytyki."," Skrypt używa stałej czcionki ",[26,969,970],{},"DejaVu Sans",", która ma pełny komplet polskich znaków. Model AI regularnie gubi „ł”, „ż” czy „ą” albo renderuje je jako artefakty.",[406,973,974,977,978,980],{},[26,975,976],{},"Powtarzalność co do piksela."," Tytuł zawsze tym samym fontem, w tym samym kolorze (ciemniejszy szary, półprzeźroczysty), w prawym dolnym rogu, z numerem ",[46,979,663],{}," pod spodem. Żadnych wahań między artykułami.",[406,982,983,986,987,990,991,994,995,49,997,999],{},[26,984,985],{},"Determinizm nazwy i treści."," Nazwa pliku (",[46,988,989],{},"0005.png",") i treść napisów pochodzą wprost z ",[26,992,993],{},"metadanych"," artykułu (",[46,996,701],{},[46,998,683],{},") — nie ma tu miejsca na kreatywność modelu.",[16,1001,1002],{},"W kodzie wygląda to mniej więcej tak (uproszczony fragment skryptu):",[117,1004,1008],{"className":1005,"code":1006,"language":1007,"meta":122,"style":122},"language-js shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","const background = await sharp(rawImage)\n  .resize(1200, 630, { fit: 'cover', position: 'centre' })\n  .toBuffer()\n\nconst finalPng = await sharp(background)\n  .composite([{ input: buildOverlaySvg(title, order), top: 0, left: 0 }])\n  .png()\n  .toBuffer()\n","js",[46,1009,1010,1030,1089,1099,1103,1119,1172,1181],{"__ignoreMap":122},[126,1011,1012,1015,1018,1020,1023,1027],{"class":128,"line":129},[126,1013,1014],{"class":140},"const",[126,1016,1017],{"class":542}," background ",[126,1019,144],{"class":132},[126,1021,1022],{"class":572}," await",[126,1024,1026],{"class":1025},"s2Zo4"," sharp",[126,1028,1029],{"class":542},"(rawImage)\n",[126,1031,1032,1035,1038,1041,1045,1048,1051,1053,1056,1059,1061,1063,1066,1069,1071,1074,1076,1078,1081,1083,1086],{"class":128,"line":171},[126,1033,1034],{"class":132},"  .",[126,1036,1037],{"class":1025},"resize",[126,1039,1040],{"class":542},"(",[126,1042,1044],{"class":1043},"sbssI","1200",[126,1046,1047],{"class":132},",",[126,1049,1050],{"class":1043}," 630",[126,1052,1047],{"class":132},[126,1054,1055],{"class":132}," {",[126,1057,1058],{"class":136}," fit",[126,1060,480],{"class":132},[126,1062,368],{"class":132},[126,1064,1065],{"class":150},"cover",[126,1067,1068],{"class":132},"'",[126,1070,1047],{"class":132},[126,1072,1073],{"class":136}," position",[126,1075,480],{"class":132},[126,1077,368],{"class":132},[126,1079,1080],{"class":150},"centre",[126,1082,1068],{"class":132},[126,1084,1085],{"class":132}," }",[126,1087,1088],{"class":542},")\n",[126,1090,1091,1093,1096],{"class":128,"line":202},[126,1092,1034],{"class":132},[126,1094,1095],{"class":1025},"toBuffer",[126,1097,1098],{"class":542},"()\n",[126,1100,1101],{"class":128,"line":233},[126,1102,561],{"emptyLinePlaceholder":560},[126,1104,1105,1107,1110,1112,1114,1116],{"class":128,"line":603},[126,1106,1014],{"class":140},[126,1108,1109],{"class":542}," finalPng ",[126,1111,144],{"class":132},[126,1113,1022],{"class":572},[126,1115,1026],{"class":1025},[126,1117,1118],{"class":542},"(background)\n",[126,1120,1121,1123,1126,1129,1132,1135,1137,1140,1143,1145,1148,1150,1153,1155,1158,1160,1163,1165,1167,1169],{"class":128,"line":611},[126,1122,1034],{"class":132},[126,1124,1125],{"class":1025},"composite",[126,1127,1128],{"class":542},"([",[126,1130,1131],{"class":132},"{",[126,1133,1134],{"class":136}," input",[126,1136,480],{"class":132},[126,1138,1139],{"class":1025}," buildOverlaySvg",[126,1141,1142],{"class":542},"(title",[126,1144,1047],{"class":132},[126,1146,1147],{"class":542}," order)",[126,1149,1047],{"class":132},[126,1151,1152],{"class":136}," top",[126,1154,480],{"class":132},[126,1156,1157],{"class":1043}," 0",[126,1159,1047],{"class":132},[126,1161,1162],{"class":136}," left",[126,1164,480],{"class":132},[126,1166,1157],{"class":1043},[126,1168,1085],{"class":132},[126,1170,1171],{"class":542},"])\n",[126,1173,1174,1176,1179],{"class":128,"line":628},[126,1175,1034],{"class":132},[126,1177,1178],{"class":1025},"png",[126,1180,1098],{"class":542},[126,1182,1183,1185,1187],{"class":128,"line":636},[126,1184,1034],{"class":132},[126,1186,1095],{"class":1025},[126,1188,1098],{"class":542},[16,1190,1191,1192,1198,1199,1201,1202,1205,1206,1208,1209,1211,1212,1215],{},"Sam obraz powstaje przez ",[26,1193,1194],{},[20,1195,735],{"href":1196,"rel":1197},"https:\u002F\u002Fopenrouter.ai\u002F",[97]," (domyślny model ",[46,1200,753],{}," - ",[271,1203,1204],{},"Nano Banana 2","), a klucz API i model siedzą wyłącznie w pliku ",[46,1207,770],{}," — nigdy w repozytorium. Koszt jest niewielki: jedno wygenerowanie obrazu domyślnym modelem ",[271,1210,1204],{}," to około ",[26,1213,1214],{},"$0.07",", więc nawet kilka podejść do briefu mieści się w rozsądnych kwotach.",[11,1217,1219,1220],{"id":1218},"skill-2-obrazy-og-podstron-generate-page-og-picture","SKILL #2: obrazy OG podstron — ",[46,1221,84],{},[16,1223,1224,1225,1228,1229,1232,1233,1236,1237,1240],{},"Drugi SKILL działa na tej samej zasadzie, ale dla ",[26,1226,1227],{},"podstron",", nie artykułów. Zlecając go, dorzuciłem ważną decyzję projektową — chciałem ",[26,1230,1231],{},"wyłączyć generowanie OG w czasie build-u"," (moduł ",[46,1234,1235],{},"nuxt-og-image"," dawał niezadowalające efekty) i zastąpić je ",[26,1238,1239],{},"statycznymi obrazami"," tworzonymi raz, w czasie development-u. Fragment promptu:",[117,1242,1244],{"className":533,"code":1243,"language":535,"meta":122,"style":122},"1. Całkowicie wyłącz aktualny mechanizm generowania grafik OG w czasie build.\n   Chcę mieć statyczne obrazy generowane w czasie development-u.\n\n2. Przygotuj SKILL o **nazwie generate-page-og-picture** wraz ze skryptem:\n\n- Analogicznie do SKILL-a **generate-article-picture**.\n- SKILL przyjmuje dwa parametry:\n  - URL względny strony, np.: `\u002Fcontact`.\n  - Główny tekst grafiki OG.\n- Rolą jest przygotowanie obrazu typu _OG_ o zadanym stylu.\n- Obraz musi być DOKŁADNIE o wymiarach **1200 x 630 pikseli**.\n- Obraz musi mieć tło w kolorze tła strony w trybie ciemnym.\n- Grafika obrazu to wygenerowane przez AI delikatne kształty geometryczne w stylu nowoczesnym, nawet futurystycznym.\n- W lewym górny rogu grafiki musi być logo strony + napis AWFS.dev - identycznie jak jest z lewej strony górnej belki strony.\n- Na środku tekst podany jak drugi argument SKILL-a. Jeśli użyto znaku \\n to rozbij tekst na linie.\n- Zawsze ta sama czcionka dla wszystkich obrazów generowanych tym SKILL-em.\n- Prompt z wszystkimi wytycznymi dla modelu AI wysyłany jest przez portal _OpenRouter_:\n  - Domyślny model: `google\u002Fgemini-3.1-flash-image-preview` (Nano Banana 2).\n  - Model i klucz API konfigurowane w pliku `.env`.\n- Wygenerowany obraz trafia do folderu `.\u002Fpublic\u002Fog\u002F`, do pliku o nazwie strony, np.: `contact.png` dla strony `\u002Fcontact` (do nazwy bierz ostatni człon URL, czyli `test2.png` dla strony `\u002Ftest1\u002Ftest2`).\n- Zaktualizuj meta dane strony tak, żeby `og:image` wskazywał na nowo utworzony obraz.\n",[46,1245,1246,1254,1259,1263,1281,1285,1300,1307,1322,1329,1345,1359,1366,1373,1380,1387,1394,1409,1425,1440,1495],{"__ignoreMap":122},[126,1247,1248,1251],{"class":128,"line":129},[126,1249,1250],{"class":132},"1.",[126,1252,1253],{"class":542}," Całkowicie wyłącz aktualny mechanizm generowania grafik OG w czasie build.\n",[126,1255,1256],{"class":128,"line":171},[126,1257,1258],{"class":542},"   Chcę mieć statyczne obrazy generowane w czasie development-u.\n",[126,1260,1261],{"class":128,"line":202},[126,1262,561],{"emptyLinePlaceholder":560},[126,1264,1265,1268,1271,1273,1276,1278],{"class":128,"line":233},[126,1266,1267],{"class":132},"2.",[126,1269,1270],{"class":542}," Przygotuj SKILL o ",[126,1272,547],{"class":546},[126,1274,1275],{"class":550},"nazwie generate-page-og-picture",[126,1277,547],{"class":546},[126,1279,1280],{"class":542}," wraz ze skryptem:\n",[126,1282,1283],{"class":128,"line":603},[126,1284,561],{"emptyLinePlaceholder":560},[126,1286,1287,1289,1292,1294,1296,1298],{"class":128,"line":611},[126,1288,566],{"class":132},[126,1290,1291],{"class":542}," Analogicznie do SKILL-a ",[126,1293,547],{"class":546},[126,1295,79],{"class":550},[126,1297,547],{"class":546},[126,1299,593],{"class":542},[126,1301,1302,1304],{"class":128,"line":628},[126,1303,566],{"class":132},[126,1305,1306],{"class":542}," SKILL przyjmuje dwa parametry:\n",[126,1308,1309,1311,1314,1316,1318,1320],{"class":128,"line":636},[126,1310,655],{"class":132},[126,1312,1313],{"class":542}," URL względny strony, np.: ",[126,1315,585],{"class":132},[126,1317,55],{"class":150},[126,1319,585],{"class":132},[126,1321,593],{"class":542},[126,1323,1324,1326],{"class":128,"line":644},[126,1325,655],{"class":132},[126,1327,1328],{"class":542}," Główny tekst grafiki OG.\n",[126,1330,1331,1333,1336,1338,1340,1342],{"class":128,"line":652},[126,1332,566],{"class":132},[126,1334,1335],{"class":542}," Rolą jest przygotowanie obrazu typu ",[126,1337,573],{"class":572},[126,1339,102],{"class":576},[126,1341,573],{"class":572},[126,1343,1344],{"class":542}," o zadanym stylu.\n",[126,1346,1347,1349,1351,1353,1355,1357],{"class":128,"line":691},[126,1348,566],{"class":132},[126,1350,616],{"class":542},[126,1352,547],{"class":546},[126,1354,621],{"class":550},[126,1356,547],{"class":546},[126,1358,593],{"class":542},[126,1360,1361,1363],{"class":128,"line":717},[126,1362,566],{"class":132},[126,1364,1365],{"class":542}," Obraz musi mieć tło w kolorze tła strony w trybie ciemnym.\n",[126,1367,1368,1370],{"class":128,"line":725},[126,1369,566],{"class":132},[126,1371,1372],{"class":542}," Grafika obrazu to wygenerowane przez AI delikatne kształty geometryczne w stylu nowoczesnym, nawet futurystycznym.\n",[126,1374,1375,1377],{"class":128,"line":743},[126,1376,566],{"class":132},[126,1378,1379],{"class":542}," W lewym górny rogu grafiki musi być logo strony + napis AWFS.dev - identycznie jak jest z lewej strony górnej belki strony.\n",[126,1381,1382,1384],{"class":128,"line":760},[126,1383,566],{"class":132},[126,1385,1386],{"class":542}," Na środku tekst podany jak drugi argument SKILL-a. Jeśli użyto znaku \\n to rozbij tekst na linie.\n",[126,1388,1389,1391],{"class":128,"line":777},[126,1390,566],{"class":132},[126,1392,1393],{"class":542}," Zawsze ta sama czcionka dla wszystkich obrazów generowanych tym SKILL-em.\n",[126,1395,1397,1399,1401,1403,1405,1407],{"class":128,"line":1396},17,[126,1398,566],{"class":132},[126,1400,730],{"class":542},[126,1402,573],{"class":572},[126,1404,735],{"class":576},[126,1406,573],{"class":572},[126,1408,740],{"class":542},[126,1410,1412,1414,1416,1418,1420,1422],{"class":128,"line":1411},18,[126,1413,655],{"class":132},[126,1415,748],{"class":542},[126,1417,585],{"class":132},[126,1419,753],{"class":150},[126,1421,585],{"class":132},[126,1423,1424],{"class":542}," (Nano Banana 2).\n",[126,1426,1428,1430,1432,1434,1436,1438],{"class":128,"line":1427},19,[126,1429,655],{"class":132},[126,1431,765],{"class":542},[126,1433,585],{"class":132},[126,1435,770],{"class":150},[126,1437,585],{"class":132},[126,1439,593],{"class":542},[126,1441,1443,1445,1447,1449,1452,1454,1457,1459,1462,1464,1467,1469,1471,1473,1476,1478,1481,1483,1485,1487,1490,1492],{"class":128,"line":1442},20,[126,1444,566],{"class":132},[126,1446,782],{"class":542},[126,1448,585],{"class":132},[126,1450,1451],{"class":150},".\u002Fpublic\u002Fog\u002F",[126,1453,585],{"class":132},[126,1455,1456],{"class":542},", do pliku o nazwie strony, np.: ",[126,1458,585],{"class":132},[126,1460,1461],{"class":150},"contact.png",[126,1463,585],{"class":132},[126,1465,1466],{"class":542}," dla strony ",[126,1468,585],{"class":132},[126,1470,55],{"class":150},[126,1472,585],{"class":132},[126,1474,1475],{"class":542}," (do nazwy bierz ostatni człon URL, czyli ",[126,1477,585],{"class":132},[126,1479,1480],{"class":150},"test2.png",[126,1482,585],{"class":132},[126,1484,1466],{"class":542},[126,1486,585],{"class":132},[126,1488,1489],{"class":150},"\u002Ftest1\u002Ftest2",[126,1491,585],{"class":132},[126,1493,1494],{"class":542},").\n",[126,1496,1498,1500,1503,1505,1507,1509],{"class":128,"line":1497},21,[126,1499,566],{"class":132},[126,1501,1502],{"class":542}," Zaktualizuj meta dane strony tak, żeby ",[126,1504,585],{"class":132},[126,1506,215],{"class":150},[126,1508,585],{"class":132},[126,1510,1511],{"class":542}," wskazywał na nowo utworzony obraz.\n",[16,1513,1514,1515,289,1518,1521,1522,1525,1526,1529],{},"Różnice względem grafik artykułów wynikają z innego celu. Tu tłem jest ",[26,1516,1517],{},"dokładny kolor strony w trybie ciemnym",[46,1519,1520],{},"#111827","), a model AI dorysowuje na nim tylko ",[26,1523,1524],{},"delikatne, geometryczne kształty"," (cienkie linie, wireframe, subtelne poświaty) — stonowane, bo nad nimi ląduje tekst i marka. Krycie tej warstwy AI reguluję opcją ",[46,1527,1528],{},"--opacity"," (domyślnie 0.7), więc kształty nigdy nie przebijają treści.",[16,1531,1532,1533,1535],{},"Tak wygląda gotowy efekt — obraz OG strony ",[46,1534,55],{}," wygenerowany tym SKILL-em: ciemne tło strony, subtelna geometryczna siatka od AI, logo „AWFS.dev” w lewym górnym rogu i wycentrowany tekst nałożony deterministycznie przez skrypt:",[16,1537,1538],{},[1539,1540],"img",{"alt":1541,"src":1542},"Obraz OG strony \u002Fcontact: ciemne tło z delikatną geometryczną siatką, logo AWFS.dev w lewym górnym rogu i wycentrowany napis „Kontakt”","\u002Fog\u002Fcontact.png",[16,1544,1545,1546,1549],{},"Najważniejszy element to ",[26,1547,1548],{},"logo AWFS.dev w lewym górnym rogu"," — odwzorowanie górnej belki strony. I tu pojawia się ciekawy szczegół architektoniczny.",[851,1551,1553],{"id":1552},"jedno-źródło-prawdy-dla-logo","Jedno źródło prawdy dla logo",[16,1555,1556,1557,1560,1561,1566,1567,1570,1571,1574,1575,1578,1579,1584,1585,1587],{},"Logo musi wyglądać ",[26,1558,1559],{},"identycznie"," na każdym obrazie OG. Żeby to wymusić, wydzieliłem je do współdzielonego modułu ",[26,1562,1563],{},[46,1564,1565],{},"og-logo.mjs"," — jedynego źródła prawdy dla marki. Moduł odwzorowuje komponent ",[46,1568,1569],{},"AppLogo.vue"," co do koloru: box ",[46,1572,1573],{},"gray-800",", „AW”\u002F„dev” szare, „FS” błękitne (",[46,1576,1577],{},"sky","), kropka żółta. Sama ikona to natywny ",[26,1580,1581],{},[46,1582,1583],{},"public\u002Ficon_32x32.png",", dokładany osobno przez ",[46,1586,919],{}," (a nie rysowany w SVG), żeby zachować ostrość:",[117,1589,1591],{"className":1005,"code":1590,"language":1007,"meta":122,"style":122},"return [\n  { input: buildLogoSvg(), top: 0, left: 0 }, \u002F\u002F box + napis \"AWFS.dev\"\n  { input: icon, top: MARGIN + ICON_PAD, left: MARGIN + ICON_PAD } \u002F\u002F ikona PNG\n]\n",[46,1592,1593,1601,1639,1684],{"__ignoreMap":122},[126,1594,1595,1598],{"class":128,"line":129},[126,1596,1597],{"class":572},"return",[126,1599,1600],{"class":542}," [\n",[126,1602,1603,1606,1608,1610,1613,1616,1618,1620,1622,1624,1626,1628,1630,1632,1635],{"class":128,"line":171},[126,1604,1605],{"class":132},"  {",[126,1607,1134],{"class":136},[126,1609,480],{"class":132},[126,1611,1612],{"class":1025}," buildLogoSvg",[126,1614,1615],{"class":542},"()",[126,1617,1047],{"class":132},[126,1619,1152],{"class":136},[126,1621,480],{"class":132},[126,1623,1157],{"class":1043},[126,1625,1047],{"class":132},[126,1627,1162],{"class":136},[126,1629,480],{"class":132},[126,1631,1157],{"class":1043},[126,1633,1634],{"class":132}," },",[126,1636,1638],{"class":1637},"sHwdD"," \u002F\u002F box + napis \"AWFS.dev\"\n",[126,1640,1641,1643,1645,1647,1650,1652,1654,1656,1659,1662,1665,1667,1669,1671,1673,1675,1678,1681],{"class":128,"line":202},[126,1642,1605],{"class":132},[126,1644,1134],{"class":136},[126,1646,480],{"class":132},[126,1648,1649],{"class":542}," icon",[126,1651,1047],{"class":132},[126,1653,1152],{"class":136},[126,1655,480],{"class":132},[126,1657,1658],{"class":542}," MARGIN ",[126,1660,1661],{"class":132},"+",[126,1663,1664],{"class":542}," ICON_PAD",[126,1666,1047],{"class":132},[126,1668,1162],{"class":136},[126,1670,480],{"class":132},[126,1672,1658],{"class":542},[126,1674,1661],{"class":132},[126,1676,1677],{"class":542}," ICON_PAD ",[126,1679,1680],{"class":132},"}",[126,1682,1683],{"class":1637}," \u002F\u002F ikona PNG\n",[126,1685,1686],{"class":128,"line":233},[126,1687,1688],{"class":542},"]\n",[16,1690,1691,1692,1694,1695,41],{},"Dzięki temu, że ",[46,1693,1565],{}," jest importowany w kilku miejscach, ",[26,1696,1697],{},"zmiana wyglądu logo w jednym pliku propaguje się wszędzie",[851,1699,1701],{"id":1700},"grafiki-artykułów-też-stają-się-obrazami-og","Grafiki artykułów też stają się obrazami OG",[16,1703,1704,1705,1708,1709,41],{},"Zostało jeszcze jedno: artykuły bloga jako linki też powinny ładnie wyglądać w podglądzie — i to z ",[26,1706,1707],{},"brandingiem AWFS",". Nie chciałem jednak generować dla nich osobnych obrazów. Rozwiązanie: wziąć istniejącą grafikę nagłówkową artykułu i ",[26,1710,1711],{},"doklejać do niej tylko logo",[16,1713,1714,1715,1720,1721,1723,1724,1727],{},"Służy do tego trzeci skrypt — ",[26,1716,1717],{},[46,1718,1719],{},"stamp-og-logo.mjs"," — który korzysta z tego samego ",[46,1722,1565],{},", ale nakłada ",[26,1725,1726],{},"samo logo"," (bez AI i bez tekstu środkowego) na gotowy obraz, skalując go do 1200 × 630:",[117,1729,1731],{"className":340,"code":1730,"language":342,"meta":122,"style":122},"node .claude\u002Fskills\u002Fgenerate-page-og-picture\u002Fstamp-og-logo.mjs public\u002Fog\u002F0005.png\n",[46,1732,1733],{"__ignoreMap":122},[126,1734,1735,1738,1741],{"class":128,"line":129},[126,1736,1737],{"class":349},"node",[126,1739,1740],{"class":150}," .claude\u002Fskills\u002Fgenerate-page-og-picture\u002Fstamp-og-logo.mjs",[126,1742,1743],{"class":150}," public\u002Fog\u002F0005.png\n",[16,1745,1746,1747,1750,1751,1754,1755,1757,1758,1761,1762,480],{},"Grafiki artykułów kopiuję więc z ",[46,1748,1749],{},"public\u002Fpics\u002FNNNN.png"," do ",[46,1752,1753],{},"public\u002Fog\u002FNNNN.png",", stempluję logiem, a strona artykułu mapuje ",[46,1756,215],{}," prostą podmianą prefiksu ścieżki — ",[46,1759,1760],{},"\u002Fpics\u002F"," → ",[46,1763,1764],{},"\u002Fog\u002F",[117,1766,1770],{"className":1767,"code":1768,"language":1769,"meta":122,"style":122},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","const ogImage = page.value.image\n  ? absoluteUrl(page.value.image.replace('\u002Fpics\u002F', '\u002Fog\u002F'))\n  : undefined\n","ts",[46,1771,1772,1794,1838],{"__ignoreMap":122},[126,1773,1774,1776,1779,1781,1784,1786,1789,1791],{"class":128,"line":129},[126,1775,1014],{"class":140},[126,1777,1778],{"class":542}," ogImage ",[126,1780,144],{"class":132},[126,1782,1783],{"class":542}," page",[126,1785,41],{"class":132},[126,1787,1788],{"class":542},"value",[126,1790,41],{"class":132},[126,1792,1793],{"class":542},"image\n",[126,1795,1796,1799,1802,1805,1807,1809,1811,1814,1816,1819,1821,1823,1825,1827,1829,1831,1833,1835],{"class":128,"line":171},[126,1797,1798],{"class":132},"  ?",[126,1800,1801],{"class":1025}," absoluteUrl",[126,1803,1804],{"class":542},"(page",[126,1806,41],{"class":132},[126,1808,1788],{"class":542},[126,1810,41],{"class":132},[126,1812,1813],{"class":542},"image",[126,1815,41],{"class":132},[126,1817,1818],{"class":1025},"replace",[126,1820,1040],{"class":542},[126,1822,1068],{"class":132},[126,1824,1760],{"class":150},[126,1826,1068],{"class":132},[126,1828,1047],{"class":132},[126,1830,368],{"class":132},[126,1832,1764],{"class":150},[126,1834,1068],{"class":132},[126,1836,1837],{"class":542},"))\n",[126,1839,1840,1843],{"class":128,"line":202},[126,1841,1842],{"class":132},"  :",[126,1844,1845],{"class":132}," undefined\n",[16,1847,1848,1849,1852],{},"W efekcie nagłówek artykułu i jego obraz OG to ",[26,1850,1851],{},"ta sama grafika",", tyle że wersja OG nosi dodatkowo logo w rogu.",[11,1854,1856],{"id":1855},"jak-wywołuję-skill-e-gdy-potrzebuję-nowych-grafik","Jak wywołuję SKILL-e, gdy potrzebuję nowych grafik",[16,1858,1859,1860,1863,1864,994,1867,1870],{},"Cała obsługa sprowadza się do kilku poleceń z poziomu ",[26,1861,1862],{},"Claude Code",". Dla ",[26,1865,1866],{},"tego",[46,1868,1869],{},"order: 5",") wyglądało to tak:",[117,1872,1874],{"className":340,"code":1873,"language":342,"meta":122,"style":122},"# 1. Grafika nagłówkowa artykułu → public\u002Fpics\u002F0005.png\n\u002Fgenerate-article-picture grafiki-artykulow-i-obrazy-og\n\n# 2. Wersja OG artykułu: kopia grafiki + logo w rogu → public\u002Fog\u002F0005.png\n#    (kopiuję pics → og, po czym stempluję logiem)\nnode .claude\u002Fskills\u002Fgenerate-page-og-picture\u002Fstamp-og-logo.mjs public\u002Fog\u002F0005.png\n",[46,1875,1876,1881,1889,1893,1898,1903],{"__ignoreMap":122},[126,1877,1878],{"class":128,"line":129},[126,1879,1880],{"class":1637},"# 1. Grafika nagłówkowa artykułu → public\u002Fpics\u002F0005.png\n",[126,1882,1883,1886],{"class":128,"line":171},[126,1884,1885],{"class":349},"\u002Fgenerate-article-picture",[126,1887,1888],{"class":150}," grafiki-artykulow-i-obrazy-og\n",[126,1890,1891],{"class":128,"line":202},[126,1892,561],{"emptyLinePlaceholder":560},[126,1894,1895],{"class":128,"line":233},[126,1896,1897],{"class":1637},"# 2. Wersja OG artykułu: kopia grafiki + logo w rogu → public\u002Fog\u002F0005.png\n",[126,1899,1900],{"class":128,"line":603},[126,1901,1902],{"class":1637},"#    (kopiuję pics → og, po czym stempluję logiem)\n",[126,1904,1905,1907,1909],{"class":128,"line":611},[126,1906,1737],{"class":349},[126,1908,1740],{"class":150},[126,1910,1743],{"class":150},[16,1912,1913],{},"A dla podstrony — np. strony kontaktowej — jeden SKILL załatwia obraz i aktualizację metadanych:",[117,1915,1917],{"className":340,"code":1916,"language":342,"meta":122,"style":122},"\u002Fgenerate-page-og-picture \u002Fcontact \"Kontakt\"\n",[46,1918,1919],{"__ignoreMap":122},[126,1920,1921,1924,1927,1930,1933],{"class":128,"line":129},[126,1922,1923],{"class":349},"\u002Fgenerate-page-og-picture",[126,1925,1926],{"class":150}," \u002Fcontact",[126,1928,1929],{"class":132}," \"",[126,1931,1932],{"class":150},"Kontakt",[126,1934,1935],{"class":132},"\"\n",[16,1937,1938,1939,1942,1943,1946,1947,1950,1951,480],{},"Pod spodem SKILL nie robi „magii” — po prostu ",[26,1940,1941],{},"uruchamia skrypt pomocniczy"," poleceniem ",[46,1944,1945],{},"node …\u002Fgenerate-page-og-picture.mjs \"\u003Curl>\" \"\u003Ctekst>\"",", podając opcjonalny ",[46,1948,1949],{},"--brief"," ze sceną. Zanim odpali API, warto rzucić okiem na pełny prompt trybem ",[46,1952,1953],{},"--dry-run",[117,1955,1957],{"className":340,"code":1956,"language":342,"meta":122,"style":122},"node .claude\u002Fskills\u002Fgenerate-article-picture\u002Fgenerate-article-picture.mjs \\\n  grafiki-artykulow-i-obrazy-og \\\n  --brief \"a pipeline turning AI artwork into branded social preview cards\" \\\n  --dry-run\n",[46,1958,1959,1969,1976,1990],{"__ignoreMap":122},[126,1960,1961,1963,1966],{"class":128,"line":129},[126,1962,1737],{"class":349},[126,1964,1965],{"class":150}," .claude\u002Fskills\u002Fgenerate-article-picture\u002Fgenerate-article-picture.mjs",[126,1967,1968],{"class":542}," \\\n",[126,1970,1971,1974],{"class":128,"line":171},[126,1972,1973],{"class":150},"  grafiki-artykulow-i-obrazy-og",[126,1975,1968],{"class":542},[126,1977,1978,1981,1983,1986,1988],{"class":128,"line":202},[126,1979,1980],{"class":150},"  --brief",[126,1982,1929],{"class":132},[126,1984,1985],{"class":150},"a pipeline turning AI artwork into branded social preview cards",[126,1987,147],{"class":132},[126,1989,1968],{"class":542},[126,1991,1992],{"class":128,"line":233},[126,1993,1994],{"class":150},"  --dry-run\n",[1996,1997,1998],"note",{},[16,1999,2000,2001,2004,2005,2008],{},"Podział na ",[26,2002,2003],{},"SKILL (decyzja) + skrypt (rygor)"," ma jeszcze jedną zaletę: skrypt można uruchomić ",[26,2006,2007],{},"ręcznie",", bez agenta. SKILL jest wygodną nakładką, ale nie jest niezbędny — co bardzo upraszcza testowanie.",[11,2010,2012],{"id":2011},"wnioski","Wnioski",[16,2014,2015],{},"Trzy obserwacje z tego małego projektu:",[403,2017,2018,2024,2035],{},[406,2019,2020,2023],{},[26,2021,2022],{},"Niedeterminizm AI trzymaj tam, gdzie nie szkodzi."," Model genialnie maluje tło, ale nie zawsze radzi sobie z dokładnym rozmiarem, czcionką i polskimi znakami. Oddanie tła AI, a reszty deterministycznemu skryptowi, daje wynik, który jest jednocześnie ładny i powtarzalny co do piksela.",[406,2025,2026,2029,2030,37,2032,2034],{},[26,2027,2028],{},"Spójność to nie kwestia modelu, lecz stałego promptu."," Tożsamość wizualna serii siedzi w skrypcie (sekcje ",[46,2031,864],{},[46,2033,868],{},"), a nie w zmiennym briefie. Dlatego dziesiąty artykuł będzie pasował do pierwszego, niezależnie od tego, co akurat wymyśli model.",[406,2036,2037,2040,2041,2043],{},[26,2038,2039],{},"Jedno źródło prawdy się opłaca."," Wydzielenie logo do ",[46,2042,1565],{}," sprawia, że marka jest wszędzie identyczna, a jej zmiana to edycja jednego pliku. Ta sama zasada, którą stosuję w kodzie aplikacji, działa równie dobrze przy grafikach.",[16,2045,2046,2047,2050],{},"Najfajniejsze jest jednak to, że ",[26,2048,2049],{},"dodanie grafiki do nowego artykułu to teraz jedno polecenie",". Nie szukam stock-ów, nie walczę z kadrowaniem, nie poprawiam fontów. Piszę treść, odpalam SKILL-a i mam spójną, profesjonalną grafikę — a obraz, który właśnie oglądasz w nagłówku tego artykułu, powstał dokładnie tą drogą.",[2052,2053,2054],"style",{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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 .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sHepR, html code.shiki .sHepR{--shiki-light:#39ADB5;--shiki-light-font-weight:bold;--shiki-default:#89DDFF;--shiki-default-font-weight:bold;--shiki-dark:#89DDFF;--shiki-dark-font-weight:bold}html pre.shiki code .so75L, html code.shiki .so75L{--shiki-light:#E53935;--shiki-light-font-weight:bold;--shiki-default:#F07178;--shiki-default-font-weight:bold;--shiki-dark:#F07178;--shiki-dark-font-weight:bold}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .s5tWE, html code.shiki .s5tWE{--shiki-light:#E53935;--shiki-light-font-style:italic;--shiki-default:#F07178;--shiki-default-font-style:italic;--shiki-dark:#F07178;--shiki-dark-font-style:italic}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}",{"title":122,"searchDepth":171,"depth":171,"links":2056},[2057,2058,2059,2060,2061,2066,2071,2072],{"id":13,"depth":171,"text":14},{"id":87,"depth":171,"text":88},{"id":303,"depth":171,"text":304},{"id":465,"depth":171,"text":466},{"id":524,"depth":171,"text":2062,"children":2063},"SKILL #1: grafiki artykułów — generate-article-picture",[2064,2065],{"id":853,"depth":202,"text":854},{"id":904,"depth":202,"text":905},{"id":1218,"depth":171,"text":2067,"children":2068},"SKILL #2: obrazy OG podstron — generate-page-og-picture",[2069,2070],{"id":1552,"depth":202,"text":1553},{"id":1700,"depth":202,"text":1701},{"id":1855,"depth":171,"text":1856},{"id":2011,"depth":171,"text":2012},"2026-06-07","Dwa SKILL-e Claude Code, które generują grafiki artykułów i obrazy Open Graph stron w powtarzalnym stylu — AI od generowania bazowej grafiki, skrypty od manipulacji obrazem.",false,"md","\u002Fpics\u002F0005.png",{"order":603},null,"\u002Fblog\u002Fgrafiki-artykulow-i-obrazy-og",{"title":5,"description":2074},{"loc":2080},"blog\u002Fgrafiki-artykulow-i-obrazy-og","H_AqlSBFz9TM6OPcqVs_1Tox0xnIy-vjXvncvQNj-cE",[2086,2079],{"title":2087,"path":22,"stem":2088,"description":2089,"children":-1},"AI-Driven Development Workflow","blog\u002Fai-driven-development-workflow","Buduję zespół agentów \u002F specjalistów Claude Code, ustalam flow procesu produkcji i utrzymania nowej aplikacji.",1780996675131]