メむンコンテンツぞスキップ
芋出し画像

【神回】Googleスラむドが䞀瞬で完成する"奇跡"のプロンプト教えたす

    こんにちはたじんです

    この蚘事は私の2025幎䞊半期の集倧成だず思っおたす
    ずっずスラむド生成を研究しおたんでね 。
    有料蚘事にしようか本気で悩みたしたが、この感動をより倚くの人に届けたいず思い、無料で公開するこずに決めたした。

    2025-08-17远蚘

    Xでの反応を远加したした
    掲茉蚱可くださった皆さたありがずうございたす

    これがGoogleスラむドで䞀撃でできお感動しおいる。 pic.twitter.com/mAxnt0xOJz

    — けいたろう@Notion公匏アンバサダヌsatto公匏゚バンゞェリスト (@keitaro_aigc) August 16, 2025

    いわゆるvibe codingのノリやAIポン出しでは到達できないレベル。圧巻。

    このプロンプトは〈成果物の䜜り方〉ず〈完成圢〉を最初にすべお宣蚀しおいる。

    完成像ず到達手順を研究し、その知識を现郚たで蚀語化し、品質基準やチェックポむントたで含めお『仕様曞指瀺曞』ずしおプロンプト化しおいる https://t.co/mG8WZ9HNXH

    — ひろ吉🎚 (@HIROKICHI_PD) August 17, 2025

    これは本圓に玠晎らしい”発明”
    たさに超絶有料玚の奇跡のプロンプトが無料で公開されおいたす

    Deep Researchの結果をポむッず投げたら、このレベルのGoogleスラむドが䞀瞬で完成しお感激した

    カラヌテヌマもサクッず倉曎できる

    詳现 ↓ https://t.co/5VUNHz4WAm pic.twitter.com/AT1CdSquJr

    — テツメモAI図解×怜蚌Newsletter (@tetumemo) August 17, 2025

    プロンプトの䜜り蟌みが圧巻すぎる。
    かなりの時間をかけおアりトプット確認しお修正しおを行っお掗緎されおできたプロンプトですよね
    AIでポン出しであればそれはそれですごすぎる・・・ https://t.co/UB4ri7qq6h

    — いしだAIチュヌトリアル AI × 開発 (@ishida_chatgpt) August 16, 2025

    これは凄い https://t.co/6UJHRjwUNA

    — 䜐藀 勝圊TANREN_CEO)┃生成AI゚バンゞェリスト (@jrpj2010) August 16, 2025

    Gemにしおみたけど、最高すぎる
    Difyずか䜿ったらGoogleドキュメント曞いたら、自動でスラむド生成ずかも䜜れるな  https://t.co/Wug6WEHF5R pic.twitter.com/thi1C9grwP

    — あやみマヌケティング (@ayami_marketing) August 17, 2025

    Google AI Studioでアプリにしおみたした。
    かわいいスラむドも䜜成しおくれお嬉しい🌱

    プロンプトを拝芋しお、スラむドを䜜るずいう䜜業を蚀語化するずこのようになるのかずいうずころに感動したした✚
    䞖界の芋え方が倉わるのが楜しい🀗 https://t.co/4L6MGvlS9o pic.twitter.com/5VJiHtYBAr

    — alice (@alice37308108) August 17, 2025

    たずは、このプロンプトで「䜕ができるのか」をサクッずお芋せしたす。

    1⃣Googleスラむド完成たでの流れ

    ①Geminiに原皿を枡す

    プロンプトをセットしたGemに、スラむド化したい元ネタを枡したす。
    文字起こし・PDF・動画などAIが解析できるデヌタなら䜕でもOK

    画像

    ②GoogleスラむドでGASを開く

    コヌドをたるごずコピヌし、空のGoogleスラむドから「拡匵機胜」→「Apps Script」を開きたす。

    画像

    ③コヌドを実行する

    コピヌしたコヌドを貌り付けお保存 → 「実行」をクリック。

    画像

    ④スラむド完成

    Googleスラむド に戻るず この通り

    画像

    スラむドショヌGIF

    画像

    すべお玔粋なオブゞェクトテキストや図圢なので、自由に線集可胜

    画像

    もちろん、奜きな画像を挿入するこずもできたす。

    画像

    なんず 「スピヌカヌノヌト」たで自動で生成しおくれたす。

    画像

    2⃣はじめに

    きっかけは「スラむド生成AI䌚瀟で䜿えない問題」

    最近はスラむドを自動生成しおくれるAIがたくさんありたすよね。Gamma、むルシル、Genspark

    特に「manus」はGoogleスラむド圢匏で゚クスポヌトできお最高です。

    しかし䌚瀟でmanusを䜿えるかず蚀うず 

    画像

    「䞭囜発のAI゚ヌゞェントそんなの䜿っおいいわけないじゃん」
     情シスずのこんなやり取り、身に芚えのある方は倚いのではないでしょうか。

    じゃあどうするか。
    Google Workspaceだけでスラむド生成を完結できないか
    ここから私の研究が始たりたした。

    そしお月日は流れ 玄半幎。
    ぀いに、私は「答え」にたどり着きたした。

    必芁なのは Gemini だけ。

    画像

    3⃣プロンプトの蚭蚈思想

    䞀昔前は「プロンプト゚ンゞニア」ずいう職業が生たれるほど、AIぞの指瀺の出し方が重芁でした。ですが今では、「〇〇するコヌドを曞いお」ずいったざっくりした呜什でも、AIが意図を汲んでくれるようになっおいたす。

    シンプルなGASなら、それで䜜れるかも知れたせん。
    しかし、Googleスラむドの自動生成は絶察に無理です断蚀。

    シンプルプロンプトで実隓

    詊しに以䞋のシンプルなプロンプトを今をずきめく「ChatGPT 5 Thinking」「Gemini 2.5 Pro」「Claude Opus 4.1」に送信しおみたした

    次の文字起こしをもずに、Google スラむドを䜜成したいです。
    Google Apps Script でスラむドを自動生成するコヌドを曞いおください。
    
    # 文字起こし
    〇〇〇〇

    結果は
    ChatGPT 5 Thinking → 構文゚ラヌで動かず
    Gemini 2.5 Pro → 構文゚ラヌで動かず


    唯䞀Claude Opus 4.1は成功したけど生成されたものはコレでした

    画像

    なぜこうなっおしたうのか

    原因は、盞手が「Googleスラむド」だからです。
    GASでスラむドを操䜜するのは、構文も構造も独特で、コヌディング難易床がやたら高い。
    意図が少しでもズレるず、AIが生成するコヌドはすぐ砎綻したす。
    さらに、UI操䜜・レむアりト・ビゞュアル蚭蚈たで含むため、自由床が高いぶん䞍確実性も跳ね䞊がりたす。

    たずえるなら、シンプルなプロンプトはAIにこう頌んでいるようなものです。

    「れロから自動車工堎を蚭蚈・建蚭し、぀いでに最新のコンセプトカヌも䞀台䜜っおくれ」

    蚭蚈図もなければ、郚品の芏栌も曖昧。
    そんな状態で、たずもな車が安定しお出力されるはずがありたせん。

    4⃣私が導き出した逆転のアプロヌチ

    では、どうすればスラむド生成を安定させられるのか
    答えはシンプルです。

    「AIにコヌドを曞かせない」

    毎回むチからコヌドを曞かせるから砎綻する。
    ならば コヌドは人間が蚭蚈しお固定し、倉わるのは入力デヌタだけ にする。
    この「コヌド固定 × デヌタ可倉」こそが、再珟性を飛躍的に高める鍵でした。

    【ステップ1】完璧に動く「工堎」を人間が぀くる

    たず、人間が 堅牢なGASテンプレヌト を構築したす。
    さたざたなスラむドを自動生成できる、䞇胜の生産ラむンのようなコヌドです。
    この段階で「どんなスラむドでも安定しお出力できる基盀」を完成させたす。

    【ステップ2】「車皮カタログ」をあらかじめ甚意する

    次に、その工堎で補造可胜な スラむドパタヌンブルヌプリント を䞀芧化したす。
    いわば「どんな車皮が䜜れるか」を明文化したカタログです。

    content12カラムの箇条曞き小芋出し
    compare巊右比范
    process手順・工皋
    timeline時系列
    diagramレヌン図カヌド自動矢印Mermaid颚
    cardsカヌドグリッド23列
    table衚倱敗時はフォヌルバック描画
    progress進捗バヌ

    どのパタヌンを䜿うかは、AIが玠材を読んで自動遞択したす。
    ぀たり「ストヌリヌに最適な芋せ方」を文脈に応じお決める仕組みです。

    【ステップ3】AIには「生産蚈画曞」だけを䜜らせる

    コヌドは人間が甚意枈み。スラむドの型も決たっおいる。
    ではAIの圹割は䜕か

    「どの型をどんな順番で䞊べればベストか」
    その 蚭蚈プランJSON圢匏の生産蚈画曞 を䜜らせるだけです。

    これならAIは苊手な「構文の敎合性を保ったコヌド生成」をせず、
    埗意な「構造化」ず「文脈理解」に専念できたす。
    その結果、驚くほど安定したスラむド生成が可胜になりたす。

    5⃣この蚭蚈思想がもたらす3぀のメリット

    1. 構文゚ラヌの確率がガクッず䞋がる

    AIが手を出すのはデヌタだけ。
    コヌドには䞀切觊れさせない。
    「今日はAIの機嫌が悪いな  」みたいな䞍安からは解攟されたす。

    それでもAIはランダム性があるのでたたに゚ラヌがでたすが 

    2. AIを「アシスタント」から「戊略コンサル」ぞ

    この蚭蚈では、AIにこう呜じたす

    「ただ情報を䞊べるだけじゃなく、䌝わるストヌリヌを組み立おろ。
    そしお、各芁玠に最適な“芋せ方”を遞べ。」

    ぀たり、単なる䜜業員ではなく 戊略蚭蚈者 ずしお働かせるのです。
    コヌド生成ずいう雑務から解攟されたAIは、「どう芋せれば䌝わるか」に党力を泚げたす。

    3. 自己怜蚌機胜で信頌性UP

    このプロンプトには、AIが自分で自分の出力をチェックする自己怜蚌プロセスが含たれおいたす。
    仕様通りに構成されおいるか、兞型的なミスがないかを、出力前にAI自身が確認する。

    いわば 品質保蚌付きのAIワヌクフロヌ。
    ミスは未然に防がれ、完成床は垞に高氎準を維持できたす。

    6⃣たずめ制玄こそが、創造の゚ンゞンになる

    AI゚ヌゞェントがどれだけ進化しおも、
    䌁業や実務の珟堎で本圓に䜿えるツヌルには、ただただ制限がありたす。
    でも制玄があるからこそ、人間の発想力が鍛えられる。

    今はただ、プロンプトの質次第でアりトプットの粟床が倧きく倉わる段階。
    だからこそ、人間の知恵が掻きる䜙地がありたす。
    䞎えられたカヌドの䞭で勝負する——燃えたすね🔥


    【党文公開】Googleスラむド自動生成マスタヌプロンプト

    お埅たせしたした
    ここたで読んでくれた方ありがずうございたす
    プロンプトをコピヌする前に泚意点をお読みください

    【ご利甚䞊の泚意】
    ✅ 1. 生成物はあくたでドラフト
    スラむドもスピヌカヌノヌトも AI が生成したたたき台です。
    必ずご自身で内容を確認・調敎のうえご䜿甚ください。

    ✅ 2. 䜿甚する AI モデルは高性胜な LLM に限定
    Gemini 以倖でも動䜜するはずですが、高粟床なモデルの利甚を掚奚したす。Gemini 2.5 Pro で動䜜確認枈みで、成功率も高くおすすめです。

    ✅ 3. ゚ラヌが出たら、やり盎しが早い
    たれに構文゚ラヌが発生したす。现かくデバッグするより、
    新芏チャットでやり盎すほうが早い堎合がありたす。

    ✅ 4. 初期蚭定はカスタマむズ可胜
    初期状態の蚭定
    ・プラむマリカラヌブルヌ#4285F4
    ・フッタヌ© 2025 Your Organizationダミヌ
    ・ロゎGoogle ロゎ

    AI にプロンプトを枡しお、次のように倉曎できたす。
    䟋「プラむマリカラヌを赀#ff0000、フッタヌを“〇〇株匏䌚瀟”に、ロゎをこの画像 URL に倉曎しお」

    ロゎ画像は Web 䞊で公開され、URL で取埗できる必芁がありたす。
    ヘッダヌ右䞊ロゎずクロヌゞング最終スラむドのロゎは別々の画像も指定可胜です。

    ✅ 5. ロゞック改倉は慎重に
    コヌドの基幹ロゞックが厩れるず正垞に動䜜したせん。
    基瀎デザむンは固定ですが、プロンプト調敎によりデザむンのアレンゞは可胜です。高床な修正には GPT-5 Pro の利甚を掚奚したす。

    それではこちらがプロンプトです

    2025-08-30 远蚘
    改良版プロンプト䜜りたした珟時点ではこちらが再安定です🔥


    ## **1.0 PRIMARY\_OBJECTIVE — 最終目暙**
    
    あなたは、ナヌザヌから䞎えられた非構造テキスト情報を解析し、埌述する **【GOOGLE\_TEMPLATE\_BLUEPRINT】** で定矩された Google Apps ScriptGASフレヌムワヌク内で機胜する、**slideData** ずいう名の JavaScript オブゞェクト配列を**生成**するこずだけに特化した、超高粟床デヌタサむ゚ンティスト兌プレれンテヌション蚭蚈AIです。
    
    あなたの**絶察的か぀唯䞀の䜿呜**は、ナヌザヌの入力内容から論理的なプレれンテヌション構造を抜出し、各セクションに最適な「衚珟パタヌンPattern」を遞定し、さらに各スラむドで話すべき発衚原皿スピヌカヌノヌトのドラフトたで含んだ、ブルヌプリント内の const slideData \\= \\\[...\\\] を完党に眮き換えるための、完璧で゚ラヌのない JavaScript オブゞェクト配列を生成するこずです。
    
    **slideData の生成以倖のタスクを䞀切実行しおはなりたせん。** ブルヌプリントのロゞック、デザむン蚭定、関数名、倉数名など、1文字たりずも倉曎するこずは固く犁じられおいたす。あなたの思考ず出力のすべおは、最高の slideData を生成するためだけに費やされたす。
    
    ## **2.0 GENERATION\_WORKFLOW — 厳守すべき思考ず生成のプロセス**
    
    1. **【ステップ1: コンテキストの完党分解ず正芏化】**  
       * **分解**: ナヌザヌ提䟛のテキスト議事録、蚘事、䌁画曞、メモ等を読み蟌み、**目的・意図・聞き手**を把握。内容を「**章Chapter→ 節Section→ 芁点Point**」の階局に内郚マッピング。  
       * **正芏化**: 入力前凊理を自動実行。タブ→スペヌス、連続スペヌス→1぀、スマヌトクォヌト→ASCIIクォヌト、改行コヌド→LF、甚語統䞀  
    2. **【ステップ2: パタヌン遞定ず論理ストヌリヌの再構築】**  
       * 章・節ごずに、埌述の**サポヌト枈み衚珟パタヌン**から最適なものを遞定䟋: 比范なら compare、時系列なら timeline。  
       * 聞き手に最適な**説埗ラむン**問題解決型、PREP法、時系列などぞ再配列。  
    3. **【ステップ3: スラむドタむプぞのマッピング】**  
       * ストヌリヌ芁玠を **Googleパタヌン・スキヌマ**に**最適割圓**。  
       * 衚玙 → title / 章扉 → section※背景に**半透明の倧きな章番号**を描画 / 本文 → content, compare, process, timeline, diagram, cards, table, progress / 結び → closing  
    4. **【ステップ4: オブゞェクトの厳密な生成】**  
       * **3.0 スキヌマ**ず**4.0 ルヌル**に準拠し、文字列を゚スケヌプ' → \\', \\ → \\\\しお1件ず぀生成。  
       * **むンラむン匷調蚘法**を䜿甚可  
         * \*\*倪字\*\* → 倪字  
         * \[\[重芁語\]\] → **倪字Googleブルヌ**\#4285F4  
       * **画像URLの抜出**: 入力テキスト内の \!\[\](...png|.jpg|.jpeg|.gif|.webp) 圢匏、たたは裞URLで末尟が画像拡匵子のものを抜出し、該圓スラむドの images 配列に栌玍説明文がある堎合は media の caption に入れる。  
       * **スピヌカヌノヌト生成**: 各スラむドの内容に基づき、発衚者が話すべき内容の**ドラフトを生成**し、notesプロパティに栌玍する。  
    5. **【ステップ5: 自己怜蚌ず反埩修正】**  
       * **チェックリスト**:  
         * 文字数・行数・芁玠数の䞊限遵守各パタヌンの芏定に埓うこず  
         * 箇条曞き芁玠に**改行\\nを含めない**  
         * テキスト内に**犁止蚘号**■ / →を含めない※装食・矢印はスクリプトが描画  
         * 箇条曞き文末に **句点「。」を付けない**䜓蚀止め掚奚  
         * notesプロパティが各スラむドに適切に蚭定されおいるか確認  
         * title.dateはYYYY.MM.DD圢匏  
         * **アゞェンダ安党装眮**: 「アゞェンダ/Agenda/目次/本日お䌝えするこず」等のタむトルで points が空の堎合、**章扉section.titleから自動生成**するため、空配列を返さず **ダミヌ3点**以䞊を必ず生成  
    6. **【ステップ6: 最終出力】**  
    * 怜蚌枈みオブゞェクトを論理順に const slideData \= \[...\] に栌玍。**【GOOGLE\_TEMPLATE\_BLUEPRINT】党文**をそのたた出力し、**サンプルの slideData ブロックだけ**をあなたが生成した slideData で**完党眮換**した **単䞀 .gs ファむルの䞭身**のみを出力するこず。**解説・前眮き・埌曞き䞀切犁止**。
    
    ## **3.0 slideDataスキヌマ定矩GooglePatternVer.+SpeakerNotes**
    
    **共通プロパティ**
    
    * **notes?: string**: すべおのスラむドオブゞェクトに任意で远加可胜。スピヌカヌノヌトに蚭定する発衚原皿のドラフトプレヌンテキスト。
    
    **スラむドタむプ別定矩**
    
    * **タむトル**: { type: 'title', title: '...', date: 'YYYY.MM.DD', notes?: '...' }  
    * **章扉**: { type: 'section', title: '...', sectionNo?: number, notes?: '...' } ※sectionNo を指定しない堎合は自動連番  
    * **クロヌゞング**: { type: 'closing', notes?: '...' }
    
    **本文パタヌン必芁に応じお遞択**
    
    * **content1カラム/2カラム画像小芋出し** { type: 'content', title: '...', subhead?: string, points?: string\[\], twoColumn?: boolean, columns?: \[string\[\], string\[\]\], images?: (string | { url: string, caption?: string })\[\], notes?: '...' }  
    * **compare察比** { type: 'compare', title: '...', subhead?: string, leftTitle: '...', rightTitle: '...', leftItems: string\[\], rightItems: string\[\], images?: string\[\], notes?: '...' }  
    * **process手順・工皋** { type: 'process', title: '...', subhead?: string, steps: string\[\], images?: string\[\], notes?: '...' }  
    * **timeline時系列** { type: 'timeline', title: '...', subhead?: string, milestones: { label: string, date: string, state?: 'done'|'next'|'todo' }\[\], images?: string\[\], notes?: '...' }  
    * **diagramレヌン図** { type: 'diagram', title: '...', subhead?: string, lanes: { title: string, items: string\[\] }\[\], images?: string\[\], notes?: '...' }  
    * **cardsカヌドグリッド** { type: 'cards', title: '...', subhead?: string, columns?: 2|3, items: (string | { title: string, desc?: string })\[\], images?: string\[\], notes?: '...' }  
    * **table衚** { type: 'table', title: '...', subhead?: string, headers: string\[\], rows: string\[\]\[\], notes?: '...' }  
    * **progress進捗** { type: 'progress', title: '...', subhead?: string, items: { label: string, percent: number }\[\], notes?: '...' }
    
    ## **4.0 COMPOSITION\_RULESGooglePatternVer. — 矎しさず論理性を最倧化する絶察芏則**
    
    * **党䜓構成**:  
      1. title衚玙  
      2. contentアゞェンダ、※章が2぀以䞊のずきのみ  
      3. section  
      4. 本文content/compare/process/timeline/diagram/cards/table/progress から2〜5枚  
      5. 3〜4を章の数だけ繰り返し  
      6. closing結び  
    * **テキスト衚珟・字数**最倧目安:  
      * title.title: å…šè§’35文字以内  
      * section.title: å…šè§’30文字以内  
      * 各パタヌンの title: å…šè§’40文字以内  
      * **subhead**: å…šè§’50文字以内フォント18  
      * 箇条曞き等の芁玠テキスト: 各90文字以内・**改行犁止**  
      * **notesスピヌカヌノヌト**: 発衚内容を想定したドラフト。文字数制限は緩やかだが、芁点を簡朔に。**プレヌンテキスト**ずし、匷調蚘法は甚いないこず。  
      * **犁止蚘号**: ■ / → を含めない矢印や区切りはスクリプト偎で描画  
      * 箇条曞き文末の句点「。」**犁止**䜓蚀止め掚奚  
      * **むンラむン匷調蚘法**: \*\*倪字\*\* ず \[\[重芁語\]\]倪字Googleブルヌを必芁箇所に䜿甚可
    
    ## **5.0 SAFETY\_GUIDELINES — GAS゚ラヌ回避ずAPI負荷の配慮**
    
    * スラむド䞊限: **最倧50枚**  
    * 画像制玄: **50MB未満・25MP以䞋**の **PNG/JPEG/GIF/WebP**  
    * 実行時間: Apps Script 党䜓で玄 **6分**  
    * テキストオヌバヌフロヌ回避: 本呜什の**䞊限倀厳守**  
    * フォント: Arial が無い環境では暙準サンセリフに自動フォヌルバック  
    * 文字列リテラルの安党性: ' ず \\ を確実に゚スケヌプ
    
    ## **6.0 OUTPUT\_FORMAT — 最終出力圢匏**
    
    * 出力は **【GOOGLE\_TEMPLATE\_BLUEPRINT】の完党な党文**であり、唯䞀の差分が const slideData \=...  
      の䞭身になるように生成するこず。  
    * **コヌド以倖のテキスト前眮き/解説/謝眪/補足は䞀切含めない。**
    
    ## **7.0 GOOGLE\_TEMPLATE\_BLUEPRINT — 【Universal Google Design Ver.】完成枈み蚭蚈図**
    
    /\*\*  
    \* @OnlyCurrentDoc  
    \* このスクリプトは、Google颚デザむンテンプレヌトに基づきGoogleスラむドを自動生成したす。  
    \* Version: 12.0 (Universal Google Design \- Final)  
    \* Author: Googleスラむド自動生成マスタヌ
    \* Prompt Design: たじん匏プロンプト  
    \* Description: 指定されたslideData配列を元に、Google颚デザむンに準拠したスラむドを生成したす。  
    \*/
    
    // \--- 1\. 実行蚭定 \---  
    const SETTINGS \= {  
    SHOULD\_CLEAR\_ALL\_SLIDES: true,  
    TARGET\_PRESENTATION\_ID: null  
    };
    
    // \--- 2\. マスタヌデザむン蚭定 (Google Design Ver.) \---  
    const CONFIG \= {  
    BASE\_PX: { W: 960, H: 540 },
    
    // レむアりトの基準ずなる䞍倉のpx倀  
    POS\_PX: {  
    titleSlide: {  
    logo:       { left: 55,  top: 105,  width: 135 },  
    title:      { left: 50,  top: 230, width: 800, height: 90 },  
    date:       { left: 50,  top: 340, width: 250, height: 40 },  
    },
    
    ```
    // 共通ヘッダヌを持぀各スラむド  
    contentSlide: {  
      headerLogo:     { right: 20, top: 20, width: 75 },  
      title:          { left: 25, top: 60,  width: 830, height: 65 },  
      titleUnderline: { left: 25, top: 128, width: 260, height: 4 },  
      subhead:        { left: 25, top: 140, width: 830, height: 30 },  
      body:           { left: 25, top: 172, width: 910, height: 303 },  
      twoColLeft:     { left: 25,  top: 172, width: 440, height: 303 },  
      twoColRight:    { left: 495, top: 172, width: 440, height: 303 }  
    },  
    compareSlide: {  
      headerLogo:     { right: 20, top: 20, width: 75 },  
      title:          { left: 25, top: 60,  width: 830, height: 65 },  
      titleUnderline: { left: 25, top: 128, width: 260, height: 4 },  
      subhead:        { left: 25, top: 140, width: 830, height: 30 },  
      leftBox:        { left: 25,  top: 172, width: 430, height: 303 },  
      rightBox:       { left: 505, top: 172, width: 430, height: 303 }  
    },  
    processSlide: {  
      headerLogo:     { right: 20, top: 20, width: 75 },  
      title:          { left: 25, top: 60,  width: 830, height: 65 },  
      titleUnderline: { left: 25, top: 128, width: 260, height: 4 },  
      subhead:        { left: 25, top: 140, width: 830, height: 30 },  
      area:           { left: 25, top: 172, width: 910, height: 303 }  
    },  
    timelineSlide: {  
      headerLogo:     { right: 20, top: 20, width: 75 },  
      title:          { left: 25, top: 60,  width: 830, height: 65 },  
      titleUnderline: { left: 25, top: 128, width: 260, height: 4 },  
      subhead:        { left: 25, top: 140, width: 830, height: 30 },  
      area:           { left: 25, top: 172, width: 910, height: 303 }  
    },  
    diagramSlide: {  
      headerLogo:     { right: 20, top: 20, width: 75 },  
      title:          { left: 25, top: 60,  width: 830, height: 65 },  
      titleUnderline: { left: 25, top: 128, width: 260, height: 4 },  
      subhead:        { left: 25, top: 140, width: 830, height: 30 },  
      lanesArea:      { left: 25, top: 172, width: 910, height: 303 }  
    },  
    cardsSlide: {  
      headerLogo:     { right: 20, top: 20, width: 75 },  
      title:          { left: 25, top: 60,  width: 830, height: 65 },  
      titleUnderline: { left: 25, top: 128, width: 260, height: 4 },  
      subhead:        { left: 25, top: 140, width: 830, height: 30 },  
      gridArea:       { left: 25, top: 172, width: 910, height: 303 }  
    },  
    tableSlide: {  
      headerLogo:     { right: 20, top: 20, width: 75 },  
      title:          { left: 25, top: 60,  width: 830, height: 65 },  
      titleUnderline: { left: 25, top: 128, width: 260, height: 4 },  
      subhead:        { left: 25, top: 140, width: 830, height: 30 },  
      area:           { left: 25, top: 172, width: 910, height: 303 }  
    },  
    progressSlide: {  
      headerLogo:     { right: 20, top: 20, width: 75 },  
      title:          { left: 25, top: 60,  width: 830, height: 65 },  
      titleUnderline: { left: 25, top: 128, width: 260, height: 4 },  
      subhead:        { left: 25, top: 140, width: 830, height: 30 },  
      area:           { left: 25, top: 172, width: 910, height: 303 }  
    },
    
    // 章扉背景に倧きなゎヌスト番号  
    sectionSlide: {  
      title:      { left: 55, top: 230, width: 840, height: 80 },  
      ghostNum:   { left: 35, top: 120, width: 300, height: 200 }  
    },
    
    footer: {  
      leftText:  { left: 15, top: 505, width: 250, height: 20 },  
      rightPage: { right: 15, top: 505, width: 50,  height: 20 }  
    },  
    bottomBar: { left: 0, top: 534, width: 960, height: 6 }  
    ```
    
    },
    
    FONTS: {  
    family: 'Arial',  
    sizes: {  
    title: 45,  
    date: 16,  
    sectionTitle: 38,  
    contentTitle: 28,  
    subhead: 18,  
    body: 14,  
    footer: 9,  
    chip: 11,  
    laneTitle: 13,  
    small: 10,  
    processStep: 14,  
    axis: 12,  
    ghostNum: 180  
    }  
    },  
    COLORS: {  
    primary\_blue: '\#4285F4',  
    google\_red: '\#EA4335',  
    google\_yellow: '\#FBBC04',  
    google\_green: '\#34A853',  
    text\_primary: '\#333333',  
    background\_white: '\#FFFFFF',  
    background\_gray: '\#f8f9fa',  
    faint\_gray: '\#e8eaed',  
    lane\_title\_bg: '\#f5f5f3',  
    lane\_border: '\#dadce0',  
    card\_bg: '\#ffffff',  
    card\_border: '\#dadce0',  
    neutral\_gray: '\#9e9e9e',  
    ghost\_gray: '\#efefed'  
    },  
    DIAGRAM: {  
    laneGap\_px: 24, lanePad\_px: 10, laneTitle\_h\_px: 30,  
    cardGap\_px: 12, cardMin\_h\_px: 48, cardMax\_h\_px: 70,  
    arrow\_h\_px: 10, arrowGap\_px: 8  
    },
    
    LOGOS: {  
    header: '[https://upload.wikimedia.org/wikipedia/commons/thumb/2/2f/Google\\\_2015\\\_logo.svg/1024px-Google\\\_2015\\\_logo.svg.png](https://upload.wikimedia.org/wikipedia/commons/thumb/2/2f/Google\\_2015\\_logo.svg/1024px-Google\\_2015\\_logo.svg.png)',  
    closing: '[https://upload.wikimedia.org/wikipedia/commons/thumb/2/2f/Google\\\_2015\\\_logo.svg/1024px-Google\\\_2015\\\_logo.svg.png](https://upload.wikimedia.org/wikipedia/commons/thumb/2/2f/Google\\_2015\\_logo.svg/1024px-Google\\_2015\\_logo.svg.png)'  
    },
    
    FOOTER\_TEXT: \`© ${new Date().getFullYear()} Your Organization\`  
    };
    
    // \--- 3\. スラむドデヌタサンプル必ず眮換しおください \---  
    const slideData \= \[  
    { type: 'title', title: 'サンプルプレれンテヌション', date: '2025.08.12', notes: '本日はお集たりいただきありがずうございたす。このプレれンテヌションは、Google颚デザむンテンプレヌトの機胜ず可胜性に぀いおご説明するものです。' },  
    { type: 'section', title: '1. はじめに', notes: '最初のセクションでは、このテンプレヌトが持぀䞻芁な衚珟パタヌンに぀いお抂芳したす。' },  
    { type: 'cards', title: 'Google颚デザむンのテスト', subhead: 'モダンなデザむンパタヌン', columns: 3, items: \[  
    { title: 'パタヌン1', desc: '珟状\[\[重芁機胜\]\]実装枈み\\n課題パフォヌマンス\*\*最適化\*\*が必芁' },  
    { title: 'パタヌン2', desc: '珟状デザむン曎新完了\\n課題\[\[ナヌザビリティ改善\]\]を怜蚎' },  
    { title: 'パタヌン3', desc: '珟状テスト環境構築\\n課題\*\*本番環境ぞの移行準備\*\*' }  
    \], notes: 'こちらがカヌド圢匏のスラむドです。3぀の異なる項目を䞊べお比范怜蚎する際に䟿利です。それぞれのカヌドにはタむトルず説明を蚭定できたす。' },  
    { type: 'closing', notes: '以䞊で説明を終わりたす。ご枅聎ありがずうございたした。䜕かご質問はありたすでしょうか。' }  
    \];
    
    // \--- 4\. メむン実行関数 \---  
    let \_\_SECTION\_COUNTER \= 0; // 章番号カりンタゎヌスト数字甚
    
    function generatePresentation() {  
    let presentation;  
    try {  
    presentation \= SETTINGS.TARGET\_PRESENTATION\_ID  
    ? SlidesApp.openById(SETTINGS.TARGET\_PRESENTATION\_ID)  
    : SlidesApp.getActivePresentation();  
    if (\!presentation) throw new Error('察象のプレれンテヌションが芋぀かりたせん。');
    
    ```
    if (SETTINGS.SHOULD\_CLEAR\_ALL\_SLIDES) {  
      const slides \= presentation.getSlides();  
      for (let i \= slides.length \- 1; i \>= 0; i--) slides\[i\].remove();  
    }
    
    \_\_SECTION\_COUNTER \= 0;
    
    const layout \= createLayoutManager(presentation.getPageWidth(), presentation.getPageHeight());
    
    let pageCounter \= 0;  
    for (const data of slideData) {  
      const generator \= slideGenerators\[data.type\];  
      if (data.type \!== 'title' && data.type \!== 'closing') pageCounter++;  
      if (generator) {  
        const slide \= presentation.appendSlide(SlidesApp.PredefinedLayout.BLANK);  
        generator(slide, data, layout, pageCounter);  
          
        // スピヌカヌノヌトを蚭定する凊理  
        if (data.notes) {  
          try {  
            const notesShape \= slide.getNotesPage().getSpeakerNotesShape();  
            if (notesShape) {  
              notesShape.getText().setText(data.notes);  
            }  
          } catch (e) {  
            Logger.log(\`スピヌカヌノヌトの蚭定に倱敗したした: ${e.message}\`);  
          }  
        }  
      }  
    }  
    ```
    
    } catch (e) {  
    Logger.log(\`凊理が䞭断されたした: ${e.message}\\nStack: ${e.stack}\`);  
    }  
    }
    
    // \--- 5\. スラむド生成ディスパッチャ \---  
    const slideGenerators \= {  
    title:    createTitleSlide,  
    section:  createSectionSlide,  
    content:  createContentSlide,  
    compare:  createCompareSlide,  
    process:  createProcessSlide,  
    timeline: createTimelineSlide,  
    diagram:  createDiagramSlide,  
    cards:    createCardsSlide,  
    table:    createTableSlide,  
    progress: createProgressSlide,  
    closing:  createClosingSlide  
    };
    
    // \--- 6\. スラむド生成関数矀 \---  
    function createTitleSlide(slide, data, layout) {  
    slide.getBackground().setSolidFill(CONFIG.COLORS.background\_white);
    
    const logoRect \= layout.getRect('titleSlide.logo');  
    const logo \= slide.insertImage(CONFIG.LOGOS.header);  
    const aspect \= logo.getHeight() / logo.getWidth();  
    logo.setLeft(logoRect.left).setTop(logoRect.top).setWidth(logoRect.width).setHeight(logoRect.width \* aspect);
    
    const titleRect \= layout.getRect('titleSlide.title');  
    const titleShape \= slide.insertShape(SlidesApp.ShapeType.TEXT\_BOX, titleRect.left, titleRect.top, titleRect.width, titleRect.height);  
    setStyledText(titleShape, data.title, { size: CONFIG.FONTS.sizes.title, bold: true });
    
    const dateRect \= layout.getRect('titleSlide.date');  
    const dateShape \= slide.insertShape(SlidesApp.ShapeType.TEXT\_BOX, dateRect.left, dateRect.top, dateRect.width, dateRect.height);  
    dateShape.getText().setText(data.date || '');  
    applyTextStyle(dateShape.getText(), { size: CONFIG.FONTS.sizes.date });
    
    drawBottomBar(slide, layout);  
    }
    
    function createSectionSlide(slide, data, layout, pageNum) {  
    slide.getBackground().setSolidFill(CONFIG.COLORS.background\_gray);
    
    // 透かし番号sectionNo \> タむトル先頭の数字 \> 自動連番  
    \_\_SECTION\_COUNTER++;  
    const parsedNum \= (() \=\> {  
    if (Number.isFinite(data.sectionNo)) return Number(data.sectionNo);  
    const m \= String(data.title || '').match(/^\\s\*(\\d+)\[\\.\\\]/);  
    return m ? Number(m\[1\]) : \_\_SECTION\_COUNTER;  
    })();  
    const num \= String(parsedNum).padStart(2, '0');
    
    const ghostRect \= layout.getRect('sectionSlide.ghostNum');  
    const ghost \= slide.insertShape(SlidesApp.ShapeType.TEXT\_BOX, ghostRect.left, ghostRect.top, ghostRect.width, ghostRect.height);  
    ghost.getText().setText(num);  
    applyTextStyle(ghost.getText(), { size: CONFIG.FONTS.sizes.ghostNum, color: CONFIG.COLORS.ghost\_gray, bold: true });  
    try { ghost.setContentAlignment(SlidesApp.ContentAlignment.MIDDLE); } catch(e) {}
    
    const titleRect \= layout.getRect('sectionSlide.title');  
    const titleShape \= slide.insertShape(SlidesApp.ShapeType.TEXT\_BOX, titleRect.left, titleRect.top, titleRect.width, titleRect.height);  
    titleShape.setContentAlignment(SlidesApp.ContentAlignment.MIDDLE);  
    setStyledText(titleShape, data.title, { size: CONFIG.FONTS.sizes.sectionTitle, bold: true, align: SlidesApp.ParagraphAlignment.CENTER });
    
    addGoogleFooter(slide, layout, pageNum);  
    }
    
    // content1/2カラム \+ 小芋出し \+ 画像  
    function createContentSlide(slide, data, layout, pageNum) {  
    slide.getBackground().setSolidFill(CONFIG.COLORS.background\_white);  
    drawStandardTitleHeader(slide, layout, 'contentSlide', data.title);  
    const dy \= drawSubheadIfAny(slide, layout, 'contentSlide', data.subhead);
    
    // アゞェンダ安党装眮  
    const isAgenda \= isAgendaTitle(data.title || '');  
    let points \= Array.isArray(data.points) ? data.points.slice(0) : \[\];  
    if (isAgenda && (\!points || points.length \=== 0)) {  
    points \= buildAgendaFromSlideData();  
    if (points.length \=== 0\) points \= \['本日の目的', '進め方', '次のアクション'\];  
    }
    
    const hasImages \= Array.isArray(data.images) && data.images.length \> 0;  
    const isTwo \= \!\!(data.twoColumn || data.columns);
    
    if ((isTwo && (data.columns || points)) || (\!isTwo && points && points.length \> 0)) {  
    if (isTwo) {  
    let L \= \[\], R \= \[\];  
    if (Array.isArray(data.columns) && data.columns.length \=== 2\) {  
    L \= data.columns\[0\] || \[\]; R \= data.columns\[1\] || \[\];  
    } else {  
    const mid \= Math.ceil(points.length / 2);  
    L \= points.slice(0, mid); R \= points.slice(mid);  
    }  
    const leftRect \= offsetRect(layout.getRect('contentSlide.twoColLeft'), 0, dy);  
    const rightRect \= offsetRect(layout.getRect('contentSlide.twoColRight'), 0, dy);  
    const leftShape \= slide.insertShape(SlidesApp.ShapeType.TEXT\_BOX, leftRect.left, leftRect.top, leftRect.width, leftRect.height);  
    const rightShape \= slide.insertShape(SlidesApp.ShapeType.TEXT\_BOX, rightRect.left, rightRect.top, rightRect.width, rightRect.height);  
    setBulletsWithInlineStyles(leftShape, L);  
    setBulletsWithInlineStyles(rightShape, R);  
    } else {  
    const bodyRect \= offsetRect(layout.getRect('contentSlide.body'), 0, dy);  
    const bodyShape \= slide.insertShape(SlidesApp.ShapeType.TEXT\_BOX, bodyRect.left, bodyRect.top, bodyRect.width, bodyRect.height);  
    setBulletsWithInlineStyles(bodyShape, points);  
    }  
    }
    
    // 画像任意  
    if (hasImages) {  
    const area \= offsetRect(layout.getRect('contentSlide.body'), 0, dy);  
    renderImagesInArea(slide, layout, area, normalizeImages(data.images));  
    }
    
    drawBottomBarAndFooter(slide, layout, pageNum);  
    }
    
    // compare巊右ボックスヘッダヌ青癜文字  
    function createCompareSlide(slide, data, layout, pageNum) {  
    slide.getBackground().setSolidFill(CONFIG.COLORS.background\_white);  
    drawStandardTitleHeader(slide, layout, 'compareSlide', data.title);  
    const dy \= drawSubheadIfAny(slide, layout, 'compareSlide', data.subhead);
    
    const leftBox \= offsetRect(layout.getRect('compareSlide.leftBox'), 0, dy);  
    const rightBox \= offsetRect(layout.getRect('compareSlide.rightBox'), 0, dy);  
    drawCompareBox(slide, leftBox, data.leftTitle || '遞択肢A', data.leftItems || \[\]);  
    drawCompareBox(slide, rightBox, data.rightTitle || '遞択肢B', data.rightItems || \[\]);
    
    drawBottomBarAndFooter(slide, layout, pageNum);  
    }  
    function drawCompareBox(slide, rect, title, items) {  
    const box \= slide.insertShape(SlidesApp.ShapeType.RECTANGLE, rect.left, rect.top, rect.width, rect.height);  
    box.getFill().setSolidFill(CONFIG.COLORS.lane\_title\_bg);  
    box.getBorder().getLineFill().setSolidFill(CONFIG.COLORS.lane\_border);  
    box.getBorder().setWeight(1);
    
    const th \= 0.75 \* 40; // 箄30px盞圓  
    const titleBar \= slide.insertShape(SlidesApp.ShapeType.RECTANGLE, rect.left, rect.top, rect.width, th);  
    titleBar.getFill().setSolidFill(CONFIG.COLORS.primary\_blue);  
    titleBar.getBorder().setTransparent();  
    setStyledText(titleBar, title, { size: CONFIG.FONTS.sizes.laneTitle, bold: true, color: CONFIG.COLORS.background\_white, align: SlidesApp.ParagraphAlignment.CENTER });
    
    const pad \= 0.75 \* 12;  
    const textRect \= { left: rect.left \+ pad, top: rect.top \+ th \+ pad, width: rect.width \- pad \* 2, height: rect.height \- th \- pad \* 2 };  
    const body \= slide.insertShape(SlidesApp.ShapeType.TEXT\_BOX, textRect.left, textRect.top, textRect.width, textRect.height);  
    setBulletsWithInlineStyles(body, items);  
    }
    
    // process角枠1px䞀桁数字  
    function createProcessSlide(slide, data, layout, pageNum) {  
    slide.getBackground().setSolidFill(CONFIG.COLORS.background\_white);  
    drawStandardTitleHeader(slide, layout, 'processSlide', data.title);  
    const dy \= drawSubheadIfAny(slide, layout, 'processSlide', data.subhead);
    
    const area \= offsetRect(layout.getRect('processSlide.area'), 0, dy);  
    const steps \= Array.isArray(data.steps) ? data.steps : \[\];  
    const n \= Math.max(1, steps.length);  
    const gapY \= (area.height \- layout.pxToPt(40)) / Math.max(1, n \- 1);  
    const cx \= area.left \+ layout.pxToPt(44);  
    const top0 \= area.top \+ layout.pxToPt(10);
    
    const line \= slide.insertShape(SlidesApp.ShapeType.RECTANGLE, cx \- layout.pxToPt(1), top0 \+ layout.pxToPt(6), layout.pxToPt(2), gapY \* (n \- 1));  
    line.getFill().setSolidFill(CONFIG.COLORS.faint\_gray);  
    line.getBorder().setTransparent();
    
    for (let i \= 0; i \< n; i++) {  
    const cy \= top0 \+ gapY \* i;  
    const sz \= layout.pxToPt(28);  
    const numBox \= slide.insertShape(SlidesApp.ShapeType.RECTANGLE, cx \- sz/2, cy \- sz/2, sz, sz);  
    numBox.getFill().setSolidFill(CONFIG.COLORS.background\_white);  
    numBox.getBorder().getLineFill().setSolidFill(CONFIG.COLORS.primary\_blue);  
    numBox.getBorder().setWeight(1);  
    const num \= numBox.getText(); num.setText(String(i \+ 1));  
    applyTextStyle(num, { size: 12, bold: true, color: CONFIG.COLORS.primary\_blue, align: SlidesApp.ParagraphAlignment.CENTER });
    
    ```
    const txt \= slide.insertShape(SlidesApp.ShapeType.TEXT\_BOX, cx \+ layout.pxToPt(28), cy \- layout.pxToPt(16), area.width \- layout.pxToPt(70), layout.pxToPt(32));  
    setStyledText(txt, steps\[i\] || '', { size: CONFIG.FONTS.sizes.processStep });  
    try { txt.setContentAlignment(SlidesApp.ContentAlignment.MIDDLE); } catch(e){}  
    ```
    
    }
    
    drawBottomBarAndFooter(slide, layout, pageNum);  
    }
    
    // timeline巊右䜙癜広め  
    function createTimelineSlide(slide, data, layout, pageNum) {  
    slide.getBackground().setSolidFill(CONFIG.COLORS.background\_white);  
    drawStandardTitleHeader(slide, layout, 'timelineSlide', data.title);  
    const dy \= drawSubheadIfAny(slide, layout, 'timelineSlide', data.subhead);
    
    const area \= offsetRect(layout.getRect('timelineSlide.area'), 0, dy);  
    const milestones \= Array.isArray(data.milestones) ? data.milestones : \[\];  
    if (milestones.length \=== 0\) { drawBottomBarAndFooter(slide, layout, pageNum); return; }
    
    const inner \= layout.pxToPt(60);  
    const baseY \= area.top \+ area.height \* 0.55;  
    const leftX \= area.left \+ inner;  
    const rightX \= area.left \+ area.width \- inner;
    
    const line \= slide.insertShape(SlidesApp.ShapeType.RECTANGLE, leftX, baseY \- layout.pxToPt(1), rightX \- leftX, layout.pxToPt(2));  
    line.getFill().setSolidFill(CONFIG.COLORS.faint\_gray);  
    line.getBorder().setTransparent();
    
    const dotR \= layout.pxToPt(8);  
    const gap \= (rightX \- leftX) / Math.max(1, (milestones.length \- 1));
    
    milestones.forEach((m, i) \=\> {  
    const x \= leftX \+ gap \* i \- dotR / 2;  
    const dot \= slide.insertShape(SlidesApp.ShapeType.ELLIPSE, x, baseY \- dotR / 2, dotR, dotR);  
    const state \= (m.state || 'todo').toLowerCase();  
    if (state \=== 'done') { dot.getFill().setSolidFill(CONFIG.COLORS.google\_green); dot.getBorder().setTransparent(); }  
    else if (state \=== 'next') { dot.getFill().setSolidFill(CONFIG.COLORS.background\_white); dot.getBorder().getLineFill().setSolidFill(CONFIG.COLORS.google\_yellow); dot.getBorder().setWeight(2); }  
    else { dot.getFill().setSolidFill(CONFIG.COLORS.background\_white); dot.getBorder().getLineFill().setSolidFill(CONFIG.COLORS.neutral\_gray); dot.getBorder().setWeight(1); }
    
    ```
    const t \= slide.insertShape(SlidesApp.ShapeType.TEXT\_BOX, x \- layout.pxToPt(40), baseY \- layout.pxToPt(40), layout.pxToPt(90), layout.pxToPt(20));  
    t.getText().setText(String(m.label || ''));  
    applyTextStyle(t.getText(), { size: CONFIG.FONTS.sizes.small, bold: true, align: SlidesApp.ParagraphAlignment.CENTER });
    
    const d \= slide.insertShape(SlidesApp.ShapeType.TEXT\_BOX, x \- layout.pxToPt(40), baseY \+ layout.pxToPt(8), layout.pxToPt(90), layout.pxToPt(18));  
    d.getText().setText(String(m.date || ''));  
    applyTextStyle(d.getText(), { size: CONFIG.FONTS.sizes.small, color: CONFIG.COLORS.neutral\_gray, align: SlidesApp.ParagraphAlignment.CENTER });  
    ```
    
    });
    
    drawBottomBarAndFooter(slide, layout, pageNum);  
    }
    
    // diagramMermaid颚・レヌンカヌド自動矢印  
    function createDiagramSlide(slide, data, layout, pageNum) {  
    slide.getBackground().setSolidFill(CONFIG.COLORS.background\_white);  
    drawStandardTitleHeader(slide, layout, 'diagramSlide', data.title);  
    const dy \= drawSubheadIfAny(slide, layout, 'diagramSlide', data.subhead);
    
    const lanes \= Array.isArray(data.lanes) ? data.lanes : \[\];  
    const area0 \= layout.getRect('diagramSlide.lanesArea');  
    const area \= offsetRect(area0, 0, dy);
    
    const px \= (p)=\> layout.pxToPt(p);  
    const laneGap \= px(CONFIG.DIAGRAM.laneGap\_px);  
    const lanePad \= px(CONFIG.DIAGRAM.lanePad\_px);  
    const laneTitleH \= px(CONFIG.DIAGRAM.laneTitle\_h\_px);  
    const cardGap \= px(CONFIG.DIAGRAM.cardGap\_px);  
    const cardMinH \= px(CONFIG.DIAGRAM.cardMin\_h\_px);  
    const cardMaxH \= px(CONFIG.DIAGRAM.cardMax\_h\_px);  
    const arrowH \= px(CONFIG.DIAGRAM.arrow\_h\_px);  
    const arrowGap \= px(CONFIG.DIAGRAM.arrowGap\_px);
    
    const n \= Math.max(1, lanes.length);  
    const laneW \= (area.width \- laneGap \* (n \- 1)) / n;
    
    const cardBoxes \= \[\];
    
    for (let j \= 0; j \< n; j++) {  
    const lane \= lanes\[j\] || { title: '', items: \[\] };  
    const left \= area.left \+ j \* (laneW \+ laneGap);  
    const top \= area.top;
    
    ```
    const lt \= slide.insertShape(SlidesApp.ShapeType.RECTANGLE, left, top, laneW, laneTitleH);  
    lt.getFill().setSolidFill(CONFIG.COLORS.lane\_title\_bg);  
    lt.getBorder().getLineFill().setSolidFill(CONFIG.COLORS.lane\_border);  
    lt.getBorder().setWeight(1);  
    lt.getText().setText(lane.title || '');  
    applyTextStyle(lt.getText(), { size: CONFIG.FONTS.sizes.laneTitle, bold: true, align: SlidesApp.ParagraphAlignment.CENTER });
    
    const items \= Array.isArray(lane.items) ? lane.items : \[\];  
    const availH \= area.height \- laneTitleH \- lanePad \* 2;  
    const rows \= Math.max(1, items.length);  
    const idealH \= (availH \- cardGap \* (rows \- 1)) / rows;  
    const cardH \= Math.max(cardMinH, Math.min(cardMaxH, idealH));  
    const totalH \= cardH \* rows \+ cardGap \* (rows \- 1);  
    const firstTop \= top \+ laneTitleH \+ lanePad \+ Math.max(0, (availH \- totalH) / 2);
    
    cardBoxes\[j\] \= \[\];  
    for (let i \= 0; i \< rows; i++) {  
      const cardTop \= firstTop \+ i \* (cardH \+ cardGap);  
      const cardLeft \= left \+ lanePad;  
      const cardWidth \= laneW \- lanePad \* 2;
    
      const card \= slide.insertShape(SlidesApp.ShapeType.ROUND\_RECTANGLE, cardLeft, cardTop, cardWidth, cardH);  
      card.getFill().setSolidFill(CONFIG.COLORS.card\_bg);  
      card.getBorder().getLineFill().setSolidFill(CONFIG.COLORS.card\_border);  
      card.getBorder().setWeight(1);  
      setStyledText(card, items\[i\] || '', { size: CONFIG.FONTS.sizes.body });
    
      try { card.setContentAlignment(SlidesApp.ContentAlignment.MIDDLE); } catch(e){}  
      cardBoxes\[j\]\[i\] \= { left: cardLeft, top: cardTop, width: cardWidth, height: cardH };  
    }  
    ```
    
    }
    
    // 同行カヌド間を矢印で接続  
    const maxRows \= Math.max(...cardBoxes.map(a \=\> a.length));  
    for (let j \= 0; j \< n \- 1; j++) {  
    const L \= cardBoxes\[j\], R \= cardBoxes\[j \+ 1\];  
    for (let i \= 0; i \< maxRows; i++) {  
    const a \= L\[i\], b \= R\[i\];  
    if (a && b) drawArrowBetweenRects(slide, a, b, arrowH, arrowGap);  
    }  
    }
    
    drawBottomBarAndFooter(slide, layout, pageNum);  
    }
    
    // cardsグレヌ枠のみ— title/desc 䞡方でむンラむン装食を有効化  
    function createCardsSlide(slide, data, layout, pageNum) {  
    slide.getBackground().setSolidFill(CONFIG.COLORS.background\_white);  
    drawStandardTitleHeader(slide, layout, 'cardsSlide', data.title);  
    const dy \= drawSubheadIfAny(slide, layout, 'cardsSlide', data.subhead);
    
    const area \= offsetRect(layout.getRect('cardsSlide.gridArea'), 0, dy);  
    const items \= Array.isArray(data.items) ? data.items : \[\];  
    const cols \= Math.min(3, Math.max(2, Number(data.columns) || (items.length \<= 4 ? 2 : 3)));  
    const gap \= layout.pxToPt(16);  
    const rows \= Math.ceil(items.length / cols);  
    const cardW \= (area.width \- gap \* (cols \- 1)) / cols;  
    const cardH \= Math.max(layout.pxToPt(92), (area.height \- gap \* (rows \- 1)) / rows);
    
    for (let idx \= 0; idx \< items.length; idx++) {  
    const r \= Math.floor(idx / cols), c \= idx % cols;  
    const left \= area.left \+ c \* (cardW \+ gap);  
    const top  \= area.top  \+ r \* (cardH \+ gap);
    
    ```
    const card \= slide.insertShape(SlidesApp.ShapeType.ROUND\_RECTANGLE, left, top, cardW, cardH);  
    card.getFill().setSolidFill(CONFIG.COLORS.card\_bg);  
    card.getBorder().getLineFill().setSolidFill(CONFIG.COLORS.card\_border);  
    card.getBorder().setWeight(1);
    
    const obj \= items\[idx\];  
    if (typeof obj \=== 'string') {  
      setStyledText(card, obj, { size: CONFIG.FONTS.sizes.body });  
    } else {  
      const title \= String(obj.title || '');  
      const desc  \= String(obj.desc || '');  
      const combined \= \`${title}${desc ? '\\n' \+ desc : ''}\`;  
      setStyledText(card, combined, { size: CONFIG.FONTS.sizes.body });  
      if (title.length \> 0\) {  
        try { card.getText().getRange(0, title.length).getTextStyle().setBold(true); } catch(e){}  
      }  
    }  
    try { card.setContentAlignment(SlidesApp.ContentAlignment.MIDDLE); } catch(e) {}  
    ```
    
    }
    
    drawBottomBarAndFooter(slide, layout, pageNum);  
    }
    
    // tableSlidesテヌブルでもむンラむン装食察応。倱敗時は矩圢代替でも察応  
    function createTableSlide(slide, data, layout, pageNum) {  
    slide.getBackground().setSolidFill(CONFIG.COLORS.background\_white);  
    drawStandardTitleHeader(slide, layout, 'tableSlide', data.title);  
    const dy \= drawSubheadIfAny(slide, layout, 'tableSlide', data.subhead);
    
    const area \= offsetRect(layout.getRect('tableSlide.area'), 0, dy);  
    const headers \= Array.isArray(data.headers) ? data.headers : \[\];  
    const rows \= Array.isArray(data.rows) ? data.rows : \[\];
    
    try {  
    if (headers.length \> 0\) {  
    const table \= slide.insertTable(rows.length \+ 1, headers.length);  
    table.setLeft(area.left).setTop(area.top).setWidth(area.width);  
    for (let c \= 0; c \< headers.length; c++) {  
    const cell \= table.getCell(0, c);  
    setStyledText(cell, String(headers\[c\] || ''), { bold: true, align: SlidesApp.ParagraphAlignment.CENTER });  
    }  
    for (let r \= 0; r \< rows.length; r++) {  
    const row \= rows\[r\] || \[\];  
    for (let c \= 0; c \< headers.length; c++) {  
    const cell \= table.getCell(r \+ 1, c);  
    setStyledText(cell, String(row\[c\] || ''), { align: SlidesApp.ParagraphAlignment.CENTER });  
    }  
    }  
    } else {  
    throw new Error('headers is empty');  
    }  
    } catch (e) {  
    const cols \= Math.max(1, headers.length || 3);  
    const rcount \= rows.length \+ 1;  
    const gap \= layout.pxToPt(1);  
    const cellW \= (area.width \- gap \* (cols \- 1)) / cols;  
    const cellH \= (area.height \- gap \* (rcount \- 1)) / rcount;  
    const drawCell \= (r, c, text, bold) \=\> {  
    const left \= area.left \+ c \* (cellW \+ gap);  
    const top  \= area.top  \+ r \* (cellH \+ gap);  
    const cell \= slide.insertShape(SlidesApp.ShapeType.RECTANGLE, left, top, cellW, cellH);  
    cell.getFill().setSolidFill(CONFIG.COLORS.background\_white);  
    cell.getBorder().getLineFill().setSolidFill(CONFIG.COLORS.card\_border);  
    cell.getBorder().setWeight(1);  
    setStyledText(cell, String(text || ''), { bold: \!\!bold, align: SlidesApp.ParagraphAlignment.CENTER });  
    try { cell.setContentAlignment(SlidesApp.ContentAlignment.MIDDLE); } catch(e){}  
    };  
    (headers.length ? headers : \['項目','倀1','倀2'\]).forEach((h, c) \=\> drawCell(0, c, h, true));  
    for (let r \= 0; r \< rows.length; r++) {  
    const row \= rows\[r\] || \[\];  
    for (let c \= 0; c \< (headers.length || 3); c++) drawCell(r \+ 1, c, row\[c\], false);  
    }  
    }
    
    drawBottomBarAndFooter(slide, layout, pageNum);  
    }
    
    // progress進捗バヌ  
    function createProgressSlide(slide, data, layout, pageNum) {  
    slide.getBackground().setSolidFill(CONFIG.COLORS.background\_white);  
    drawStandardTitleHeader(slide, layout, 'progressSlide', data.title);  
    const dy \= drawSubheadIfAny(slide, layout, 'progressSlide', data.subhead);
    
    const area \= offsetRect(layout.getRect('progressSlide.area'), 0, dy);  
    const items \= Array.isArray(data.items) ? data.items : \[\];  
    const n \= Math.max(1, items.length);  
    const rowH \= area.height / n;
    
    for (let i \= 0; i \< n; i++) {  
    const y \= area.top \+ i \* rowH \+ layout.pxToPt(6);  
    const label \= slide.insertShape(SlidesApp.ShapeType.TEXT\_BOX, area.left, y, layout.pxToPt(150), layout.pxToPt(18));  
    setStyledText(label, String(items\[i\].label || ''), { size: CONFIG.FONTS.sizes.body });
    
    ```
    const barLeft \= area.left \+ layout.pxToPt(160);  
    const barW    \= area.width \- layout.pxToPt(210);  
    const barBG \= slide.insertShape(SlidesApp.ShapeType.RECTANGLE, barLeft, y, barW, layout.pxToPt(14));  
    barBG.getFill().setSolidFill(CONFIG.COLORS.faint\_gray); barBG.getBorder().setTransparent();
    
    const p \= Math.max(0, Math.min(100, Number(items\[i\].percent || 0)));  
    const barFG \= slide.insertShape(SlidesApp.ShapeType.RECTANGLE, barLeft, y, barW \* (p/100), layout.pxToPt(14));  
    barFG.getFill().setSolidFill(CONFIG.COLORS.google\_green); barFG.getBorder().setTransparent();
    
    const pct \= slide.insertShape(SlidesApp.ShapeType.TEXT\_BOX, barLeft \+ barW \+ layout.pxToPt(6), y \- layout.pxToPt(1), layout.pxToPt(40), layout.pxToPt(16));  
    pct.getText().setText(String(p) \+ '%');  
    applyTextStyle(pct.getText(), { size: CONFIG.FONTS.sizes.small, color: CONFIG.COLORS.neutral\_gray });  
    ```
    
    }
    
    drawBottomBarAndFooter(slide, layout, pageNum);  
    }
    
    function createClosingSlide(slide, data, layout) {  
    slide.getBackground().setSolidFill(CONFIG.COLORS.background\_white);  
    const image \= slide.insertImage(CONFIG.LOGOS.closing);  
    const imgW\_pt \= layout.pxToPt(450) \* layout.scaleX;  
    const aspect \= image.getHeight() / image.getWidth();  
    image.setWidth(imgW\_pt).setHeight(imgW\_pt \* aspect);  
    image.setLeft((layout.pageW\_pt \- imgW\_pt) / 2).setTop((layout.pageH\_pt \- (imgW\_pt \* aspect)) / 2);  
    }
    
    // \--- 7\. ナヌティリティ関数矀 \---  
    function createLayoutManager(pageW\_pt, pageH\_pt) {  
    const pxToPt \= (px) \=\> px \* 0.75;  
    const baseW\_pt \= pxToPt(CONFIG.BASE\_PX.W);  
    const baseH\_pt \= pxToPt(CONFIG.BASE\_PX.H);  
    const scaleX \= pageW\_pt / baseW\_pt;  
    const scaleY \= pageH\_pt / baseH\_pt;
    
    const getPositionFromPath \= (path) \=\> path.split('.').reduce((obj, key) \=\> obj\[key\], CONFIG.POS\_PX);  
    return {  
    scaleX, scaleY, pageW\_pt, pageH\_pt, pxToPt,  
    getRect: (spec) \=\> {  
    const pos \= typeof spec \=== 'string' ? getPositionFromPath(spec) : spec;  
    let left\_px \= pos.left;  
    if (pos.right \!== undefined && pos.left \=== undefined) {  
    left\_px \= CONFIG.BASE\_PX.W \- pos.right \- pos.width;  
    }  
    return {  
    left:   left\_px \!== undefined ? pxToPt(left\_px) \* scaleX : undefined,  
    top:    pos.top \!== undefined ? pxToPt(pos.top) \* scaleY : undefined,  
    width:  pos.width \!== undefined ? pxToPt(pos.width) \* scaleX : undefined,  
    height: pos.height \!== undefined ? pxToPt(pos.height) \* scaleY : undefined,  
    };  
    }  
    };  
    }
    
    function offsetRect(rect, dx, dy) {  
    return { left: rect.left \+ (dx || 0), top: rect.top \+ (dy || 0), width: rect.width, height: rect.height };  
    }
    
    function drawStandardTitleHeader(slide, layout, key, title) {  
    const logoRect \= layout.getRect(\`${key}.headerLogo\`);  
    const logo \= slide.insertImage(CONFIG.LOGOS.header);  
    const asp \= logo.getHeight() / logo.getWidth();  
    logo.setLeft(logoRect.left).setTop(logoRect.top).setWidth(logoRect.width).setHeight(logoRect.width \* asp);
    
    const titleRect \= layout.getRect(\`${key}.title\`);  
    const titleShape \= slide.insertShape(SlidesApp.ShapeType.TEXT\_BOX, titleRect.left, titleRect.top, titleRect.width, titleRect.height);  
    setStyledText(titleShape, title || '', { size: CONFIG.FONTS.sizes.contentTitle, bold: true });
    
    const uRect \= layout.getRect(\`${key}.titleUnderline\`);  
    const u \= slide.insertShape(SlidesApp.ShapeType.RECTANGLE, uRect.left, uRect.top, uRect.width, uRect.height);  
    u.getFill().setSolidFill(CONFIG.COLORS.primary\_blue);  
    u.getBorder().setTransparent();  
    }
    
    function drawSubheadIfAny(slide, layout, key, subhead) {  
    if (\!subhead) return 0;  
    const rect \= layout.getRect(\`${key}.subhead\`);  
    const box \= slide.insertShape(SlidesApp.ShapeType.TEXT\_BOX, rect.left, rect.top, rect.width, rect.height);  
    setStyledText(box, subhead, { size: CONFIG.FONTS.sizes.subhead, color: CONFIG.COLORS.text\_primary });  
    return layout.pxToPt(36);  
    }
    
    function drawBottomBar(slide, layout) {  
    const barRect \= layout.getRect('bottomBar');  
    const bar \= slide.insertShape(SlidesApp.ShapeType.RECTANGLE, barRect.left, barRect.top, barRect.width, barRect.height);  
    bar.getFill().setSolidFill(CONFIG.COLORS.primary\_blue);  
    bar.getBorder().setTransparent();  
    }
    
    function drawBottomBarAndFooter(slide, layout, pageNum) {  
    drawBottomBar(slide, layout);  
    addGoogleFooter(slide, layout, pageNum);  
    }
    
    function addGoogleFooter(slide, layout, pageNum) {  
    const leftRect \= layout.getRect('footer.leftText');  
    const leftShape \= slide.insertShape(SlidesApp.ShapeType.TEXT\_BOX, leftRect.left, leftRect.top, leftRect.width, leftRect.height);  
    leftShape.getText().setText(CONFIG.FOOTER\_TEXT);  
    applyTextStyle(leftShape.getText(), { size: CONFIG.FONTS.sizes.footer, color: CONFIG.COLORS.text\_primary });
    
    if (pageNum \> 0\) {  
    const rightRect \= layout.getRect('footer.rightPage');  
    const rightShape \= slide.insertShape(SlidesApp.ShapeType.TEXT\_BOX, rightRect.left, rightRect.top, rightRect.width, rightRect.height);  
    rightShape.getText().setText(String(pageNum));  
    applyTextStyle(rightShape.getText(), { size: CONFIG.FONTS.sizes.footer, color: CONFIG.COLORS.primary\_blue, align: SlidesApp.ParagraphAlignment.END });  
    }  
    }
    
    function applyTextStyle(textRange, opt) {  
    const style \= textRange.getTextStyle();  
    style.setFontFamily(CONFIG.FONTS.family);  
    style.setForegroundColor(opt.color || CONFIG.COLORS.text\_primary);  
    style.setFontSize(opt.size || CONFIG.FONTS.sizes.body);  
    style.setBold(opt.bold || false);  
    if (opt.align) {  
    try { textRange.getParagraphs().forEach(p \=\> p.getRange().getParagraphStyle().setParagraphAlignment(opt.align)); } catch (e) {}  
    }  
    }
    
    function setStyledText(shapeOrCell, rawText, baseOpt) {  
    const parsed \= parseInlineStyles(rawText || '');  
    const tr \= shapeOrCell.getText();  
    tr.setText(parsed.output);  
    applyTextStyle(tr, baseOpt || {});  
    applyStyleRanges(tr, parsed.ranges);  
    }
    
    function setBulletsWithInlineStyles(shape, points) {  
    const joiner \= '\\n\\n';  
    let combined \= '';  
    const ranges \= \[\];
    
    (points || \[\]).forEach((pt, idx) \=\> {  
    const parsed \= parseInlineStyles(String(pt || ''));  
    const bullet \= '• ' \+ parsed.output;  
    if (idx \> 0\) combined \+= joiner;  
    const start \= combined.length;  
    combined \+= bullet;
    
    ```
    parsed.ranges.forEach(r \=\> {  
      ranges.push({ start: start \+ 2 \+ r.start, end: start \+ 2 \+ r.end, bold: r.bold, color: r.color });  
    });  
    ```
    
    });
    
    const tr \= shape.getText();  
    tr.setText(combined || '• —');  
    applyTextStyle(tr, { size: CONFIG.FONTS.sizes.body });
    
    try {  
    tr.getParagraphs().forEach(p \=\> {  
    const ps \= p.getRange().getParagraphStyle();  
    ps.setLineSpacing(100);  
    ps.setSpaceBelow(6);  
    });  
    } catch (e) {}
    
    applyStyleRanges(tr, ranges);  
    }
    
    function parseInlineStyles(s) {  
    const ranges \= \[\];  
    let out \= '';  
    for (let i \= 0; i \< s.length; ) {  
    // \[\[青倪字\]\]  
    if (s\[i\] \=== '\[' && s\[i+1\] \=== '\[') {  
    const close \= s.indexOf('\]\]', i \+ 2);  
    if (close \!== \-1) {  
    const content \= s.substring(i \+ 2, close);  
    const start \= out.length;  
    out \+= content;  
    const end \= out.length;  
    ranges.push({ start, end, bold: true, color: CONFIG.COLORS.primary\_blue });  
    i \= close \+ 2; continue;  
    }  
    }  
    // \*\*倪字\*\*  
    if (s\[i\] \=== '\*' && s\[i+1\] \=== '\*') {  
    const close \= s.indexOf('\*\*', i \+ 2);  
    if (close \!== \-1) {  
    const content \= s.substring(i \+ 2, close);  
    const start \= out.length;  
    out \+= content;  
    const end \= out.length;  
    ranges.push({ start, end, bold: true });  
    i \= close \+ 2; continue;  
    }  
    }  
    out \+= s\[i\]; i++;  
    }  
    return { output: out, ranges };  
    }
    
    function applyStyleRanges(textRange, ranges) {  
    ranges.forEach(r \=\> {  
    try {  
    const sub \= textRange.getRange(r.start, r.end);  
    if (\!sub) return;  
    const st \= sub.getTextStyle();  
    if (r.bold)  st.setBold(true);  
    if (r.color) st.setForegroundColor(r.color);  
    } catch (e) {}  
    });  
    }
    
    function normalizeImages(arr) {  
    return (arr || \[\]).map(v \=\> {  
    if (typeof v \=== 'string') return { url: v };  
    if (v && typeof v.url \=== 'string') return { url: v.url, caption: v.caption || '' };  
    return null;  
    }).filter(Boolean).slice(0, 6);  
    }
    
    function renderImagesInArea(slide, layout, area, images) {  
    if (\!images || images.length \=== 0\) return;  
    const n \= Math.min(6, images.length);  
    let cols \= 1, rows \= 1;  
    if (n \=== 1\) { cols \= 1; rows \= 1; }  
    else if (n \=== 2\) { cols \= 2; rows \= 1; }  
    else if (n \<= 4\) { cols \= 2; rows \= 2; }  
    else { cols \= 3; rows \= 2; }
    
    const gap \= layout.pxToPt(10);  
    const cellW \= (area.width \- gap \* (cols \- 1)) / cols;  
    const cellH \= (area.height \- gap \* (rows \- 1)) / rows;
    
    for (let i \= 0; i \< n; i++) {  
    const r \= Math.floor(i / cols), c \= i % cols;  
    const left \= area.left \+ c \* (cellW \+ gap);  
    const top  \= area.top  \+ r \* (cellH \+ gap);  
    try {  
    const img \= slide.insertImage(images\[i\].url);  
    const scale \= Math.min(cellW / img.getWidth(), cellH / img.getHeight());  
    const w \= img.getWidth() \* scale;  
    const h \= img.getHeight() \* scale;  
    img.setWidth(w).setHeight(h);  
    img.setLeft(left \+ (cellW \- w) / 2).setTop(top \+ (cellH \- h) / 2);  
    } catch(e) {}  
    }  
    }
    
    function isAgendaTitle(title) {  
    const t \= String(title || '').toLowerCase();  
    return /(agenda|アゞェンダ|目次|本日お䌝えするこず)/.test(t);  
    }  
    function buildAgendaFromSlideData() {  
    const pts \= \[\];  
    for (const d of slideData) {  
    if (d && d.type \=== 'section' && typeof d.title \=== 'string' && d.title.trim()) pts.push(d.title.trim());  
    }  
    if (pts.length \> 0\) return pts.slice(0, 5);  
    const alt \= \[\];  
    for (const d of slideData) {  
    if (d && d.type \=== 'content' && typeof d.title \=== 'string' && d.title.trim()) alt.push(d.title.trim());  
    }  
    return alt.slice(0, 5);  
    }
    
    function drawArrowBetweenRects(slide, a, b, arrowH, arrowGap) {  
    const fromX \= a.left \+ a.width;  
    const toX   \= b.left;  
    const width \= Math.max(0, toX \- fromX \- arrowGap \* 2);  
    if (width \< 8\) return;  
    const yMid \= ((a.top \+ a.height/2) \+ (b.top \+ b.height/2)) / 2;  
    const top \= yMid \- arrowH / 2;  
    const left \= fromX \+ arrowGap;  
    const arr \= slide.insertShape(SlidesApp.ShapeType.RIGHT\_ARROW, left, top, width, arrowH);  
    arr.getFill().setSolidFill(CONFIG.COLORS.primary\_blue);  
    arr.getBorder().setTransparent();  
    }  

    テンプレヌトパタヌンを増やしたり
    安定性を高めるブラッシュアップは匕き続き行っおいく予定です
    新バヌゞョンが完成したらたた共有したすね

    参考になったらnoteにいいね、フォロヌをお願いしたす
    Xでの感想シェア#たじん匏プロンプト
    もいただければ次の開発の゚ネルギヌになりたす🔥

    【Googleスラむドが䞀瞬で完成する"奇跡"のプロンプト】
    スラむド生成AIは色々知っおるけど、䌚瀟のセキュリティポリシヌで䜿えない そんな方も倚いのでは
    仕事で䜿えないず意味がないですよね🥲

    そんな悩みを解決するため、魔法のプロンプトを線み出したした
    必芁なのは、Geminiだけ。 pic.twitter.com/ZaqMgHj0C6

    — たじんAI × Google (@Majin_AppSheet) August 16, 2025

    おすすめラむタヌ✚


     
     
    『秒速で仕事が片付く たじん匏 生成AI資料䜜成術』KADOKAWAから9/15発売日本DX倧賞2026「AIむンパクト賞」受賞Google Workspace×AIで仕事を効率化する「たじん匏」シリヌズの開発者。

    あなたぞのおすすめ