zsviczian / obsidian-excalidraw-plugin

A plugin to edit and view Excalidraw drawings in Obsidian
4.27k stars 233 forks source link

BUG: publish.css needs one little change - I think #2073

Closed HKohlhoff closed 1 month ago

HKohlhoff commented 1 month ago

Have you searched for existing issues (including closed ones)?

Does this bug persist in a new vault with only Excalidraw installed?

Your environment

No response

Describe the bug

In the publish.css-file there is one little typo - at least to my opinion: div.excalidraw-svg { /width: fit-content;/ height: 100%> } should read: div.excalidraw-svg { /width: fit-content;/ height: 100%; } change the > to ;

Steps to reproduce

No response

Expected behavior

No response

Additional context

No response

zsviczian commented 1 month ago

Nice catch. I published the update.

Btw, here's my updated publish.css and publish.js - in case you find it helpful:

@font-face {font-family: "Virgil";src: url("https://excalidraw.com/Virgil.woff2");}
@font-face {font-family: "Cascadia";src: url("https://excalidraw.com/Cascadia.woff2");}
@font-face {font-family: "Assistant";src: url("https://excalidraw.com/Assistant-Regular.woff2");}

div.markdown-embed-title {
  display: none;
}

div.markdown-embed {
  border: none;
  padding: 0px;
  background-color: inherit;
}

div.excalidraw-svg {
    /*width: fit-content;*/
    height: 100%;
}

svg.excalidraw-svg {
    max-width:100%;
    max-height: 90vh;
    width: var(--page-width);
}

svg.excalidraw-svg.ex-pageheight {
    width: initial;
    height: 100%;
}

svg.excalidraw-svg.ex-pagewidth {
    width: 90vw;
    height: initial;
}

.excalidraw-svg .text {
  width: 100%;
  text-align: center;
}

div.excalidraw-svg.enlarged {
    position: fixed;
    top: 0;
    left: 0;
    z-index: 10;
    width: 100%;
    height: 100%;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
}

body {
    --background-primary: #FFF6F0;
}

a.site-body-left-column-site-name {
    display: none;
}

div.site-footer {
    display: none;
}

svg a {
  text-decoration: none;
}

svg.excalidraw-svg.ex-w200 {
    width: 200px;
}

svg.excalidraw-svg.ex-w300 {
    width: 300px;
}

svg.excalidraw-svg.ex-w400 {
    width: 400px;
}

svg.excalidraw-svg.ex-w500 {
    width: 500px;
}

svg.excalidraw-svg.ex-w600 {
    width: 600px;
}

div.excalidraw-svg:has(.ex-center) {
  text-align: center;
}

svg.ex-16em { width: 16em !important; }
svg.ex-17em { width: 17em !important; }
svg.ex-18em { width: 18em !important; }
svg.ex-19em { width: 19em !important; }
svg.ex-20em { width: 20em !important; }
svg.ex-21em { width: 21em !important; }
svg.ex-22em { width: 22em !important; }
svg.ex-23em { width: 23em !important; }
svg.ex-24em { width: 24em !important; }
svg.ex-25em { width: 25em !important; }
svg.ex-26em { width: 26em !important; }
svg.ex-27em { width: 27em !important; }
svg.ex-28em { width: 28em !important; }
svg.ex-29em { width: 29em !important; }
svg.ex-30em { width: 30em !important; }
svg.ex-31em { width: 31em !important; }
svg.ex-32em { width: 32em !important; }
svg.ex-33em { width: 33em !important; }
svg.ex-34em { width: 34em !important; }
svg.ex-35em { width: 35em !important; }
svg.ex-36em { width: 36em !important; }
svg.ex-37em { width: 37em !important; }
svg.ex-38em { width: 38em !important; }
svg.ex-39em { width: 39em !important; }
svg.ex-40em { width: 40em !important; }
svg.ex-41em { width: 41em !important; }
svg.ex-42em { width: 42em !important; }
svg.ex-43em { width: 43em !important; }
svg.ex-44em { width: 44em !important; }
svg.ex-45em { width: 45em !important; }
svg.ex-46em { width: 46em !important; }
svg.ex-47em { width: 47em !important; }
svg.ex-48em { width: 48em !important; }
svg.ex-49em { width: 49em !important; }
svg.ex-50em { width: 50em !important; }
svg.ex-51em { width: 51em !important; }
svg.ex-52em { width: 52em !important; }
svg.ex-53em { width: 53em !important; }
svg.ex-54em { width: 54em !important; }
svg.ex-55em { width: 55em !important; }
svg.ex-56em { width: 56em !important; }
svg.ex-57em { width: 57em !important; }
svg.ex-58em { width: 58em !important; }
svg.ex-59em { width: 59em !important; }
svg.ex-60em { width: 60em !important; }
svg.ex-61em { width: 61em !important; }
svg.ex-62em { width: 62em !important; }
svg.ex-63em { width: 63em !important; }
svg.ex-64em { width: 64em !important; }
svg.ex-65em { width: 65em !important; }

