Lock in $30 Savings on PRO—Offer Ends Soon! ⏳

はてなCMSでFigmaを取り込むシステムに使われている技術

Avatar for NanimonoDemonai NanimonoDemonai
November 29, 2025
55

 はてなCMSでFigmaを取り込むシステムに使われている技術

Kyoto.js 24

Avatar for NanimonoDemonai

NanimonoDemonai

November 29, 2025
Tweet

Transcript

  1. ౰࣌ͷઌߦࣄྫௐࠪ • FigmaΛHTMLʹ͢ΔͨΊͷɺϧʔϧϕʔεͷΦʔϓϯιʔε͕͋Δ • Coding AgentͰFigma৯ΘͤΕ͹ɺHTMLͱCSSΛॻ͍ͯ͘ΕΔ • Gemini 2.5 Proʹ৯ΘͤΕ͹ɺHTMLͱCSSΛԠ౴ͯ͘͠ΕΔ

    • ௐࠪͷ੒Ռͱͯ͠ϓϩϯϓτҰൃͰม׵͢ΔPOC΋࡞ͬͯɺօͷධՁ΋্ʑ 9 ઌߦࣄྫΛ͋ͨͬͯΈΔͱ… ͔͠͠ɺAIʹ͓೚ͤͰ͸͏·͍͔͘ͳ͔ͬͨ
  2. 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: جຊతͳΤʔδΣϯτΛ࡞ΔͨΊͷϑϨʔϜϫʔΫ
  3. Figma࿈ܞ͸࠷ऴతʹͲ͏࡞ΒΕ͍ͯΔΜ͚ͩͬʁ • LangGraph.jsΛ༻͍ͨAgentic Work fl ow • LangChain.jsͰݺͼग़͍ͯ͠ΔAIαʔϏε ͸Vertex AI

    (Gemini 2.5 pro) • ΞʔΩςΫνϟ͸Re fl ectionͱݺ͹ΕΔ΋ ͷͰɺAIʹࣗ਎ͷग़ྗΛ൑ఆͤ͞ɺظ଴͢ Δग़ྗΛಘΒΕΔ·Ͱग़ྗΛॻ͖௚ͤ͞Δ ࢓૊Έ 14 ·Ί͖ͪ͠ ਪ࿦ʹ͸͕͔͔࣌ؒΔ͔Βɺ"QQͷ֎Ͱಈ͔͍ͯ͠·͢ "NB[PO424ͰΩϡʔΠϯά͍ͯ͠Δ
  4. 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; } } • ͜Μͳײ͡ͷδΣωϨʔλ ؔ਺Λ༰қ͢Δ
  5. 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ʹม׵͠ ͨ΋ͷΛ౉ͯ͠΍Ε͹׬੒
  6. Figma to ͸ͯͳCMSʹ͓͚ΔServer-Sent Eventsͷ۩ମతͳར༻γʔϯ • ೝূϑϩʔͰͷϙʔϦϯά • FigmaϓϥάΠϯ͔ΒͷOIDCೝূετϦʔϜʹ͓͍ͯɺreadKeyʹอଘ͞Εͨೝ ূίʔυͷঢ়ଶΛϙʔϦϯά͢Δͷʹར༻ •

    ม׵݁ՌͷετϦʔϛϯά͸΍͍ͬͯͳ͍΋ͷͷ… • ͔͠͠ɺServer-Sent Events͸AI͕ੜ੒ͨ͠ίʔυยΛϢʔβʔʹܧଓతʹૹ৴ ͠ϦΞϧλΠϜʹ൓ө͢Δ͜ͱʹΑ͘࢖ΘΕΔٕज़ 23 Next.jsͰ؆୯ʹServer-Sent EventsΛ࡞Δํ๏͕ݟ͔ͭͬͨͷͰɺ ࠓޙ׆༻͍͖͍ͯͨ͠