qraymo Garden

Powered by 🌱Roam Garden

roam/my-custom-setup

JS Plugins

const CARD_MODE_VERSION = "53e3a57";
window.URLScriptServer = `https://raw.githack.com/JimmyLv/styled-roam/${CARD_MODE_VERSION}/`;

var existing = document.getElementById("styled-roam");
if (!existing) {
  var extension = document.createElement("script");
  extension.src = window.URLScriptServer + "js/index.js";
  extension.id = "styled-roam";
  extension.async = true;
  extension.type = "text/javascript";
  document.getElementsByTagName("head")[0].appendChild(extension);
}

Page Synonyms

var existing = document.getElementById("page-synonyms");
if (!existing) {
  var extension = document.createElement("script");
  extension.src = "https://roamjs.com/page-synonyms.js";
  extension.id = "page-synonyms";
  extension.async = true;
  extension.type = "text/javascript";
  document.getElementsByTagName("head")[0].appendChild(extension);
}

Todont

var existing = document.getElementById("todont");
if (!existing) {
  var extension = document.createElement("script");
  extension.src = "https://roamjs.com/todont.js";
  extension.id = "todont";
  extension.async = true;
  extension.type = "text/javascript";
  document.getElementsByTagName("head")[0].appendChild(extension);
}

Article

var existing = document.getElementById("roamjs-article");
if (!existing) {
  var extension = document.createElement("script");
  extension.src = "https://roamjs.com/article.js";
  extension.id = "roamjs-article";
  extension.async = true;
  extension.type = "text/javascript";
  document.getElementsByTagName("head")[0].appendChild(extension);
}

Image Tagging

var existing = document.getElementById("roamjs-image-tagging");
if (!existing) {
  var extension = document.createElement("script");
  extension.src = "https://roamjs.com/image-tagging.js";
  extension.id = "roamjs-image-tagging";
  extension.async = true;
  extension.type = "text/javascript";
  document.getElementsByTagName("head")[0].appendChild(extension);
}

Google Calendar - Usage: {{import google calendar}}

var existing = document.getElementById("roamjs-google-calendar");
if (!existing) {
  var extension = document.createElement("script");
  extension.src = "https://roamjs.com/google-calendar.js";
  extension.id = "roamjs-google-calendar";
  extension.async = true;
  extension.type = "text/javascript";
  document.getElementsByTagName("head")[0].appendChild(extension);
}

Mouseless - CTRL+SHIFT+? Shortcuts Helper

var existing = document.getElementById("roamjs-mouseless");
if (!existing) {
  var extension = document.createElement("script");
  extension.src = "https://roamjs.com/mouseless.js";
  extension.id = "roamjs-mouseless";
  extension.async = true;
  extension.type = "text/javascript";
  document.getElementsByTagName("head")[0].appendChild(extension);
}

Pull References - Usage: {{pull references}}

var existing = document.getElementById("roamjs-pull-references");
if (!existing) {
  var extension = document.createElement("script");
  extension.src = "https://roamjs.com/pull-references.js";
  extension.id = "roamjs-pull-references";
  extension.async = true;
  extension.type = "text/javascript";
  document.getElementsByTagName("head")[0].appendChild(extension);
}

Query Builder - Usage: {{query builder}}

var existing = document.getElementById("roamjs-query-builder");
if (!existing) {
  var extension = document.createElement("script");
  extension.src = "https://roamjs.com/query-builder.js";
  extension.id = "roamjs-query-builder";
  extension.async = true;
  extension.type = "text/javascript";
  document.getElementsByTagName("head")[0].appendChild(extension);
}

Video

var existing = document.getElementById("roamjs-video");
if (!existing) {
  var extension = document.createElement("script");
  extension.src = "https://roamjs.com/video.js";
  extension.id = "roamjs-video";
  extension.async = true;
  extension.type = "text/javascript";
  document.getElementsByTagName("head")[0].appendChild(extension);
}

