Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
はてなCMSでFigmaを取り込むシステムに使われている技術
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
NanimonoDemonai
November 29, 2025
190
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
はてなCMSでFigmaを取り込むシステムに使われている技術
Kyoto.js 24
NanimonoDemonai
November 29, 2025
More Decks by NanimonoDemonai
See All by NanimonoDemonai
LLMを「機能」として組み込む技術:「Figma to はてなCMS」におけるプロンプトエンジニアリングからAIエージェント構築にわたる精度向上の軌跡
nanimonodemonai
0
620
はてなCMSのアーキテクチャ; 巨大な既存システムと共存して最新技術を取り入れる
nanimonodemonai
0
850
ViteでCSSのバージョン違いを作る
nanimonodemonai
0
120
はてなブログのブログ表示に必要なJSを1/6にした話
nanimonodemonai
0
1k
はてなブログのESM化
nanimonodemonai
0
440
登壇資料.pdf
nanimonodemonai
0
460
はてなブログのフロントエンドに秩序はもたらされたのか
nanimonodemonai
3
6.2k
Featured
See All Featured
Beyond borders and beyond the search box: How to win the global "messy middle" with AI-driven SEO
davidcarrasco
3
280
The Curious Case for Waylosing
cassininazir
1
560
Paper Plane (Part 1)
katiecoart
PRO
2
11k
Designing for Timeless Needs
cassininazir
1
520
The Psychology of Web Performance [Beyond Tellerrand 2023]
tammyeverts
49
3.6k
Skip the Path - Find Your Career Trail
mkilby
1
250
コードの90%をAIが書く世界で何が待っているのか / What awaits us in a world where 90% of the code is written by AI
rkaga
63
46k
Why Our Code Smells
bkeepers
PRO
340
58k
Digital Ethics as a Driver of Design Innovation
axbom
PRO
1
450
Art, The Web, and Tiny UX
lynnandtonic
304
22k
More Than Pixels: Becoming A User Experience Designer
marktimemedia
3
560
Balancing Empowerment & Direction
lara
6
1.3k
Transcript
,ZPUPKT id: nanimono_demonai ͯͳCMSͰFigmaΛऔΓࠐΉγεςϜʹ ΘΕ͍ͯΔٕज़
ࣗݾհɾΠϯτϩ AIΤʔδΣϯτͷߏங FigmaϓϥάΠϯͷߏங Server-Sent Events 2
ࣗݾհɾΠϯτϩ 3
ࣗݾհ • id: nanimono_demonai • @NanimonoDaemon • ͯͳϒϩάɺͯͳCMSͷ։ൃͱɺλεΫཧΛߦ͏ • େֶͰػցֶशʢػց༁ʣΛݚڀ͍ͯͨ͠
4
ͯͳCMSͱ 5 ·Ί͖ͪ͠ ϩΰϖϯϩʔζͷࡾ֯ܗ https://www.hatena.ne.jp/cms/ ײతͳૢ࡞ੑ ڧݻͳηΩϡϦςΟ ॊೈͳίϯςϯπཧ ࠷৽ػೳΛ͍ͭͰ •
ͯͳͷ๏ਓ͚αʔϏε
ͯͳCMSͷΞʔΩςΫνϟ 6 ·Ί͖ͪ͠ https://speakerdeck.com/nanimonodemonai/hatenacmsnoakitekutiya-ju-da-naji- cun-sisutemutogong-cun-sitezui-xin-ji-shu-woqu-riru-reru Next.jsͷTypeScriptΞϓϦέʔγϣϯ ࠷ۙ/FYUʹͯ͠5VSCPQBDLʹ͠·ͨ͠ɻ݁ՌϏϧυߴԽ͞Εͨͷ·ͨผͷʜ
Figma to ͯͳCMSͱ 7
• ʮσβΠϯʯͱʮ࣮ʯͷஅΛຒΊΔαʔϏεΛ࡞Γ͍ͨ • FigmaͷσβΠϯσʔλΛͯͳCMSʹࣗવͳίʔυͱͯ͠ө͍ͨ͠ • ͔͠͠ɺFigmaͷσʔλMarkdownͷΑ͏ʹHTMLͱશવରԠ͍ͯ͠ͳ͍ • ๏ਓ͚ͷαʔϏεͱͯ͠࡞ΔʢͳͷͰɺݸਓͰ͑·ͤΜ…ʣ ՝: FigmaͰ࡞ͬͨσʔλΛऔΓࠐΊΔFigmaϓϥάΠϯΛ࡞Γ͍ͨ
8 ·Ί͖ͪ͠ ࠓ'JHNB3FBDUʹ͢ΔαʔϏεग़͍ͯ͠·͢ AIʹΑΔΞϓϦέʔγϣϯ࡞͕ख़͍ͯ͠Δੈͷத͔ͩΒ AIΛ׆༻ͯ͠ΞϓϦέʔγϣϯΛ࡞Ζ͏
࣌ͷઌߦࣄྫௐࠪ • FigmaΛHTMLʹ͢ΔͨΊͷɺϧʔϧϕʔεͷΦʔϓϯιʔε͕͋Δ • Coding AgentͰFigma৯ΘͤΕɺHTMLͱCSSΛॻ͍ͯ͘ΕΔ • Gemini 2.5 Proʹ৯ΘͤΕɺHTMLͱCSSΛԠͯ͘͠ΕΔ
• ௐࠪͷՌͱͯ͠ϓϩϯϓτҰൃͰม͢ΔPOC࡞ͬͯɺօͷධՁ্ʑ 9 ઌߦࣄྫΛ͋ͨͬͯΈΔͱ… ͔͠͠ɺAIʹ͓ͤͰ͏·͍͔͘ͳ͔ͬͨ
AIΤʔδΣϯτͷߏங 10
Figma࿈ܞͰ • ҰճͷLLMݺͼग़͠Ͱɺͯ͢ͷHTMLΛग़ྗ͢Δ͜ͱ͕Ͱ͖ͳ͍ • ग़ྗτʔΫϯͷݶք͕͋ΔͨΊ • ͔͠͠ɺग़ྗτʔΫϯΛ૿ͨ͢Ίʹɺ࠶ؼతʹೖྗͤͨ͞ͷΛग़ྗͤ͞Δ ΈͰ࣭͕ྼԽͯ͠͠·͏ 11 ᘳͳࢦࣔจʢਆϓϩϯϓτʣʹݶք͕͋ͬͨ
ͦ͜ͰɺAIΤʔδΣϯτతΞϓϩʔνͷ࣮ʹΓସ͑Δ͜ͱʹ
AIΤʔδΣϯτΛ࡞ΔͨΊʹ • ٻΊΔ࣭ͷग़ྗ͕ಘΒΕΔ·Ͱɺग़ྗʹԠͯ͡৽ͨͳਪΛߦΘ͍ͤͨ • ͜Ε·Ͱͷग़ྗΛอଘ͍ͨ͠ • ͜ΕΒΛAIαʔϏεͷΠϯλʔϑΣʔεʹґଘͤͣʹ࡞Γ͍ͨ 12 ʮAIʹ͓ͤʯͰͲΜͲΜखʹෛ͑ͳ͍࣮ͷෳࡶ͕͞ొͯ͘͠Δ ͜ΕΒΛղܾ͢ΔϥΠϒϥϦͱͯ͠զʑLangChainͱLangGraphΛ࠾༻
Agentic Work fl owΛࢧ͑Δٕज़ 13 Agent Framework Agent Runtime Agent
Harness • LangChain.js :͞·͟·ͳAIαʔϏεΛ౷Ұͨ͠ΠϯλʔϑΣʔεͰѻ͑Δ • αʔϏεΛநԽ͢ΔͨΊͷϨΠϠʔ https://blog.langchain.com/agent-frameworks-runtimes-and-harnesses-oh-my/ • LangGraph.js: ΤʔδΣϯτͷ෦ঢ়ଶΛཧ͢Δ • ReactͰ͍͏ͱ͜ΖͷReduxͳͲͷεςʔτཧϥΠϒϥϦͷงғؾ • Deep Agents: جຊతͳΤʔδΣϯτΛ࡞ΔͨΊͷϑϨʔϜϫʔΫ
Figma࿈ܞ࠷ऴతʹͲ͏࡞ΒΕ͍ͯΔΜ͚ͩͬʁ • LangGraph.jsΛ༻͍ͨAgentic Work fl ow • LangChain.jsͰݺͼग़͍ͯ͠ΔAIαʔϏε Vertex AI
(Gemini 2.5 pro) • ΞʔΩςΫνϟRe fl ectionͱݺΕΔ ͷͰɺAIʹࣗͷग़ྗΛఆͤ͞ɺظ͢ Δग़ྗΛಘΒΕΔ·Ͱग़ྗΛॻ͖ͤ͞Δ Έ 14 ·Ί͖ͪ͠ ਪʹ͕͔͔࣌ؒΔ͔Βɺ"QQͷ֎Ͱಈ͔͍ͯ͠·͢ "NB[PO424ͰΩϡʔΠϯά͍ͯ͠Δ
Figma Pluginͷߏங 15
Figma Pluginͷ֎؍ 16 https://developers. fi gma.com/docs/plugins/how-plugins-run/ FigmaͷηΩϡϦςΟʹΑΓɺΠϯλʔωοτͷΞΫηε͕ݫ͍͠
Figma PluginͰͷೝূ 17 ࢀর: https://developers. fi gma.com/docs/plugins/how-plugins-run/ ͜ͷՕॴΛ Ͳ͏ϥΫʹ࡞Ζ͏͔ʁ
Server-Sent Events 18
ͳͥServer-Sent Events͕ඞཁ͔ʁ • AIʹΑΔਪɺΞϓϦέʔγϣϯͷ֎Ͱ͕͔͔࣌ؒΔඇಉظॲཧ • มͷਐḿ݁ՌΛϦΞϧλΠϜʹΫϥΠΞϯτ௨͢Δͱྑ͍ • FigmaϓϥάΠϯͷೝূɺΞϓϦέʔγϣϯͷ֎Ͱ͕͔͔࣌ؒΔඇಉظॲཧ • ͍ͭऴΘΔ͔Θ͔Βͳ͍ϢʔβʔͷॲཧΛͨͳ͍ͱ͍͚ͳ͍
19 Ͳ͔͜ʹɺ؆୯ʹ࣮Ͱ͖Δ௨ٕज़͕ͳ͍͔ͳ͋ʁ
Server-Sent Events 20 https://html.spec.whatwg.org/multipage/server-sent-events.html#server-sent-events • αʔόʔ͔ΒΫϥΠΞϯτʹҰํʹσʔλΛૹ৴͢Δ͜ͱʹಛ Խٕͨ͠ज़ • ํʹ͚ͨ͠ΕWebSocket͕͍͍ͩΖ͏ •
HTTPϓϩτίϧͰಈ࡞ • γϯϓϧ͔ͭɺ࣮͕༰қ • ΫϥΠΞϯτ͕Web APIͷEventSource ʹؚ·Ε͓ͯΓར༻͕༰ қ
Server-Sent Eventsͷ࣮ 21 async function * readPolling(readKey: string) { const
encoder = new TextEncoder(); while (true) { const code = await getCodeForReadKey(readKey); if (code.type = = = 'pending') { yield encoder.encode('event: pending\ndata: pending\n\n'); await sleep(1000); continue; } if (code.type = = = 'expired') { yield encoder.encode('event: expired\ndata: expired\n\n'); return; } if (code.type = = = 'denied') { yield encoder.encode('event: denied\ndata: denied\n\n'); return; } if (code.type = = = 'success') { yield encoder.encode(`event: success\ndata: ${code.code}\n\n`); return; } return; } } • ͜Μͳײ͡ͷδΣωϨʔλ ؔΛ༰қ͢Δ
Server-Sent Events 22 export function GET(request: NextRequest) { const searchParams
= request.nextUrl.searchParams; const readKey = searchParams.get('readKey'); if (!readKey) { return new Response('bad request', { status: 400 }); } const iterator = readPolling(readKey); const stream = iteratorToStream(iterator); request.signal.onabort = async () = > { await clearReadKey(readKey); }; return new Response(stream, { headers: { 'Content-Type': 'text/event - stream', 'Cache-Control': 'no - cache, no - transform', 'Connection': 'keep - alive', 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS', 'Access-Control-Allow-Headers': 'Content-Type, Authorization', }, }); } function iteratorToStream(iterator: AsyncGenerator<Uint8Array>) { return new ReadableStream({ async pull(controller) { const { value, done } = await iterator.next(); if (done) { controller.close(); } else { controller.enqueue(value); } }, }); } • Next.jsͳΒɺ RouteϋϯυϥʹδΣω ϨʔλؔΛ ReadableStreamʹม͠ ͨͷΛͯ͠Ε
Figma to ͯͳCMSʹ͓͚ΔServer-Sent Eventsͷ۩ମతͳར༻γʔϯ • ೝূϑϩʔͰͷϙʔϦϯά • FigmaϓϥάΠϯ͔ΒͷOIDCೝূετϦʔϜʹ͓͍ͯɺreadKeyʹอଘ͞Εͨೝ ূίʔυͷঢ়ଶΛϙʔϦϯά͢Δͷʹར༻ •
ม݁ՌͷετϦʔϛϯά͍ͬͯͳ͍ͷͷ… • ͔͠͠ɺServer-Sent EventsAI͕ੜͨ͠ίʔυยΛϢʔβʔʹܧଓతʹૹ৴ ͠ϦΞϧλΠϜʹө͢Δ͜ͱʹΑ͘ΘΕΔٕज़ 23 Next.jsͰ؆୯ʹServer-Sent EventsΛ࡞Δํ๏͕ݟ͔ͭͬͨͷͰɺ ࠓޙ׆༻͍͖͍ͯͨ͠
·ͱΊ: ࠓճ͝հͨ͠ͷ • AIΤʔδΣϯτΛ࡞͢ΔͨΊͷٕज़ • Figma Pluginͷ࡞ʹ͓͚Δೝূܥϑϩʔෳࡶ • ৭ʑͳՕॴͰར༻ՄೳͳServer-Sent Eventsͷϓϩτίϧ֓ཁ
• ͦͯ͠Next.jsͰͷ࣮ํ๏ 24
ऴΘΓ 25