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
640
はてなCMSのアーキテクチャ; 巨大な既存システムと共存して最新技術を取り入れる
nanimonodemonai
0
860
ViteでCSSのバージョン違いを作る
nanimonodemonai
0
130
はてなブログのブログ表示に必要なJSを1/6にした話
nanimonodemonai
0
1k
はてなブログのESM化
nanimonodemonai
0
440
登壇資料.pdf
nanimonodemonai
0
460
はてなブログのフロントエンドに秩序はもたらされたのか
nanimonodemonai
3
6.2k
Featured
See All Featured
Making the Leap to Tech Lead
cromwellryan
135
10k
Paper Plane
katiecoart
PRO
5
54k
Thoughts on Productivity
jonyablonski
76
5.4k
The Limits of Empathy - UXLibs8
cassininazir
2
750
DevOps and Value Stream Thinking: Enabling flow, efficiency and business value
helenjbeal
2
420
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
360
31k
A Tale of Four Properties
chriscoyier
163
24k
Mobile First: as difficult as doing things right
swwweet
225
10k
Cheating the UX When There Is Nothing More to Optimize - PixelPioneers
stephaniewalter
287
14k
Navigating the Design Leadership Dip - Product Design Week Design Leaders+ Conference 2024
apolaine
2
490
How to audit for AI Accessibility on your Front & Back End
davetheseo
0
620
Speed Design
sergeychernyshev
33
2.2k
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