/* =============================================================================
GB SLIDES - PowerPoint-like Presentation JavaScript
General Bots Suite Component
============================================================================= */
(function () {
"use strict";
const CONFIG = {
CANVAS_WIDTH: 960,
CANVAS_HEIGHT: 540,
MAX_HISTORY: 50,
AUTOSAVE_DELAY: 3000,
WS_RECONNECT_DELAY: 5000,
MIN_ELEMENT_SIZE: 20,
};
const state = {
presentationId: null,
presentationName: "Untitled Presentation",
slides: [],
currentSlideIndex: 0,
selectedElement: null,
clipboard: null,
history: [],
historyIndex: -1,
zoom: 1,
collaborators: [],
ws: null,
isDragging: false,
isResizing: false,
isRotating: false,
dragStart: null,
resizeHandle: null,
isDirty: false,
autoSaveTimer: null,
isPresenting: false,
theme: null,
};
const elements = {
container: null,
sidebar: null,
thumbnails: null,
canvas: null,
canvasContainer: null,
selectionHandles: null,
propertiesPanel: null,
presentationName: null,
collaborators: null,
contextMenu: null,
slideContextMenu: null,
cursorIndicators: null,
};
function init() {
cacheElements();
bindEvents();
loadFromUrlParams();
connectWebSocket();
createNewPresentation();
}
function cacheElements() {
elements.container = document.querySelector(".slides-container");
elements.sidebar = document.getElementById("slides-sidebar");
elements.thumbnails = document.getElementById("slide-thumbnails");
elements.canvas = document.getElementById("slide-canvas");
elements.canvasContainer = document.getElementById("canvas-container");
elements.selectionHandles = document.getElementById("selection-handles");
elements.propertiesPanel = document.getElementById("properties-panel");
elements.presentationName = document.getElementById("presentation-name");
elements.collaborators = document.getElementById("collaborators");
elements.contextMenu = document.getElementById("context-menu");
elements.slideContextMenu = document.getElementById("slide-context-menu");
elements.cursorIndicators = document.getElementById("cursor-indicators");
}
function bindEvents() {
elements.canvas.addEventListener("mousedown", handleCanvasMouseDown);
elements.canvas.addEventListener("dblclick", handleCanvasDoubleClick);
document.addEventListener("mousemove", handleMouseMove);
document.addEventListener("mouseup", handleMouseUp);
document.addEventListener("keydown", handleKeyDown);
document.addEventListener("click", hideContextMenus);
elements.canvas.addEventListener("contextmenu", handleContextMenu);
const handles = elements.selectionHandles.querySelectorAll(
".handle, .rotate-handle",
);
handles.forEach((handle) => {
handle.addEventListener("mousedown", handleResizeStart);
});
window.addEventListener("beforeunload", handleBeforeUnload);
}
function createNewPresentation() {
const titleSlide = createSlide("title");
state.slides = [titleSlide];
state.currentSlideIndex = 0;
state.theme = createDefaultTheme();
renderThumbnails();
renderCurrentSlide();
updateSlideCounter();
}
function createSlide(layout) {
const slide = {
id: generateId(),
layout: layout,
elements: [],
background: {
bg_type: "solid",
color: "#ffffff",
},
notes: null,
transition: {
transition_type: "fade",
duration: 0.5,
},
};
switch (layout) {
case "title":
slide.elements.push(
createTextElement(100, 200, 760, 100, "Presentation Title", {
fontSize: 48,
fontWeight: "bold",
textAlign: "center",
color: "#1e293b",
}),
);
slide.elements.push(
createTextElement(100, 320, 760, 50, "Subtitle or Author Name", {
fontSize: 24,
textAlign: "center",
color: "#64748b",
}),
);
break;
case "title-content":
slide.elements.push(
createTextElement(50, 40, 860, 60, "Slide Title", {
fontSize: 36,
fontWeight: "bold",
color: "#1e293b",
}),
);
slide.elements.push(
createTextElement(
50,
120,
860,
400,
"• Click to add content\n• Add your bullet points here",
{
fontSize: 20,
color: "#374151",
lineHeight: 1.6,
},
),
);
break;
case "two-column":
slide.elements.push(
createTextElement(50, 40, 860, 60, "Slide Title", {
fontSize: 36,
fontWeight: "bold",
color: "#1e293b",
}),
);
slide.elements.push(
createTextElement(50, 120, 410, 400, "Left column content", {
fontSize: 18,
color: "#374151",
}),
);
slide.elements.push(
createTextElement(500, 120, 410, 400, "Right column content", {
fontSize: 18,
color: "#374151",
}),
);
break;
case "section":
slide.elements.push(
createTextElement(100, 220, 760, 100, "Section Title", {
fontSize: 48,
fontWeight: "bold",
textAlign: "center",
color: "#1e293b",
}),
);
break;
case "blank":
default:
break;
}
return slide;
}
function createTextElement(x, y, width, height, text, style) {
return {
id: generateId(),
element_type: "text",
x: x,
y: y,
width: width,
height: height,
rotation: 0,
content: { text: text },
style: {
fontFamily: style.fontFamily || "Inter",
fontSize: style.fontSize || 16,
fontWeight: style.fontWeight || "normal",
fontStyle: style.fontStyle || "normal",
textAlign: style.textAlign || "left",
verticalAlign: style.verticalAlign || "top",
color: style.color || "#000000",
lineHeight: style.lineHeight || 1.4,
...style,
},
animations: [],
z_index: 1,
locked: false,
};
}
function createShapeElement(x, y, width, height, shapeType, style) {
return {
id: generateId(),
element_type: "shape",
x: x,
y: y,
width: width,
height: height,
rotation: 0,
content: { shape_type: shapeType },
style: {
fill: style.fill || "#3b82f6",
stroke: style.stroke || "none",
strokeWidth: style.strokeWidth || 0,
opacity: style.opacity || 1,
borderRadius: style.borderRadius || 0,
...style,
},
animations: [],
z_index: 1,
locked: false,
};
}
function createImageElement(x, y, width, height, src) {
return {
id: generateId(),
element_type: "image",
x: x,
y: y,
width: width,
height: height,
rotation: 0,
content: { src: src },
style: {
opacity: 1,
borderRadius: 0,
},
animations: [],
z_index: 1,
locked: false,
};
}
function createDefaultTheme() {
return {
name: "Default",
colors: {
primary: "#3b82f6",
secondary: "#64748b",
accent: "#f59e0b",
background: "#ffffff",
text: "#1e293b",
text_light: "#64748b",
},
fonts: {
heading: "Inter",
body: "Inter",
},
};
}
function renderThumbnails() {
if (!elements.thumbnails) return;
elements.thumbnails.innerHTML = state.slides
.map(
(slide, index) => `
${renderSlideThumbnailContent(slide)}
${index + 1}
`,
)
.join("");
}
function renderSlideThumbnailContent(slide) {
const scale = 0.15;
let html = ``;
slide.elements.forEach((element) => {
html += renderElementHTML(element, true);
});
html += "
";
return html;
}
function renderCurrentSlide() {
if (!elements.canvas) return;
const slide = state.slides[state.currentSlideIndex];
if (!slide) return;
elements.canvas.style.background = slide.background.color || "#ffffff";
elements.canvas.innerHTML = "";
slide.elements.forEach((element) => {
const el = document.createElement("div");
el.innerHTML = renderElementHTML(element);
const elementNode = el.firstElementChild;
if (elementNode) {
elements.canvas.appendChild(elementNode);
bindElementEvents(elementNode, element);
}
});
clearSelection();
updateSlideCounter();
}
function renderElementHTML(element, isThumbnail = false) {
const style = buildElementStyle(element);
const classes = ["slide-element"];
if (
state.selectedElement &&
state.selectedElement.id === element.id &&
!isThumbnail
) {
classes.push("selected");
}
if (element.locked) {
classes.push("locked");
}
let content = "";
switch (element.element_type) {
case "text":
classes.push("slide-element-text");
content = escapeHtml(element.content.text || "").replace(/\n/g, "
");
break;
case "image":
classes.push("slide-element-image");
content = `
`;
break;
case "shape":
classes.push("slide-element-shape");
content = renderShapeSVG(element);
break;
case "chart":
classes.push("slide-element-chart");
content = renderChartContent(element);
break;
}
return `
${content}
`;
}
function buildElementStyle(element) {
const styles = [
`left: ${element.x}px`,
`top: ${element.y}px`,
`width: ${element.width}px`,
`height: ${element.height}px`,
`transform: rotate(${element.rotation || 0}deg)`,
`z-index: ${element.z_index || 1}`,
];
const s = element.style || {};
if (element.element_type === "text") {
if (s.fontFamily) styles.push(`font-family: ${s.fontFamily}`);
if (s.fontSize) styles.push(`font-size: ${s.fontSize}px`);
if (s.fontWeight) styles.push(`font-weight: ${s.fontWeight}`);
if (s.fontStyle) styles.push(`font-style: ${s.fontStyle}`);
if (s.textAlign) styles.push(`text-align: ${s.textAlign}`);
if (s.color) styles.push(`color: ${s.color}`);
if (s.lineHeight) styles.push(`line-height: ${s.lineHeight}`);
if (s.fill) styles.push(`background: ${s.fill}`);
}
if (element.element_type === "shape") {
if (s.opacity) styles.push(`opacity: ${s.opacity}`);
}
return styles.join("; ");
}
function renderShapeSVG(element) {
const shapeType = element.content.shape_type || "rectangle";
const fill = element.style.fill || "#3b82f6";
const stroke = element.style.stroke || "none";
const strokeWidth = element.style.strokeWidth || 0;
let path = "";
switch (shapeType) {
case "rectangle":
path = ``;
break;
case "rounded-rectangle":
path = ``;
break;
case "ellipse":
path = ``;
break;
case "triangle":
path = ``;
break;
case "diamond":
path = ``;
break;
case "star":
path = ``;
break;
case "arrow-right":
path = ``;
break;
case "callout":
path = ``;
break;
default:
path = ``;
}
return `
`;
}
function renderChartContent(element) {
return 'Chart
';
}
function bindElementEvents(node, element) {
node.addEventListener("mousedown", (e) =>
handleElementMouseDown(e, element),
);
node.addEventListener("dblclick", (e) =>
handleElementDoubleClick(e, element),
);
}
function handleCanvasMouseDown(e) {
if (e.target === elements.canvas) {
clearSelection();
}
}
function handleCanvasDoubleClick(e) {
if (e.target === elements.canvas) {
const rect = elements.canvas.getBoundingClientRect();
const x = (e.clientX - rect.left) / state.zoom;
const y = (e.clientY - rect.top) / state.zoom;
addTextBoxAt(x - 100, y - 25);
}
}
function addTextBox() {
const slide = state.slides[state.currentSlideIndex];
const centerX = CONFIG.CANVAS_WIDTH / 2 - 150;
const centerY = CONFIG.CANVAS_HEIGHT / 2 - 30;
addTextBoxAt(centerX, centerY);
}
function addTextBoxAt(x, y) {
const slide = state.slides[state.currentSlideIndex];
const textElement = createTextElement(x, y, 300, 60, "Click to edit text", {
fontSize: 24,
color: "#1e293b",
});
slide.elements.push(textElement);
saveToHistory();
renderCurrentSlide();
selectElement(textElement);
scheduleAutoSave();
broadcastChange("elementAdded", { element: textElement });
}
function handleElementMouseDown(e, element) {
e.stopPropagation();
if (element.locked) return;
selectElement(element);
if (e.button === 0) {
state.isDragging = true;
state.dragStart = {
x: e.clientX,
y: e.clientY,
elementX: element.x,
elementY: element.y,
};
}
}
function handleElementDoubleClick(e, element) {
e.stopPropagation();
if (element.element_type === "text") {
startTextEditing(element);
}
}
function handleResizeStart(e) {
e.stopPropagation();
if (!state.selectedElement) return;
const handle = e.target.dataset.handle;
if (handle === "rotate") {
state.isRotating = true;
} else {
state.isResizing = true;
state.resizeHandle = handle;
}
state.dragStart = {
x: e.clientX,
y: e.clientY,
elementX: state.selectedElement.x,
elementY: state.selectedElement.y,
elementWidth: state.selectedElement.width,
elementHeight: state.selectedElement.height,
elementRotation: state.selectedElement.rotation || 0,
};
}
function handleMouseMove(e) {
if (state.isDragging && state.selectedElement && state.dragStart) {
const dx = (e.clientX - state.dragStart.x) / state.zoom;
const dy = (e.clientY - state.dragStart.y) / state.zoom;
state.selectedElement.x = state.dragStart.elementX + dx;
state.selectedElement.y = state.dragStart.elementY + dy;
updateElementPosition(state.selectedElement);
updateSelectionHandles();
broadcastChange("elementMove", state.selectedElement);
} else if (state.isResizing && state.selectedElement && state.dragStart) {
const dx = (e.clientX - state.dragStart.x) / state.zoom;
const dy = (e.clientY - state.dragStart.y) / state.zoom;
resizeElement(dx, dy);
updateElementPosition(state.selectedElement);
updateSelectionHandles();
broadcastChange("elementResize", state.selectedElement);
} else if (state.isRotating && state.selectedElement) {
const rect = elements.canvas.getBoundingClientRect();
const centerX = state.selectedElement.x + state.selectedElement.width / 2;
const centerY =
state.selectedElement.y + state.selectedElement.height / 2;
const mouseX = (e.clientX - rect.left) / state.zoom;
const mouseY = (e.clientY - rect.top) / state.zoom;
const angle =
Math.atan2(mouseY - centerY, mouseX - centerX) * (180 / Math.PI) + 90;
state.selectedElement.rotation = Math.round(angle);
updateElementPosition(state.selectedElement);
updateSelectionHandles();
updatePropertiesPanel();
broadcastChange("elementRotate", state.selectedElement);
}
broadcastCursor(e);
}
function resizeElement(dx, dy) {
const el = state.selectedElement;
const s = state.dragStart;
switch (state.resizeHandle) {
case "se":
el.width = Math.max(CONFIG.MIN_ELEMENT_SIZE, s.elementWidth + dx);
el.height = Math.max(CONFIG.MIN_ELEMENT_SIZE, s.elementHeight + dy);
break;
case "sw":
el.x = s.elementX + dx;
el.width = Math.max(CONFIG.MIN_ELEMENT_SIZE, s.elementWidth - dx);
el.height = Math.max(CONFIG.MIN_ELEMENT_SIZE, s.elementHeight + dy);
break;
case "ne":
el.y = s.elementY + dy;
el.width = Math.max(CONFIG.MIN_ELEMENT_SIZE, s.elementWidth + dx);
el.height = Math.max(CONFIG.MIN_ELEMENT_SIZE, s.elementHeight - dy);
break;
case "nw":
el.x = s.elementX + dx;
el.y = s.elementY + dy;
el.width = Math.max(CONFIG.MIN_ELEMENT_SIZE, s.elementWidth - dx);
el.height = Math.max(CONFIG.MIN_ELEMENT_SIZE, s.elementHeight - dy);
break;
case "n":
el.y = s.elementY + dy;
el.height = Math.max(CONFIG.MIN_ELEMENT_SIZE, s.elementHeight - dy);
break;
case "s":
el.height = Math.max(CONFIG.MIN_ELEMENT_SIZE, s.elementHeight + dy);
break;
case "e":
el.width = Math.max(CONFIG.MIN_ELEMENT_SIZE, s.elementWidth + dx);
break;
case "w":
el.x = s.elementX + dx;
el.width = Math.max(CONFIG.MIN_ELEMENT_SIZE, s.elementWidth - dx);
break;
}
}
function handleMouseUp() {
if (state.isDragging || state.isResizing || state.isRotating) {
saveToHistory();
scheduleAutoSave();
}
state.isDragging = false;
state.isResizing = false;
state.isRotating = false;
state.dragStart = null;
state.resizeHandle = null;
}
function handleKeyDown(e) {
if (
e.target.tagName === "INPUT" ||
e.target.tagName === "TEXTAREA" ||
e.target.isContentEditable
) {
return;
}
const isMod = e.ctrlKey || e.metaKey;
if (isMod && e.key === "z") {
e.preventDefault();
if (e.shiftKey) {
redo();
} else {
undo();
}
} else if (isMod && e.key === "y") {
e.preventDefault();
redo();
} else if (isMod && e.key === "c") {
e.preventDefault();
copyElement();
} else if (isMod && e.key === "x") {
e.preventDefault();
cutElement();
} else if (isMod && e.key === "v") {
e.preventDefault();
pasteElement();
} else if (isMod && e.key === "d") {
e.preventDefault();
duplicateElement();
} else if (isMod && e.key === "s") {
e.preventDefault();
savePresentation();
} else if (isMod && e.key === "a") {
e.preventDefault();
selectAll();
} else if (e.key === "Delete" || e.key === "Backspace") {
if (state.selectedElement) {
e.preventDefault();
deleteElement();
}
} else if (e.key === "Escape") {
clearSelection();
hideContextMenus();
if (state.isPresenting) {
exitPresentation();
}
} else if (e.key === "ArrowUp" && state.selectedElement) {
e.preventDefault();
state.selectedElement.y -= e.shiftKey ? 10 : 1;
updateElementPosition(state.selectedElement);
updateSelectionHandles();
} else if (e.key === "ArrowDown" && state.selectedElement) {
e.preventDefault();
state.selectedElement.y += e.shiftKey ? 10 : 1;
updateElementPosition(state.selectedElement);
updateSelectionHandles();
} else if (e.key === "ArrowLeft" && state.selectedElement) {
e.preventDefault();
state.selectedElement.x -= e.shiftKey ? 10 : 1;
updateElementPosition(state.selectedElement);
updateSelectionHandles();
} else if (e.key === "ArrowRight" && state.selectedElement) {
e.preventDefault();
state.selectedElement.x += e.shiftKey ? 10 : 1;
updateElementPosition(state.selectedElement);
updateSelectionHandles();
} else if (e.key === "F5") {
e.preventDefault();
startPresentation();
} else if (
e.key === "PageDown" ||
(e.key === "ArrowRight" && !state.selectedElement)
) {
e.preventDefault();
goToSlide(state.currentSlideIndex + 1);
} else if (
e.key === "PageUp" ||
(e.key === "ArrowLeft" && !state.selectedElement)
) {
e.preventDefault();
goToSlide(state.currentSlideIndex - 1);
}
}
function selectElement(element) {
state.selectedElement = element;
document.querySelectorAll(".slide-element.selected").forEach((el) => {
el.classList.remove("selected");
});
const node = document.querySelector(`[data-id="${element.id}"]`);
if (node) {
node.classList.add("selected");
}
updateSelectionHandles();
updatePropertiesPanel();
showPropertiesPanel();
}
function clearSelection() {
state.selectedElement = null;
document.querySelectorAll(".slide-element.selected").forEach((el) => {
el.classList.remove("selected");
});
hideSelectionHandles();
updatePropertiesPanel();
}
function updateSelectionHandles() {
if (!state.selectedElement || !elements.selectionHandles) {
hideSelectionHandles();
return;
}
const el = state.selectedElement;
elements.selectionHandles.classList.remove("hidden");
elements.selectionHandles.style.left = `${el.x}px`;
elements.selectionHandles.style.top = `${el.y}px`;
elements.selectionHandles.style.width = `${el.width}px`;
elements.selectionHandles.style.height = `${el.height}px`;
elements.selectionHandles.style.transform = `rotate(${el.rotation || 0}deg)`;
}
function hideSelectionHandles() {
if (elements.selectionHandles) {
elements.selectionHandles.classList.add("hidden");
}
}
function updateElementPosition(element) {
const node = document.querySelector(`[data-id="${element.id}"]`);
if (node) {
node.style.left = `${element.x}px`;
node.style.top = `${element.y}px`;
node.style.width = `${element.width}px`;
node.style.height = `${element.height}px`;
node.style.transform = `rotate(${element.rotation || 0}deg)`;
}
state.isDirty = true;
}
function updatePropertiesPanel() {
if (!state.selectedElement) {
document.getElementById("prop-x").value = "";
document.getElementById("prop-y").value = "";
document.getElementById("prop-width").value = "";
document.getElementById("prop-height").value = "";
document.getElementById("prop-rotation").value = 0;
document.getElementById("rotation-value").textContent = "0°";
document.getElementById("prop-opacity").value = 100;
document.getElementById("opacity-value").textContent = "100%";
return;
}
const el = state.selectedElement;
document.getElementById("prop-x").value = Math.round(el.x);
document.getElementById("prop-y").value = Math.round(el.y);
document.getElementById("prop-width").value = Math.round(el.width);
document.getElementById("prop-height").value = Math.round(el.height);
document.getElementById("prop-rotation").value = el.rotation || 0;
document.getElementById("rotation-value").textContent =
`${el.rotation || 0}°`;
const opacity = (el.style.opacity || 1) * 100;
document.getElementById("prop-opacity").value = opacity;
document.getElementById("opacity-value").textContent =
`${Math.round(opacity)}%`;
}
function showPropertiesPanel() {
if (elements.propertiesPanel) {
elements.propertiesPanel.classList.remove("collapsed");
}
}
function startTextEditing(element) {
const node = document.querySelector(`[data-id="${element.id}"]`);
if (!node) return;
node.contentEditable = true;
node.classList.add("editing");
node.focus();
const range = document.createRange();
range.selectNodeContents(node);
const sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
node.addEventListener(
"blur",
() => {
node.contentEditable = false;
node.classList.remove("editing");
element.content.text = node.innerText;
saveToHistory();
scheduleAutoSave();
renderThumbnails();
},
{ once: true },
);
}
function goToSlide(index) {
if (index < 0 || index >= state.slides.length) return;
state.currentSlideIndex = index;
renderCurrentSlide();
renderThumbnails();
updateSlideCounter();
broadcastChange("slideChange", { slideIndex: index });
}
function addSlide(layout = "title-content") {
const newSlide = createSlide(layout);
state.slides.splice(state.currentSlideIndex + 1, 0, newSlide);
state.currentSlideIndex++;
saveToHistory();
renderThumbnails();
renderCurrentSlide();
updateSlideCounter();
scheduleAutoSave();
broadcastChange("slideAdded", { slideIndex: state.currentSlideIndex });
}
function duplicateSlide() {
const currentSlide = state.slides[state.currentSlideIndex];
const duplicated = JSON.parse(JSON.stringify(currentSlide));
duplicated.id = generateId();
duplicated.elements.forEach((el) => {
el.id = generateId();
});
state.slides.splice(state.currentSlideIndex + 1, 0, duplicated);
state.currentSlideIndex++;
saveToHistory();
renderThumbnails();
renderCurrentSlide();
updateSlideCounter();
scheduleAutoSave();
}
function deleteSlide() {
if (state.slides.length <= 1) return;
state.slides.splice(state.currentSlideIndex, 1);
if (state.currentSlideIndex >= state.slides.length) {
state.currentSlideIndex = state.slides.length - 1;
}
saveToHistory();
renderThumbnails();
renderCurrentSlide();
updateSlideCounter();
scheduleAutoSave();
broadcastChange("slideDeleted", { slideIndex: state.currentSlideIndex });
}
function updateSlideCounter() {
const currentEl = document.getElementById("current-slide-num");
const totalEl = document.getElementById("total-slides-num");
if (currentEl) currentEl.textContent = state.currentSlideIndex + 1;
if (totalEl) totalEl.textContent = state.slides.length;
}
function showImageModal() {
const url = prompt("Enter image URL:");
if (url) {
addImage(url);
}
}
function addImage(url) {
const slide = state.slides[state.currentSlideIndex];
const imageElement = createImageElement(100, 100, 400, 300, url);
slide.elements.push(imageElement);
saveToHistory();
renderCurrentSlide();
selectElement(imageElement);
scheduleAutoSave();
}
function showShapeModal() {
addShape("rectangle");
}
function addShape(shapeType) {
const slide = state.slides[state.currentSlideIndex];
const shapeElement = createShapeElement(100, 100, 200, 150, shapeType, {
fill: "#3b82f6",
});
slide.elements.push(shapeElement);
saveToHistory();
renderCurrentSlide();
selectElement(shapeElement);
scheduleAutoSave();
}
function showChartModal() {
alert("Chart insertion coming soon!");
}
function addTable() {
alert("Table insertion coming soon!");
}
function setFontFamily(family) {
if (
state.selectedElement &&
state.selectedElement.element_type === "text"
) {
state.selectedElement.style.fontFamily = family;
renderCurrentSlide();
scheduleAutoSave();
}
}
function setFontSize(size) {
if (
state.selectedElement &&
state.selectedElement.element_type === "text"
) {
state.selectedElement.style.fontSize = parseInt(size, 10);
renderCurrentSlide();
scheduleAutoSave();
}
}
function toggleBold() {
if (
state.selectedElement &&
state.selectedElement.element_type === "text"
) {
state.selectedElement.style.fontWeight =
state.selectedElement.style.fontWeight === "bold" ? "normal" : "bold";
renderCurrentSlide();
scheduleAutoSave();
}
}
function toggleItalic() {
if (
state.selectedElement &&
state.selectedElement.element_type === "text"
) {
state.selectedElement.style.fontStyle =
state.selectedElement.style.fontStyle === "italic"
? "normal"
: "italic";
renderCurrentSlide();
scheduleAutoSave();
}
}
function toggleUnderline() {
if (
state.selectedElement &&
state.selectedElement.element_type === "text"
) {
state.selectedElement.style.textDecoration =
state.selectedElement.style.textDecoration === "underline"
? "none"
: "underline";
renderCurrentSlide();
scheduleAutoSave();
}
}
function toggleSidebar() {
if (elements.sidebar) {
elements.sidebar.classList.toggle("open");
}
}
function renamePresentation(name) {
state.presentationName = name;
scheduleAutoSave();
}
function searchPresentations(query) {
console.log("Searching:", query);
}
function startPresentation() {
state.isPresenting = true;
document.body.classList.add("presenting");
renderCurrentSlide();
}
function exitPresentation() {
state.isPresenting = false;
document.body.classList.remove("presenting");
renderCurrentSlide();
}
// Initialize when DOM is ready
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", init);
} else {
init();
}
// Expose functions globally
window.addSlide = addSlide;
window.addTextBox = addTextBox;
window.showImageModal = showImageModal;
window.showShapeModal = showShapeModal;
window.showChartModal = showChartModal;
window.addTable = addTable;
window.setFontFamily = setFontFamily;
window.setFontSize = setFontSize;
window.toggleBold = toggleBold;
window.toggleItalic = toggleItalic;
window.toggleUnderline = toggleUnderline;
window.toggleSidebar = toggleSidebar;
window.renamePresentation = renamePresentation;
window.searchPresentations = searchPresentations;
window.duplicateSlide = duplicateSlide;
window.deleteSlide = deleteSlide;
window.startPresentation = startPresentation;
window.exitPresentation = exitPresentation;
})();