span[alt="center"] {
  display: flex;
  justify-content: center;
}

span[alt="center-25"] {
  display: flex;
  justify-content: center;
}

img[alt="center-25"] {
  width: 25em;
}
const clickToEnlarge = "Click and hold to enlarge. SHIFT + wheel to zoom. ESC to reset.";
const clickToCollapse = "ESC to reset. Click and hold to collapse. SHIFT + wheel to zoom";

//check if in iFrame - if yes the page is assumed to be an embedded frame
if(window.self !== window.top) {
  const elements = [
    "div.site-body-right-column",
    "div.site-body-left-column",
    "div.site-header",
    "div.site-footer"
  ];
  elements.forEach(x=>{
    document.querySelectorAll(x).forEach(div=>{
      div.style.display = "none";
    });
  });
}

const baseUrl = `${window.location.origin}/`;

const [isDesktop, isMobile, isTablet] = (()=>{
  const userAgent = navigator.userAgent;
  const mobileKeywords = ['Mobile', 'Android', 'iPhone', 'iPad', 'Windows Phone'];

  const isMobile = mobileKeywords.some(keyword => userAgent.includes(keyword));
  const isTablet = /iPad/i.test(userAgent) || (isMobile && !/Mobile/i.test(userAgent));
  const isDesktop = !isMobile && !isTablet;

  return [isDesktop, isMobile, isTablet];
})();

const addNavigationToDiv = (container) => {
  const svgElement = container?.querySelector('.excalidraw-svg');
  if(!svgElement) return;
  container.addClass("excalidraw-svg");
  svgElement.removeAttribute("width");
  svgElement.removeAttribute("height");

  if(!isDesktop) return;

  const textDiv = document.createElement('div');
  textDiv.className = 'text';
  textDiv.textContent = clickToEnlarge;
  container.appendChild(textDiv);

  let isEnlarged = false;
  let timeout = null;
  let isReadyToPan = false;
  let isPanning = false;
  let zoomLevel = 1;
  let panX = 0;
  let panY = 0;
  let pinchStartDistance = 0;
  let panStartX = 0;
  let panStartY = 0;

  const clearEnlargeTimeout = () => {
    if(timeout) clearTimeout(timeout);
    timeout = null;
  }

  const enablePointerEvents = (val) => {
    svgElement.querySelectorAll("a").forEach(el=>{
      el.style.pointerEvents = val ? "all" : "none";
    });
  }

  const applyTransform = () => {
    svgElement.style.transform = `scale(${zoomLevel}) translate(${panX}px, ${panY}px)`;
    clearEnlargeTimeout();
  };

  //Wheel zoom
  svgElement.addEventListener('wheel', (event) => {
    if(!event.shiftKey ) return;
    if (event.deltaY > 0) {
    zoomLevel -= zoomLevel > 4 
      ? (zoomLevel > 6 
        ? (zoomLevel > 10 ? 0.4 : 0.3)
        : 0.2) 
      : 0.1;
    } else {
    zoomLevel += zoomLevel > 4 
      ? (zoomLevel > 6 
        ? (zoomLevel > 10 ? 0.4 : 0.3)
        : 0.2) 
      : 0.1;
    }
    applyTransform();
  });

  // Panning
  svgElement.addEventListener('mousedown', (event) => {
    isReadyToPan = true;
    panStartX = event.clientX;
    panStartY = event.clientY;
  });

  svgElement.addEventListener('mousemove', (event) => {
    const deltaX = event.clientX - panStartX;
    const deltaY = event.clientY - panStartY;
    const distance = Math.sqrt(deltaX**2+deltaY**2);
    if (isReadyToPan && (distance > 20)) {
      if(!isPanning) {
        enablePointerEvents(false);
        isPanning = true;
      }
      panX += deltaX/zoomLevel;
      panY += deltaY/zoomLevel;
      panStartX = event.clientX;
      panStartY = event.clientY;

      applyTransform();
    }
  });

  svgElement.addEventListener('mouseup', () => {
    enablePointerEvents(true);
    isPanning = false;
    isReadyToPan = false;
  });

  svgElement.addEventListener('mouseleave', () => {
    enablePointerEvents(true);
    isPanning = false;
    isReadyToPan = false;
  });

  //abort on Escape
  document.addEventListener('keydown', (event) => {
    if (event.key === 'Escape') {
      enablePointerEvents(true);
      isEnlarged = false;
      isPanning = false;
      isReadyToPan = false;
      container.classList.remove("enlarged");
      textDiv.textContent = clickToEnlarge;
      zoomLevel = 1;
      panX = 0;
      panY = 0;
      applyTransform();
    }
  });

  //Enlarge on long click
  svgElement.addEventListener('mouseup', () => clearEnlargeTimeout());
  svgElement.addEventListener('mousedown', () => {
    timeout = setTimeout(()=> {
      timeout = null;
      if(isPanning) return;
      isReadyToPan = false;
      if (isEnlarged) {
        // Collapse the image
        container.classList.remove("enlarged");
        textDiv.textContent = clickToEnlarge;
      } else {
        // Enlarge the image
        container.addClass("enlarged");
        textDiv.textContent = clickToCollapse;
      }
      isEnlarged = !isEnlarged;
    },1000);
  });

  applyTransform();
}