Wiki - Usage: {{wiki}}

var existing = document.getElementById("roamjs-wiki-data");
if (!existing) {
  var extension = document.createElement("script");
  extension.src = "https://roamjs.com/wiki-data.js";
  extension.id = "roamjs-wiki-data";
  extension.async = true;
  extension.type = "text/javascript";
  document.getElementsByTagName("head")[0].appendChild(extension);
}

WYSIWYG

To edit a block in WYSIWYG (what you see is what you get) mode, hold on the ALT key while clicking on it. Now you should be able to edit the content just like how it will be when rendered!

While in a block, you could hit ALT+w to toggle the block to and from WYSIWYG mode.

var existing = document.getElementById("roamjs-wysiwyg-mode");
if (!existing) {
  var extension = document.createElement("script");
  extension.src = "https://roamjs.com/wysiwyg-mode.js";
  extension.id = "roamjs-wysiwyg-mode";
  extension.async = true;
  extension.type = "text/javascript";
  document.getElementsByTagName("head")[0].appendChild(extension);
}

Emojis disabled

var existing = document.getElementById("emojis");
if (!existing) {
  var extension = document.createElement("script");
  extension.src = "https://roamjs.com/emojis.js";
  extension.id = "emojis";
  extension.async = true;
  extension.type = "text/javascript";
  document.getElementsByTagName("head")[0].appendChild(extension);
}

Sort References disabled

var existing = document.getElementById("sort-references");
if (!existing) {
  var extension = document.createElement("script");
  extension.src = "https://roamjs.com/sort-references.js";
  extension.id = "sort-references";
  extension.async = true;
  extension.type = "text/javascript";
  document.getElementsByTagName("head")[0].appendChild(extension);
}

Mindmap disabled

var existing = document.getElementById("mindmap");
if (!existing) {
  var extension = document.createElement("script");
  extension.src = "https://roamjs.com/mindmap.js";
  extension.id = "mindmap";
  extension.async = true;
  extension.type = "text/javascript";
  document.getElementsByTagName("head")[0].appendChild(extension);
}

Checkbox Strikeout

// roam/js code snippet to add crossing out completed todos
// also adds the CSS classname custom-strikethrough for css mods

;(()=>{
  
  if( typeof window.roamhacker_checkboxStrikeout != 'undefined') return;

  window.roamhacker_checkboxStrikeout = {};

  const scanCheckboxes = () => {
    document.querySelectorAll('.check-container input').forEach( (element)=>{
      var span = element.parentElement.parentElement.parentElement
      if(element.checked) {
        span.classList.add('custom-strikethrough')
        span.style.textDecoration='line-through'
      } else {
        span.classList.remove('custom-strikethrough')
        span.style.textDecoration='none'
      }
    })
  }

  scanCheckboxes()
  var observerCheckBoxes = new MutationObserver(scanCheckboxes);
  observerCheckBoxes.observe(document.querySelector('#app'), {
    childList: true,
    subtree: true
  })

})();

Show Weekday Under Page Titles

// Roam42 is a prerequisite for this code, as it uses Roam42 libraries
// Install & Config:
//   Add the code from this gist to a roam/js block in your roam graph and enable it
//   If you prefer foreign day names, modify the english in the Javascript below
//   CSS can be customized using #roam-title-day-banner CSS selector. Example:
//   #roam-title-day-banner {
//      color:silver;
//   }
//  to exclude sidebars, change 'var includeSidebars = true;' in the code o 
//  var includeSidebars = false;

;(()=>{
  
  if( typeof window.roamhacker_daytitle != 'undefined') return;

  window.roamhacker_daytitle = ()=> {
    var includeSidebars = true;
    var querystring =  includeSidebars ? '.rm-title-display, .sidebar-content .level2 a' : '.rm-title-display';
    const addDateToRoamTitleBanners = ()=> {
      setTimeout(()=>{
        document.querySelectorAll(querystring).forEach(title=>{
        try {
          if(title.nextElementSibling.classList.contains('roam-title-day-banner')) { 
            return;            
          }
        } catch(e) {}
        let pageDate = chrono.parseDate( title.innerText );
        if( pageDate!=null ) {            
          var weekdays = new Array( "Sunday", "Monday", "Tuesday", "Wednesday", 
                                    "Thursday", "Friday", "Saturday" );
          var day = pageDate.getDay();
          var div = document.createElement('DIV');
              div.className = 'roam-title-day-banner';
              div.innerText = weekdays[day];
              div.style.fontSize="10pt";
              div.style.top = -(Number(getComputedStyle(title).marginBottom.replace('px','')) + 6) + 'px';
              div.style.position="relative";
              div.style.fontStyle="oblique";
          title.insertAdjacentElement('afterend', div);
         }
        });
      },300)
    };

    setTimeout(()=>{
      addDateToRoamTitleBanners();
      const observerHeadings = new MutationObserver(addDateToRoamTitleBanners);
      observerHeadings.observe(document.querySelector('.roam-body'), {
        childList: true,
        subtree: true
      });
    },10000);
  }
  
  window.roamhacker_daytitle();
})();
 

Mobile JS

Mobile Todos

var existing = document.getElementById("mobile-todos");
if (!existing) {
  var extension = document.createElement("script");
  extension.src = "https://roamjs.com/mobile-todos.js";
  extension.id = "mobile-todos";
  extension.async = true;
  extension.type = "text/javascript";
  document.getElementsByTagName("head")[0].appendChild(extension);
}

/*
 * Viktor's Roam Mobile Double tap to Exluce Filters and Right click on bullets
 * version: 0.2
 * author: @ViktorTabori
 *
 * How to install it:
 *  - go to page [[roam/js]]
 *  - create a node with: { {[[roam/js]]}}
 *  - create a clode block under it, and change its type from clojure to javascript
 *  - allow the running of the javascript on the {{[[roam/js]]}} node
 *  - double tap a filter and it gets excluded
 *  - double tap on bullets to simulate right click
 */
window.ViktorMobileTap = window.ViktorMobileTap || (function(){
    // max wait for second tap in ms
    var maxWait = 200;
    var added;

    addListener();

    return {
        added: added,
        addListener: addListener,
        removeListener: removeListener,
    };

    function addListener() {
        if (added) return;
        added = true;
        document.addEventListener('touchstart', process, {passive: false, capture: true});
        console.log('** double tap installed **');
    }

    function removeListener() {
        if (!added) return;
        added = false;
        document.removeEventListener('touchstart', process, {passive: false, capture: true});
        console.log('** double tap STOPPED **');
    }

    function process(e) {
        var target = e.target;

        // check click on elements we plan to modify
        var action = [];
        try {
            // filter double tap to exclude
            if (target.classList.contains('bp3-button') && target.parentNode.parentNode.parentNode.parentNode.classList.contains('bp3-popover-content')) {
                action = [target, ['mousedown', 'click', 'mouseup'], true, {shiftKey:true}];
            } else
            // bullet double tap to right click
            if (target.className && target.className.match(/simple-bullet-(inner|outer)|controls|block-expand|rm-caret/i)) {
                // make clicking on the controls element
                while (target.classList && !target.classList.contains('controls') && target.parentNode) target = target.parentNode;
                var bound = target.getBoundingClientRect();
                action = [target, ['contextmenu'], false, {clientX:(bound.left+bound.width/2) , clientY:(bound.top+bound.height/2)}];
            }
        } catch(_) {
            return;
        }
        if (!action.length) return;

        // prevent propagation of event
        e.preventDefault();
        e.stopPropagation();

        // first tap
        if(!target.dataset.doubleTap) { 
            target.dataset.doubleTap = 1;
            setTimeout( function() { 
                if (target.dataset.doubleTap) {
                    // click on the original target if no double tap happened
                    simulateClick(e.target, ['mousedown', 'click', 'mouseup'], true); 
                }
                delete target.dataset.doubleTap;
            }, maxWait );
            return false;
        }

        //action on double tap goes below
        //console.log('*** Double tap detected ***',target);
        delete target.dataset.doubleTap;
        simulateClick.apply(null, action);

        // mouse click emulation
        function simulateClick(element, events, leftButton, opts) {
            events.forEach(function(type){
                element.dispatchEvent(new MouseEvent(type, {
                    view: window,
                    bubbles: true,
                    cancelable: true,
                    buttons: leftButton?1:2,
                    ...opts,
                }))
            });
        }
    }
})();
window.onhashchange = function () {
  var isPage = window.location.hash.split('/')[3] == 'page'
  if (isPage) {
    setTimeout(function () {
      var topBlock = document.getElementsByClassName("roam-block")[0].children[0]
      var blockValue = topBlock.innerText.split(':')
      if (blockValue.length == 2 && blockValue[0] == 'forward') {
        var attrs = topBlock.children[1].attributes[1]
        if (attrs.name == 'data-link-uid') {
          var newUrl = window.location.href.slice(0, -9) + attrs.value
          window.location.assign(newUrl)
        }
      }
    }, 1)
  }
}

For Studying disabled

/* Image Occlusion in Roam Research
   Author: Adam Krivka
   https://roamresearch.com/#/app/roam-depot-developers/page/fIR-UkBAL
*/

if (window.imageocclusion) {
  document.removeEventListener("click", imageocclusion.buttonClickHandler);
} else window.imageocclusion = {};

/* ====== SETTINGS (edit here!) ====== */

imageocclusion.COLORS = {
  primaryColor: "gray",
  secondaryColor: "#FEE8A6"
}

imageocclusion.HELPMESSAGE = "You can draw rectangles of two colors on the image with your left and right mouse buttons.";
imageocclusion.SUCCESSMESSAGE = "Image copied to clipboard.";
imageocclusion.FAILUREMESSAGE = "Copying unsuccessful. Right click the image to copy manually.";

/* ======= HELPER FUNCTIONS ====== */
imageocclusion.sleep = m => {
  var t = m ? m : 50;
  return new Promise(r => setTimeout(r, t))
};

imageocclusion.loadColors = () => {
  if (imageocclusion.COLORS &&
    imageocclusion.COLORS.primaryColor && imageocclusion.COLORS.primaryColor != "" && imageocclusion.COLORS.secondaryColor && imageocclusion.COLORS.secondaryColor != "")
    return [imageocclusion.COLORS.primaryColor, imageocclusion.COLORS.secondaryColor];
  else
    return ["gray", "#FEE8A6"];
};

/* ====== STYLES ====== */
imageocclusion.textColor = "#eeeeee";
imageocclusion.addBasicStyles = () => {
  var style = `
  .roam-lift-modal .bp3-dialog-container {
    flex-direction: column;
  }

  .roam-lift-modal .bp3-dialog {
    margin: 5px;
  }

  .imageocclusion-button {
    margin: 5px;
    height: 40px;
  }
  .imageocclusion-help-text {
    color: ${imageocclusion.textColor};
    width: 300px;
    height: 80px;
  }
  `
  var basicStyles = Object.assign(document.createElement("style"), {
    id: "imageocclusion-css-basic",
    innerHTML: style
  });
  document.getElementsByTagName("head")[0].appendChild(basicStyles);
};

// Actually loading them
imageocclusion.addBasicStyles();

