This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
This is a Google Apps Script project that enhances Google Slides with automated formatting, styling, and content management features. The module provides a custom menu with various tools to improve slide design consistency and streamline presentation creation.
# Install clasp globally if not already installed
npm install -g @google/clasp
# Login to Google account
clasp login
# Initialize new project (use the provided script)
./init.sh
# Or manually create project
clasp create --type slides --title "Your Presentation Title"
cp appsscript.example.json appsscript.json
clasp push
clasp open-container# Push changes to Google Apps Script
clasp push
# Pull changes from Google Apps Script
clasp pull
# Open the Google Slides presentation
clasp open-container
# Open the Apps Script editor
clasp openclasp pushscans the whole tree..clasp.jsonhere usesrootDir: ""andskipSubdirectories: false, so clasp walks every subdirectory — including any git worktrees created under.claude/worktrees/.- Duplicate-file collision. A worktree (e.g.
.claude/worktrees/t/) holds a full copy of the source tree. Because all.js/.htmlfiles flatten to the same Apps Script names, clasp fails withA file with this name already exists in the current project: appsscript(twoappsscript.json, etc.). Plainclasp pushmay also just printSkipping push. .claspignore's.*does NOT recurse. The catch-all.*only ignores dotfiles/dirs at the root; it does not exclude a dotdir's contents. To keep clasp out of session files/worktrees,.claspignorehas an explicit.claude/**entry — keep it.- Verify before pushing:
clasp statusshould show source files at repo-root paths (e.g.src/util/grid_minter.js), with zeroworktreesentries. If you see.claude/worktrees/...lines, the ignore rule is missing or broke. - Don't commit the worktree.
git statuslists.claude/worktrees/t/as untracked — nevergit addit; commit only the real source changes. clasp push --forceskips the manifest-overwrite confirmation that the non-interactive shell otherwise auto-declines.- Never
git reset --hardwith uncommitted work present — it silently discards working-tree edits (this is how an earlier CLAUDE.md edit was lost). To test whether a commit conflicts, usegit cherry-pick -nthengit cherry-pick --abort(notreset --hard), or test on a throwaway branch. - A global
~/.gitignore_globalcan silently blockgit addeven after you remove the pattern from the repo.gitignore(e.g. it ignoresdist/). If agit addreports "paths are ignored" but the repo.gitignorelooks clean, rungit check-ignore -v <path>— it names the actual rule + file. Fix with a repo-level negation (!dist/,!dist/**) and/orgit add -f(once tracked, ignores no longer apply; CI runners have no global ignore so they're unaffected). appsscript.jsonis gitignored —appsscript.example.jsonis the committed source of truth. Both CI workflows (clasp-push.yml,build-bundle.yml)cp appsscript.example.json appsscript.jsonbefore pushing/building, since the real manifest is absent on the runner. So edit BOTH manifests together (scopes, advanced services) or CI silently ships the example's version.- An explicit
oauthScopeslist DISABLES scope auto-detection. With NOoauthScopesfield, Apps Script auto-detects scopes from the code at auth time (soUi.showModalDialog/showSidebarsilently pull inscript.container.ui). The moment you listoauthScopesexplicitly, ONLY those scopes are granted — auto-detection is off. A missing scope then surfaces at RUNTIME as e.g.指定的權限不足,無法呼叫 Ui.showModalDialog. 必要權限: .../auth/script.container.ui. This is how clones broke while the main deck (which had no explicit list, so auto-detect covered it) worked. Any deck opening a dialog/sidebar needshttps://www.googleapis.com/auth/script.container.uiin the list. Keep the explicit list COMPLETE in both manifests. - Changing scopes forces every user to re-consent. After a scope edit ships
(clones self-update from
dist/bundle.json), the next dialog/sidebar call triggers Google's re-authorization prompt. This is unavoidable per Google's OAuth rules — not a bug; it only happens once per account per scope change.
Clones pull updates themselves — no central push needed. src/util/self_update.js
(menu ⚙ 設定與批次 → 🔄 更新腳本) fetches dist/bundle.json from GitHub raw and
overwrites the clone's own content via the Apps Script API (projects.updateContent,
authed with ScriptApp.getOAuthToken()). dist/ is built by
scripts/build-bundle.mjs + .github/workflows/build-bundle.yml on every push to
main (and committed back with [skip ci] to avoid a workflow loop). dist/ is
TRACKED in git (the pull reads it from raw). One-time per account: enable the Apps
Script API at script.google.com/home/usersettings. cloned.txt +
batch_push_to_cloned_project.sh remain only as a recovery fallback (force clasp push to a clone whose self-update broke).
- No automated testing framework is configured
- Testing is done manually in Google Slides after pushing code
- After making changes, run
clasp pushand test in the Google Slides presentation
-
src/config.js - Main configuration and menu creation logic
- Contains
onOpen()function that creates custom menus - Defines global configuration variables (colors, fonts, etc.)
- Manages configuration persistence via PropertiesService
- Contains
-
src/util/ - Utility functions for slide manipulation
- Individual utility files with functions for specific tasks
- No ES6 imports - functions are globally available in Google Apps Script
-
src/batch/ - Batch processing modules
- Functions that process multiple slides at once
- Uses Google Slides API batch update requests for efficiency
-
src/components/ - HTML components for sidebar interface
- Modular HTML files included via
<?!= include() ?>syntax - Contains configuration forms and style buttons
- flowchartSidebar.html - Interactive flowchart creation interface
- Modular HTML files included via
-
src/util/flowchart/ - Flowchart and hierarchical shape management
- main.js - Main API functions for flowchart operations
- graphIdUtils.js - Graph ID parsing, generation, and management
- childCreationUtils.js - Child shape creation with positioning and styling
- siblingCreationUtils.js - Sibling shape creation with layout consistency
- index.js - Function exports and documentation
- Global Functions: Google Apps Script doesn't support ES6 modules, so all functions are global
- Batch API Updates: Uses
runRequestProcessors()pattern to collect multiple API requests and send them as a batch - HTML Service: Uses server-side HTML templates with
<?!= include() ?>for modular components - Configuration Management: Uses PropertiesService for persistent configuration storage
- Graph ID System: Uses shape title (alt text) to store hierarchical graph IDs for flowchart management
- Flowchart Architecture: Supports both LR (Left-Right) and TD (Top-Down) layout patterns
Three main menu categories are created in src/config.js:
- 🗃 批次處理 (Batch Processing) - Functions that process multiple slides
- 🎨 加入元素 (Add Elements) - Single slide beautification tools
- 🖖 跨頁功能 (Cross-page Functions) - Functions that work across multiple slides
Configuration is managed through:
- Global variables in
src/config.js(defaults) - PropertiesService for user-specific persistent settings
- Sidebar interface for real-time configuration updates
onOpen()- Automatically creates menus when presentation opensrunRequestProcessors(...)- Batches multiple API requests for efficiencycreateCustomMenu()- Creates the custom menu structureapplyThemeToCurrentPresentation()- Applies theme from template presentation
createChildTop/Right/Bottom/Left()- Creates child shapes in specified directioncreateChildTopWithText()- Creates child shapes with custom text contentcreateSiblingShape()- Creates sibling shapes with proper positioningshowSelectedShapeGraphId()- Displays Graph ID information for debuggingparseGraphId()- Parses Graph ID format:graph[parent](layout)[current][children]generateGraphId()- Generates hierarchical Graph IDs with layout supportgetShapeGraphId()/setShapeGraphId()- Graph ID management via shape titles
- This is a Google Apps Script project, not a Node.js project
- No package.json or npm dependencies
- Uses Google Slides API v1 (enabled in appsscript.json)
- HTML templates use server-side includes, not client-side frameworks
- All code runs in Google's V8 runtime (specified in appsscript.json)
Key variables in src/config.js:
var main_color = "#3D6869"; // Main theme color
var main_font_family = "Source Sans Pro"; // Font family
var water_mark_text = "ⓒ Hsieh-Ting Lin"; // Watermark text
var label_font_size = 14; // Font size for labels
const sourcePresentationId = "1qAZzq-..."; // Template presentation ID- Files must be .js or .gs extensions (both work the same)
- Uses HtmlService for UI components
- PropertiesService for data persistence
- SlidesApp and Slides API for slide manipulation
- No require() or import statements - everything is global
- Stores hierarchical information in shape titles (alt text) instead of visible text
- Format:
graph[parent](layout)[current][children] - Examples:
- Root:
graph[](TD)[A1][] - Child:
graph[A1](TD)[B1][] - Parent with children:
graph[A1](LR)[B1][C1,C2]
- Root:
- LR (Left-Right): Parent connects to children horizontally
- TD (Top-Down): Parent connects to children vertically
- Layout consistency maintained across sibling relationships
- By Count: Specify number of children to create (empty text)
- By Text: Multi-line text input, each line becomes text for one child shape
- Collapsible Line Settings (default folded)
- Tab-based child creation interface
- Real-time Graph ID inspector
- Persistent settings via localStorage