const processIMG = (img) => {
  const svgURL = img.src;
  const container = img.parentElement;

  fetch(svgURL)
    .then((response) => {
      if (response.ok) {
        return response.text();
      }
      throw new Error('Failed to fetch SVG');
    })
    .then((svgContent) => {    
      svgContainer = document.createElement('div');
      svgContainer.innerHTML = svgContent;
      svgContainer.querySelectorAll(`a[href^="obsidian://open?vault="`).forEach(el=>{
        el.setAttribute("href",unescape(el.getAttribute("href").replace(/.*&file=/,baseUrl).replaceAll(" ","+")));
      });
      svgContainer.querySelectorAll(`iframe[src^="obsidian://open?vault="`).forEach(el=>{
        el.setAttribute("src",unescape(el.getAttribute("src").replace(/.*&file=/,baseUrl).replaceAll(" ","+")));
      });
      container.removeChild(img);
      container.appendChild(svgContainer);
      addNavigationToDiv(svgContainer);

    })
    .catch((error) => {
      console.error('Error: ' + error);
    });
}

const processIframe = (iframe) => {
  const p = iframe.parentElement;
  if(!p || p.tagName.toLowerCase()!=="p") return;
  const div = p.parentElement;
  if(!div || div.tagName.toLowerCase()!=="div") return;

  div.style.maxWidth = '600px';
  div.style.maxHeight = '350px';
  div.style.overflow = 'hidden';

  p.style.position = 'relative';
  p.style.width = '100%';
  p.style.paddingBottom = '56.25%'; // 16:9 aspect ratio
  p.style.height = '0';

  // Apply inline styles to the iframe
  iframe.style.position = 'absolute';
  iframe.style.top = '0';
  iframe.style.left = '0';
  iframe.style.width = '100%';
  iframe.style.height = '100%';
  iframe.style.border = '0';
};

const addMutationObserver = () => {
  const targetElement = document.body;

  const handler = (mutationsList, observer) => {
    for (const mutation of mutationsList) {
      if (mutation.type === 'childList') {
        mutation.addedNodes.forEach(node => {
          if (node instanceof Element) {
            // Process SVG images as before
            if (node.querySelector(`img[alt$=".svg"]`)) {
              processIMG(node.querySelector(`img[alt$=".svg"]`));
            }
            // Process iframes with the class 'external-embed'
            const iframe = node.querySelector('iframe.external-embed');
            if (iframe) {
              processIframe(iframe);
            }
          }
        });
      }
    }
  };

  const observer = new MutationObserver(handler);
  const config = { childList: true, subtree: true };
  observer.observe(targetElement, config);
};

//process images after loading
document.body.querySelectorAll(`img[alt$=".svg"`).forEach(img => {
  processIMG(img);
});

addMutationObserver();

// -----------------------------------
// Page redirect for 404 pages
// -----------------------------------

const pageNotFoundObserver = new MutationObserver(() => {
  handlePageNotFound();
});

function handlePageNotFound() {
  const container = document.querySelector('div.published-container.has-not-found');
  if (container) {
    pageNotFoundObserver.disconnect(); // Stop observing once the .published-container is found

    const currentLocation = window.location.href;
    const newLocation = currentLocation.replace(/Hobbies\/Excalidraw\+Blog\//, '');

    // Update the content of the div.not-found-description
    const notFoundDescription = document.querySelector('div.not-found-description');
    if (notFoundDescription) {
      notFoundDescription.innerHTML = `
        <p style="text-align: center;">This page may have moved. You will be redirected in <span id="countdown" style="color:red;">10</span><span style="color:red;"> seconds</span>.</p>
        <p style="text-align: center;">If you arrived from an external link, please email details to 
        <a href="mailto:webmaster@visual-thinking-workshop.com">webmaster@visual-thinking-workshop.com</a>.</p>
        <p style="text-align: center;"><a href="${newLocation}">Click here to redirect now.</a></p>
      `;
    }

    // Countdown timer
    let countdown = 10;
    const countdownElement = document.getElementById('countdown');
    const interval = setInterval(() => {
      //abort if the user has moved to another page
      if(currentLocation !== window.location.href) {
          clearInterval(interval);
      }
      countdown -= 1;
      countdownElement.textContent = countdown;

      // Once the countdown reaches 0, redirect the page
      if (countdown <= 0) {
        clearInterval(interval);
        window.location.href = newLocation;
      }
    }, 1000); // Update every second

    console.log(`Redirecting to: ${newLocation}`);
    return true;
  }
  return false;
}

if(!handlePageNotFound()) {
  const target = document.querySelector("div.published-container") ?? document.body;
  pageNotFoundObserver.observe(target, { childList: true, attributes: true });
}