/* ====== CLICK LISTENER ====== */
imageocclusion.buttonClickHandler = async (e) => {
  if (e.target.tagName === "IMG" && !e.target.classList.contains("rm-modal-img")) {
    await imageocclusion.sleep(500);

    var parentDiv = document.querySelector(".roam-lift-modal .bp3-dialog-container");
    if (!parentDiv) return;

    let container = Object.assign(document.createElement("div"), {
      classList: "flex-h-box imageocclusion-container",
    });
    container.style.cssText = `justify-content: space-between; pointer-events: all;`

    var startButton = Object.assign(document.createElement("button"), {
      classList: "bp3-button bp3-minimal imageocclusion-button",
      innerHTML: "Start Image Occlusion",
      onclick: () => { imageocclusion.start(startButton, container, parentDiv) }
    });
    startButton.style.cssText = "color: #aaaaaa";

    container.append(startButton);
    parentDiv.append(container);
  }
};

// Adding click listener
document.addEventListener("click", imageocclusion.buttonClickHandler, false);

/* ======= KEYBOARD LISTENER ====== */
imageocclusion.keyboardHandler = async (e) => {
  console.log("Key");
  console.log(e);
  if (e.ctrlKey && e.key === "z") {
    console.log("Trying to go back.");
    imageocclusion.rects.pop();
    imageocclusion.clear();
    imageocclusion.drawRects();
  }
}

// Adding keyboard listener
document.addEventListener("keydown", imageocclusion.keyboardHandler);

/* ====== MAIN ====== */
// Variables
imageocclusion.rects = [];
imageocclusion.canvas;
imageocclusion.draw;

// Functions
imageocclusion.start = (startButton, container, parentDiv) => {
  imageocclusion.rects = [];

  // Common
  let img = document.querySelector(".roam-lift-modal .bp3-dialog img");
  img.crossOrigin = "anonymous";
  const IMG = img
  // Removing start button
  startButton.remove();

  // Appending canvas
  let canvas = Object.assign(document.createElement("canvas"), {
    classList: "image-occlusion-canvas",
    width: IMG.width,
    height: IMG.height
  });

  canvas.style.cssText = `
    border: 1px solid #FEE8A6;
    z-index: 1000;
    position:absolute;
  `
  canvas.oncontextmenu = () => { return false };
  document.querySelector(".roam-lift-modal .bp3-dialog").append(canvas);
  imageocclusion.canvas = canvas;

  // Appending new UI elements
  let saveButton = Object.assign(document.createElement("button"), {
    classList: "bp3-button imageocclusion-button",
    innerHTML: "Save to Clipboard",
    onclick: () => { imageocclusion.save() }
  });
  let clearButton = Object.assign(document.createElement("button"), {
    classList: "bp3-button imageocclusion-button",
    innerHTML: "Clear canvas",
    onclick: () => { imageocclusion.clear(); imageocclusion.rects = []; }
  });
  let helpText = Object.assign(document.createElement("p"), {
    classList: "imageocclusion-help-text",
    innerHTML: imageocclusion.HELPMESSAGE
  });

  container.append(helpText, clearButton, saveButton);

  // Enabling drawing
  imageocclusion.startDrawing(IMG);
};

imageocclusion.drawRects = (draw) => {
  imageocclusion.rects.forEach((rect) => {
    imageocclusion.draw.fillStyle = rect.color;
    imageocclusion.draw.fillRect(rect.start.x, rect.start.y, rect.dim.x, rect.dim.y);
  })
};

