Skip to content

Instantly share code, notes, and snippets.

@mr-eyes
Last active May 17, 2025 19:59
Show Gist options
  • Select an option

  • Save mr-eyes/b6bb736b2f64a2c4ae3316ed6c52b834 to your computer and use it in GitHub Desktop.

Select an option

Save mr-eyes/b6bb736b2f64a2c4ae3316ed6c52b834 to your computer and use it in GitHub Desktop.
Google Slides Agenda Tracking Bar Script
function addOrUpdateAgendaBarWithProgressV5() {
const pres = SlidesApp.getActivePresentation();
const slides = pres.getSlides();
// 0) Clear any existing timeline shapes on ALL slides
slides.forEach(slide => {
slide.getPageElements().forEach(el => {
const t = el.getTitle && el.getTitle();
if (t && t.startsWith('AGENDA_')) {
el.remove();
}
});
});
// 1) Section definitions
const sections = [
{ name: 'Motivation', from: 2, to: 3 },
{ name: 'Scientific Background', from: 4, to: 5 },
{ name: 'Methods', from: 6, to: 7 },
{ name: 'Use Case 1', from: 8, to: 11 },
{ name: 'Use Case 2', from: 12, to: 15 },
{ name: 'Future Directions', from: 16, to: 17 },
{ name: 'PhD Thesis', from: 18, to: 50 }
];
// 2) Palette
const baselineColor = '#B0BEC5';
const inactiveBarBg = '#ECEFF1';
const activeBarFill = '#42A5F5';
const inactiveCircleFill = '#FFFFFF';
const inactiveBorder = '#CFD8DC';
const inactiveLabelColor = '#90A4AE';
const activeLabelColor = '#42A5F5';
// 3) Layout constants (ultra-compact)
const MARGIN = 8; // side margin
const VSPACE = 20; // total vertical space
const DIAMETER = 8; // circle size
const BAR_HEIGHT = 3; // progress bar thickness
const DOT_D = 3; // boundary dot size
const W = pres.getPageWidth();
const H = pres.getPageHeight();
const usableW = W - MARGIN * 2;
const slotW = usableW / sections.length;
// 4) Vertical positions
const centerY = H - MARGIN - (VSPACE / 2);
const lineY = centerY;
const circleY = lineY - (DIAMETER / 2);
const barY = lineY + (DIAMETER / 2) + 1;
const labelY = barY + BAR_HEIGHT - 5; // tucked up under the bar
// 5) Draw on each slide
slides.forEach((slide, idx) => {
const slideNum = idx + 1;
// 5a) Baseline
const line = slide.insertLine(
SlidesApp.LineCategory.STRAIGHT,
MARGIN, lineY,
W - MARGIN, lineY
);
line.setTitle('AGENDA_LINE')
.getLineFill().setSolidFill(baselineColor);
line.setWeight(1);
// 5b) Boundary dots
for (let j = 0; j <= sections.length; j++) {
const x = MARGIN + slotW * j;
const dot = slide.insertShape(
SlidesApp.ShapeType.ELLIPSE,
x - DOT_D/2, lineY - DOT_D/2,
DOT_D, DOT_D
);
dot.setTitle(`AGENDA_BOUNDARY_DOT_${j}`)
.getFill().setSolidFill(baselineColor);
dot.getBorder().setWeight(0.1)
.getLineFill().setSolidFill(baselineColor);
}
// 5c) Sections: bars, circles, labels
sections.forEach((sec, i) => {
const startX = MARGIN + slotW * i;
const centerX = startX + slotW / 2;
const totalSlides = sec.to - sec.from + 1;
let progress = 0;
if (slideNum >= sec.to) progress = 1;
else if (slideNum >= sec.from) progress = (slideNum - sec.from + 1) / totalSlides;
// Background bar
const bgBar = slide.insertShape(
SlidesApp.ShapeType.RECTANGLE,
startX, barY,
slotW, BAR_HEIGHT
);
bgBar.setTitle(`AGENDA_BAR_BG_${i}`)
.getFill().setSolidFill(inactiveBarBg);
bgBar.getBorder().setWeight(0.1)
.getLineFill().setSolidFill(inactiveBarBg);
// Fill bar if needed
const fillWidth = Math.min(slotW, slotW * progress);
if (fillWidth > 0) {
const fillBar = slide.insertShape(
SlidesApp.ShapeType.RECTANGLE,
startX, barY,
fillWidth, BAR_HEIGHT
);
fillBar.setTitle(`AGENDA_BAR_FILL_${i}`)
.getFill().setSolidFill(activeBarFill);
fillBar.getBorder().setWeight(0.1)
.getLineFill().setSolidFill(activeBarFill);
}
// Circle marker
const circle = slide.insertShape(
SlidesApp.ShapeType.ELLIPSE,
centerX - DIAMETER/2, circleY,
DIAMETER, DIAMETER
);
circle.setTitle(`AGENDA_CIRCLE_${i}`);
if (slideNum >= sec.from && slideNum <= sec.to) {
circle.getFill().setSolidFill(activeBarFill);
circle.getBorder().setWeight(0.1)
.getLineFill().setSolidFill(activeBarFill);
} else {
circle.getFill().setSolidFill(inactiveCircleFill);
circle.getBorder().setWeight(1)
.getLineFill().setSolidFill(inactiveBorder);
}
// Label (6pt, snug under the bar)
const box = slide.insertTextBox(
sec.name,
startX, labelY,
slotW, 10
);
box.setTitle(`AGENDA_LABEL_${i}`)
.getFill().setTransparent();
const txt = box.getText().setText(sec.name);
txt.getTextStyle()
.setFontFamily('Roboto')
.setFontSize(6)
.setBold(slideNum >= sec.from && slideNum <= sec.to)
.setForegroundColor(
(slideNum >= sec.from && slideNum <= sec.to)
? activeLabelColor
: inactiveLabelColor
);
txt.getParagraphStyle()
.setParagraphAlignment(SlidesApp.ParagraphAlignment.CENTER);
});
});
SlidesApp.getUi().alert('✅ Agenda timeline added!');
}
@mr-eyes
Copy link
Author

mr-eyes commented Feb 7, 2024

In Google Slides, click "Extensions" -> "Apps Script".
Then, copy and paste this code to add the progress bar.

@mr-eyes
Copy link
Author

mr-eyes commented Feb 7, 2024

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment