/* Palm Guard: A mobile-friendly drawing mode for Excalidraw that prevents accidental palm touches by hiding UI controls and entering fullscreen mode. Perfect for drawing with a stylus on tablets. Features: - Enters fullscreen to maximize drawing space (configurable in plugin script settings) - Hides all UI controls to prevent accidental taps - Provides a minimal floating toolbar with toggle visibility button - Enables a completely distraction-free canvas even on desktop devices by hiding the main toolbar and all chrome while keeping a tiny movable toggle control (addresses immersive canvas / beyond Zen Mode request) - Draggable toolbar can be positioned anywhere on screen - Exit Palm Guard mode with a single tap - Press the hotkey you configured for this script in Obsidian's Hotkey settings (e.g., ALT+X) to toggle UI visibility; if no hotkey is set, use the on-screen toggle button. ![Palm Guard Script](https://youtu.be/A_udjVjgWN0) ```js */ if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("2.14.2")) { new Notice("This script requires a newer version of Excalidraw. Please install the latest version."); return; } function requestFullscreen() { const el = ea.targetView.ownerDocument.body; if (el.requestFullscreen) { el.requestFullscreen(); } else if (el.webkitRequestFullscreen) { el.webkitRequestFullscreen(); } ea.targetView.gotoFullscreen(); } function exitFullscreen() { const doc = ea.targetView.ownerDocument; if (doc.exitFullscreen) { doc.exitFullscreen(); } else if (doc.webkitExitFullscreen) { doc.webkitExitFullscreen(); } ea.targetView.exitFullscreen(); } async function run() { if(window.excalidrawPalmGuard) { window.excalidrawPalmGuard() return; } const modal = new ea.FloatingModal(ea.plugin.app); if (modal.bgEl) { modal.containerEl.removeChild(modal.bgEl); } modal.modalEl.style.borderRadius = "6px"; modal.contentEl.style.padding = "4px"; const FULLSCREEN = "Goto fullscreen?"; let settings = ea.getScriptSettings() || {}; if(!settings[FULLSCREEN]) { settings[FULLSCREEN] = { value: true }; await ea.setScriptSettings(settings); } //added only to clean up settings if someone installed the initial version of the script const HOTKEY_MODIFIERS = "PalmGuard Toggle UI Hotkey Modifiers"; const HOTKEY_KEY = "PalmGuard Toggle UI Hotkey Key"; if(settings[HOTKEY_MODIFIERS] || settings[HOTKEY_KEY]) { delete settings[HOTKEY_MODIFIERS]; delete settings[HOTKEY_KEY]; await ea.setScriptSettings(settings); } const enableFullscreen = settings[FULLSCREEN].value; // Initialize state let uiHidden = true; let currentIcon = "eye"; let layerUIWrapper = ea.targetView.contentEl.querySelector(".excalidraw.excalidraw-container > .layer-ui__wrapper"); const toolbar = ea.targetView.contentEl.querySelector(".excalidraw > .Island"); let toolbarActive = toolbar?.style.display === "block"; let prevHiddenState = false; // Function to toggle UI visibility const toggleUIVisibility = (hidden) => { if(hidden === prevHiddenState) return hidden; prevHiddenState = hidden; if (!!layerUIWrapper) { try { if(hidden) { layerUIWrapper.style.display = "none"; } else { layerUIWrapper.style.display = "block"; } } catch {}; } else { try{ const topBar = ea.targetView.containerEl.querySelector(".App-top-bar"); const bottomBar = ea.targetView.containerEl.querySelector(".App-bottom-bar"); const sidebarToggle = ea.targetView.containerEl.querySelector(".sidebar-toggle"); const plugins = ea.targetView.containerEl.querySelector(".plugins-container"); if(hidden) { if (toolbarActive && (toolbar?.style.display === "none")) { toolbarActive = false; } if (toolbarActive = toolbar?.style.display === "block") { toolbarActive = true; }; } const display = hidden ? "none" : ""; if (topBar) topBar.style.display = display; if (bottomBar) bottomBar.style.display = display; if (sidebarToggle) sidebarToggle.style.display = display; if (plugins) plugins.style.display = display; if (toolbarActive) toolbar.style.display = hidden ? "none" : "block"; modal.modalEl.style.opacity = hidden ? "0.4" : "0.8"; } catch {}; }; return hidden; }; // Enter fullscreen view mode if(enableFullscreen) { requestFullscreen (); } setTimeout(()=>toggleUIVisibility(true),100); // Create floating toolbar modal Object.assign(modal.modalEl.style, { width: "fit-content", minWidth: "fit-content", height: "fit-content", minHeight: "fit-content", paddingBottom: "4px", paddingTop: "16px", paddingRight: "4px", paddingLeft: "4px" }); modal.headerEl.style.display = "none"; // Configure modal modal.titleEl.setText(""); // No title for minimal UI // Create modal content modal.contentEl.createDiv({ cls: "palm-guard-toolbar" }, div => { const container = div.createDiv({ attr: { style: "display: flex; flex-direction: column; background-color: var(--background-secondary); border-radius: 4px;" } }); // Button container const buttonContainer = container.createDiv({ attr: { style: "display: flex; flex-wrap: wrap; gap: 4px; justify-content: center;" } }); // Toggle UI visibility button const toggleButton = buttonContainer.createEl("button", { cls: "palm-guard-btn clickable-icon", attr: { style: "background-color: var(--interactive-accent); color: var(--text-on-accent);" } }); toggleButton.innerHTML = ea.obsidian.getIcon("eye").outerHTML; // Keyboard hotkey listener (only acts if hotkey configured) window.excalidrawPalmGuard = () => toggleButton.click(); toggleButton.addEventListener("click", () => { uiHidden = !uiHidden; toggleUIVisibility(uiHidden); // Toggle icon currentIcon = uiHidden ? "eye" : "eye-off"; toggleButton.innerHTML = ea.obsidian.getIcon(currentIcon).outerHTML; }); // Exit button const exitButton = buttonContainer.createEl("button", { cls: "palm-guard-btn clickable-icon", attr: { style: "background-color: var(--background-secondary-alt); color: var(--text-normal);" } }); exitButton.innerHTML = ea.obsidian.getIcon("cross").outerHTML; exitButton.addEventListener("click", () => { modal.close(); }); // Add CSS div.createEl("style", { text: ` .palm-guard-btn:hover { filter: brightness(1.1); } .modal-close-button { display: none; } .palm-guard-btn { display: flex; justify-content: center; align-items: center; padding: 4px; border-radius: 10%; width: 2em; height: 2em; cursor: pointer; } ` }); }); const autocloseTimer = setInterval(()=>{ if(!ea.targetView) modal.close(); },1000); // Handle modal close (exit Palm Guard mode) modal.onClose = () => { // Show all UI elements toggleUIVisibility(false); // Exit fullscreen if(ea.targetView && enableFullscreen) { exitFullscreen(); } clearInterval(autocloseTimer); delete window.excalidrawPalmGuard; }; // Open the modal modal.open(); // Position the modal in the top left initially setTimeout(() => { const modalEl = modal.modalEl; const rect = ea.targetView.contentEl.getBoundingClientRect(); if (modalEl) { modalEl.style.left = `${rect.left+10}px`; modalEl.style.top = `${rect.top+10}px`; } }, 100); } run();