imageocclusion.startDrawing = (IMG) => {
  imageocclusion.draw = imageocclusion.canvas.getContext("2d");

  imageocclusion.draw.fillStyle = "gray"

  var colors = imageocclusion.loadColors();
  var isDown;
  var rects = imageocclusion.rects;

  // Helper function
  var getMousePos = (canvas, e) => {
    var rect = canvas.getBoundingClientRect();
    return {
      x: Math.floor(e.clientX - rect.left),
      y: Math.floor(e.clientY - rect.top)
    };
  };

  var stop = (e) => {
    e.preventDefault();
    e.stopPropagation();
  };

  imageocclusion.canvas.addEventListener("mousedown", (e) => {
    stop(e);
    isDown = true;

    var color = (() => {
      switch (e.button) {
        case 2:
          return colors[1];
        default:
          return colors[0];
      }
    })();
    imageocclusion.rects.push({
      start: getMousePos(imageocclusion.canvas, e),
      dim: { x: 0, y: 0 },
      color: color
    })
  });

  imageocclusion.canvas.addEventListener("mouseup", (e) => {
    stop(e);
    isDown = false;
  });

  imageocclusion.canvas.addEventListener("mousemove", (e) => {
    stop(e);
    if (!isDown) return;
    imageocclusion.canvas.oncontextmenu = () => { return false; };  

    var last = imageocclusion.rects[imageocclusion.rects.length - 1];

    var mousePos = getMousePos(imageocclusion.canvas, e)
    var newDim = {
      x: mousePos.x - last.start.x,
      y: mousePos.y - last.start.y
    };

    imageocclusion.draw.clearRect(last.start.x, last.start.y, last.dim.x, last.dim.y);
    imageocclusion.draw.drawImage(IMG, 0, 0, IMG.width, IMG.height);

    last.dim = newDim;

    imageocclusion.drawRects();
  });
};

imageocclusion.clear = () => {
  imageocclusion.draw.clearRect(0, 0, imageocclusion.canvas.width, imageocclusion.canvas.height);
};

imageocclusion.log = async (message, color) => {
  var helpText = document.querySelector(".imageocclusion-help-text");
  if (!helpText) return;


  helpText.innerHTML = message;
  helpText.style.color = color;

  await imageocclusion.sleep(3000);

  helpText.innerHTML = imageocclusion.HELPMESSAGE;
  helpText.style.color = imageocclusion.textColor;
};

imageocclusion.save = async () => {
  // Copy to clipboard
  try {
    const response = await fetch(imageocclusion.canvas.toDataURL());
    const blob = await response.blob();
    await navigator.clipboard.write([new ClipboardItem({ 'image/png': blob })]);
    imageocclusion.log(imageocclusion.SUCCESSMESSAGE, "#AAE171")
  } catch (e) {
    imageocclusion.log(imageocclusion.FAILUREMESSAGE, "#F17E8E")
    imageocclusion.canvas.oncontextmenu = () => { };  
  }
};
var handleKeyEvent = async ev => {
  // console.log("alt: " + ev.altKey + "  shift: " + ev.shiftKey + "  ctrl: " + ev.ctrlKey + "   code: " + ev.code);
  if (ev.key == "$") {
    // Suppress default action
    ev.preventDefault();
    ev.stopImmediatePropagation();
    ev.stopPropagation();
      
    var txtarea = document.activeElement;
    var s = txtarea.selectionStart;
    // Check if next two characters are dollar signs "$$"
    if(txtarea.value.slice(s, s+2) == "$$") {
      // Move cursor
      txtarea.selectionStart = s + 2;
      return;
    } else {
      // Check if not in the middle of equation
      var dollarSplit = txtarea.value.split("$$");
      var counter = 0;
      var inEquation = false;
      for (let i = 0; i < dollarSplit.length; i++) {
        var l = dollarSplit[i].length;
        if (s > counter + 2 && s < counter + l) {
          inEquation = i % 2 == 1;
          break;
        }
        counter += l + 2;
      }
      if(!inEquation){
        // If not, add dollar signs
        var contentArray = txtarea.value.split("");
        contentArray.splice(txtarea.selectionStart,0,"$$$$");
        var setValue = Object.getOwnPropertyDescriptor(
        window.HTMLTextAreaElement.prototype, "value").set;
        setValue.call(txtarea, contentArray.join(""));
        var e = new Event("input", { bubbles: true });
        txtarea.dispatchEvent(e);
        await new Promise(r => setTimeout(r, 10));
        // Move cursor
        txtarea.selectionStart = s + 2;
        txtarea.selectionEnd = s + 2;
      }
    }
  }
};

// Add keybindings
document.addEventListener("keydown", handleKeyEvent);
roam/my-custom-setup