/** * final-compile.js * Создаёт: * - build/intro.pptx (cover + TOC) * - build/dividers.pptx (5 dividers между секциями) * - build/recap.pptx (3 recap слайда: timeline, what's next, closing) * Затем вызывает merge.js для склейки всех в один .pptx. */ 'use strict'; const path = require('path'); const fs = require('fs'); const ds = require('./design-system'); const { theme, palette, fonts, sizes, layouts, helpers } = ds; const pptxgen = require('pptxgenjs'); const WORKSPACE = __dirname; const SLIDES = path.join(WORKSPACE, 'slides'); const BUILD = path.join(WORKSPACE, 'build'); fs.mkdirSync(BUILD, { recursive: true }); const SECTIONS = [ { dir: 'section1-chains', title: 'LangChain 1.0', subtitle: 'chains, LCEL, agents, retrievers' }, { dir: 'section2-langgraph', title: 'LangGraph 1.0', subtitle: 'state, nodes, persistence, HITL' }, { dir: 'section3-deepagents', title: 'Deep Agents', subtitle: 'harness, todos, virtual FS, subagents' }, { dir: 'section4-openswe', title: 'Open SWE', subtitle: 'async coding agent, triggers, dashboard' }, { dir: 'section5-ecosystem', title: 'Экосистема', subtitle: 'LangSmith, Studio, deployment' }, ]; // -------- INTRO (cover + TOC) -------- function buildIntro(pres) { // Slide 1 -- cover const s1 = pres.addSlide(); helpers.slideBase(s1, pres, theme); // top accent s1.addShape(pres.ShapeType.rect, { x: 0, y: 0, w: 10, h: 0.15, fill: { color: palette.accent.primary }, line: { type: 'none' } }); helpers.addHeader(s1, pres, theme, { eyebrow: 'DEEP DIVE / TUTORIAL', sectionNumber: 0, title: 'Эволюция LangChain', }); // subtitle s1.addText('от chains до Deep Agents и Open SWE', { x: 0.5, y: 1.85, w: 9, h: 0.5, fontFace: fonts.ui, fontSize: 24, color: palette.text.secondary, bold: false, }); // gold bar s1.addShape(pres.ShapeType.rect, { x: 0.5, y: 2.55, w: 1.5, h: 0.06, fill: { color: palette.accent.secondary }, line: { type: 'none' } }); // description s1.addText( 'Большой tutorial по экосистеме LangChain: chains, LCEL, LangGraph, Deep Agents, Open SWE. ' + 'Python 1.0+, реальные API, плотный код.', { x: 0.5, y: 2.85, w: 9, h: 1.5, fontFace: fonts.ui, fontSize: 16, color: palette.text.secondary, } ); // footer meta s1.addText('100+ слайдов | 2022 -- 2026 | Python 1.0+', { x: 0.5, y: 5.15, w: 9, h: 0.3, fontFace: fonts.ui, fontSize: 11, color: palette.text.muted, align: 'center', }); helpers.addPageNumber(s1, pres, theme, 1); // Slide 2 -- TOC const s2 = pres.addSlide(); helpers.slideBase(s2, pres, theme); helpers.addHeader(s2, pres, theme, { eyebrow: 'CONTENTS', sectionNumber: 0, title: 'Содержание', }); let y = layouts.CONTENT_TOP + 0.1; SECTIONS.forEach((sec, i) => { // card s2.addShape(pres.ShapeType.roundRect, { x: 0.5, y: y, w: 9, h: 0.55, fill: { color: palette.bg.elevated }, line: { color: palette.border.subtle, width: 0.5 }, rectRadius: 0.08, }); // number badge s2.addShape(pres.ShapeType.roundRect, { x: 0.65, y: y + 0.1, w: 0.35, h: 0.35, fill: { color: palette.accent.primary }, line: { type: 'none' }, rectRadius: 0.04, }); s2.addText(String(i + 1), { x: 0.65, y: y + 0.12, w: 0.35, h: 0.3, fontFace: fonts.ui, fontSize: 14, color: palette.bg.primary, bold: true, align: 'center', }); s2.addText(sec.title + ' -- ' + sec.subtitle, { x: 1.15, y: y + 0.08, w: 7.5, h: 0.4, fontFace: fonts.ui, fontSize: 14, color: palette.text.primary, bold: true, }); y += 0.65; }); // total s2.addText('Всего: 122 content слайдов + cover, TOC, 5 dividers, recap = 132', { x: 0.5, y: 5.05, w: 9, h: 0.3, fontFace: fonts.ui, fontSize: 12, color: palette.accent.secondary, align: 'center', }); helpers.addPageNumber(s2, pres, theme, 2); } // -------- DIVIDERS (5) -------- function buildDividers(pres) { SECTIONS.forEach((sec, i) => { const slide = pres.addSlide(); helpers.slideBase(slide, pres, theme); // big stage number slide.addShape(pres.ShapeType.rect, { x: 0, y: 0, w: 10, h: 0.15, fill: { color: palette.accent.primary }, line: { type: 'none' }, }); // huge number slide.addText('STAGE ' + (i + 1), { x: 0.5, y: 1.4, w: 9, h: 0.5, fontFace: fonts.ui, fontSize: 18, color: palette.accent.primary, bold: true, charSpacing: 6, }); slide.addText(sec.title, { x: 0.5, y: 2.0, w: 9, h: 1.2, fontFace: fonts.ui, fontSize: 60, color: palette.text.primary, bold: true, }); slide.addText(sec.subtitle, { x: 0.5, y: 3.2, w: 9, h: 0.5, fontFace: fonts.ui, fontSize: 22, color: palette.text.secondary, }); // gold accent line slide.addShape(pres.ShapeType.rect, { x: 0.5, y: 3.85, w: 1.5, h: 0.05, fill: { color: palette.accent.secondary }, line: { type: 'none' }, }); // section nav slide.addText('Раздел ' + (i + 1) + ' из ' + SECTIONS.length, { x: 0.5, y: 5.0, w: 9, h: 0.3, fontFace: fonts.ui, fontSize: 12, color: palette.text.muted, align: 'center', }); helpers.addPageNumber(slide, pres, theme, 0); // 0 = no number }); } // -------- RECAP (3) -------- function buildRecap(pres) { // Recap 1 -- Timeline let s = pres.addSlide(); helpers.slideBase(s, pres, theme); helpers.addHeader(s, pres, theme, { eyebrow: 'TIMELINE', sectionNumber: 0, title: 'Полная карта эволюции', }); s.addText( [ { text: '2022-10 ', options: { color: palette.accent.primary, bold: true } }, { text: 'LangChain 0.1 -- запуск фреймворка chains\n', options: { color: palette.text.primary } }, { text: '2023-10 ', options: { color: palette.accent.primary, bold: true } }, { text: 'LangChain 0.1 (stable) -- первый production-ready\n', options: { color: palette.text.primary } }, { text: '2024-01 ', options: { color: palette.accent.primary, bold: true } }, { text: 'LangGraph 0.1 -- stateful графы\n', options: { color: palette.text.primary } }, { text: '2025-08 ', options: { color: palette.accent.primary, bold: true } }, { text: 'Deep Agents 0.x -- harness с subagents\n', options: { color: palette.text.primary } }, { text: '2025-10 ', options: { color: palette.accent.secondary, bold: true } }, { text: 'LangChain 1.0 + LangGraph 1.0 (22.10.2025)\n', options: { color: palette.text.primary } }, { text: '2025-12 ', options: { color: palette.accent.secondary, bold: true } }, { text: 'Open SWE 1.0 -- async coding agent', options: { color: palette.text.primary } }, ], { x: 0.7, y: 1.5, w: 8.6, h: 3.4, fontFace: 'JetBrains Mono', fontSize: 13, color: palette.text.primary, paraSpaceAfter: 8, } ); helpers.addPageNumber(s, pres, theme, 130); // Recap 2 -- What's next s = pres.addSlide(); helpers.slideBase(s, pres, theme); helpers.addHeader(s, pres, theme, { eyebrow: 'WHAT IS NEXT', sectionNumber: 0, title: 'Куда движется стек', }); const items = [ ['Subagents', 'делегирование доменных задач с собственным state'], ['Virtual filesystem', 'stateful scratchpad для reasoning-агентов'], ['Human-in-the-loop', 'production-ready approval flows в LangGraph 1.0+'], ['Open SWE', 'async coding agent с GitHub-триггерами и dashboard'], ['LangSmith', 'observability: traces, evals, online monitoring'], ]; let y = 1.5; items.forEach(([k, v]) => { s.addText(k, { x: 0.7, y: y, w: 3, h: 0.4, fontFace: fonts.ui, fontSize: 16, color: palette.accent.secondary, bold: true, }); s.addText(v, { x: 3.8, y: y + 0.05, w: 5.7, h: 0.4, fontFace: fonts.ui, fontSize: 13, color: palette.text.primary, }); y += 0.55; }); helpers.addPageNumber(s, pres, theme, 131); // Recap 3 -- Closing s = pres.addSlide(); helpers.slideBase(s, pres, theme); helpers.addHeader(s, pres, theme, { eyebrow: 'CLOSING', sectionNumber: 0, title: 'Главный тренд', }); s.addText( 'От chain-of-prompts к stateful агентам с harness и human-in-the-loop.', { x: 0.7, y: 1.7, w: 8.6, h: 0.8, fontFace: fonts.ui, fontSize: 22, color: palette.accent.secondary, bold: true, } ); s.addText( 'LangChain 1.0 -- это stable ядро (chains, LCEL, agents, retrievers). ' + 'LangGraph 1.0 -- stateful execution layer. ' + 'Deep Agents -- reasoning harness "из коробки". ' + 'Open SWE -- продуктовая реализация coding agent. ' + 'Всё это работает на Python 1.0+ с @traceable из LangSmith.', { x: 0.7, y: 2.7, w: 8.6, h: 1.8, fontFace: fonts.ui, fontSize: 14, color: palette.text.primary, } ); s.addText('Спасибо! | Mavis / 2026 / Python 1.0+', { x: 0.7, y: 5.0, w: 8.6, h: 0.3, fontFace: fonts.ui, fontSize: 12, color: palette.text.muted, align: 'center', }); helpers.addPageNumber(s, pres, theme, 132); } // -------- MAIN -------- async function main() { // intro let p = new pptxgen(); p.layout = 'LAYOUT_WIDE'; // 13.33x7.5 -- not used since helpers override p.defineLayout({ name: 'LC_16x9', width: 10, height: 5.625 }); p.layout = 'LC_16x9'; buildIntro(p); const introPath = path.join(BUILD, 'intro.pptx'); await p.writeFile({ fileName: introPath }); console.log('[final-compile] wrote ' + introPath); // dividers p = new pptxgen(); p.defineLayout({ name: 'LC_16x9', width: 10, height: 5.625 }); p.layout = 'LC_16x9'; buildDividers(p); const divPath = path.join(BUILD, 'dividers.pptx'); await p.writeFile({ fileName: divPath }); console.log('[final-compile] wrote ' + divPath); // recap p = new pptxgen(); p.defineLayout({ name: 'LC_16x9', width: 10, height: 5.625 }); p.layout = 'LC_16x9'; buildRecap(p); const recapPath = path.join(BUILD, 'recap.pptx'); await p.writeFile({ fileName: recapPath }); console.log('[final-compile] wrote ' + recapPath); } main().catch((e) => { console.error(e); process.exit(1); });