Skip to content

Instantly share code, notes, and snippets.

@zsviczian
Last active June 4, 2024 02:28
Show Gist options
  • Save zsviczian/33ff695d5b990de1ebe8b82e541c26ad to your computer and use it in GitHub Desktop.
Save zsviczian/33ff695d5b990de1ebe8b82e541c26ad to your computer and use it in GitHub Desktop.
Excalidraw Icon Library

<%* /*

*/
s = await navigator.clipboard.readText();
navigator.clipboard.writeText(s.replaceAll("\n","").replaceAll(/\s{2,}/g," "));
%>

/*

FILENAME_FILTER=/^icon -/i;
KEYWORD_GRABBER=/(?:icon -)?([^-]*)-?/i;
COLS=22;
HEIGHT=180;
WIDTH=180;
TEXTHEIGHT=40;
PADDING=50;
const api = ea.getExcalidrawAPI();
const f=ea.targetView.file;
icons=app.vault.getFiles().filter(f=>(f.extension!=='md'||ea.isExcalidrawFile(f))&&f.basename.toLowerCase().match(FILENAME_FILTER)).sort((a,b)=>a.basename.toLowerCase()<b.basename.toLowerCase()?-1:1);
const {
  zenModeEnabled,
  viewModeEnabled,
  linkOpacity,
  trayModeEnabled,
  penMode,
  penDetected,
  allowPinchZoom,
  allowWheelZoom,
  pinnedScripts,
  customPens
} = api.getAppState();
api.resetScene();
api.updateScene({appState: {
  zenModeEnabled,
  viewModeEnabled,
  linkOpacity,
  trayModeEnabled,
  penMode,
  penDetected,
  allowPinchZoom,
  allowWheelZoom,
  pinnedScripts,
  customPens
}});
col=0;
row=0;
for(icon of icons) {
  id=await ea.addImage(col*(WIDTH+PADDING),row*(HEIGHT+PADDING+TEXTHEIGHT),icon);
  if(f!==ea.targetView.file && ea.targetView?.getViewType?.()!=='excalidraw') return;
  if(!id) continue;
  keywords=icon.basename.match(KEYWORD_GRABBER)[1].trim();
  ea.style.verticalAlign='top';
  ea.style.textAlign='center';
  ea.style.fontSize=12;  
  el=ea.getElement(id);
  ratio=el.width/WIDTH;
  if(el.height/ratio>HEIGHT) ratio=el.height/HEIGHT;
  el.width=el.width/ratio;
  el.height=el.height/ratio;
  ea.style.strokeColor='black';
  boxID=ea.addText(col*(WIDTH+PADDING)-PADDING/2+10,row*(HEIGHT+PADDING+TEXTHEIGHT)+HEIGHT+PADDING/2-10,keywords,{
    width:WIDTH+PADDING-20,
    height:TEXTHEIGHT-20,
    boxPadding:10,
    textAlign:'center',
    textVerticalAlign:'top',
    boxStrokeColor:'transparent',
    box:'box'
  });		
  if(++col===COLS) {
    row++;
    col=0;
    await ea.addElementsToView(false,false,false);
    ea.targetView.clearDirty();
    ea.clear();
  }
}
await ea.addElementsToView(false,false,false);
ea.targetView.clearDirty();
api.zoomToFit();
api.updateContainerSize(ea.getViewElements().filter(el=>el.type==='rectangle'));
api.setActiveTool({type: 'hand'});
excalidraw-plugin excalidraw-onload-script
parsed
FILENAME_FILTER=/^icon -/i;KEYWORD_GRABBER=/(?:icon -)?([^-]*)-?/i;COLS=22;HEIGHT=180;WIDTH=180;TEXTHEIGHT=40;PADDING=50;const api = ea.getExcalidrawAPI();const f=ea.targetView.file;icons=app.vault.getFiles().filter(f=>(f.extension!=='md'||ea.isExcalidrawFile(f))&&f.basename.toLowerCase().match(FILENAME_FILTER)).sort((a,b)=>a.basename.toLowerCase()<b.basename.toLowerCase()?-1:1);const { zenModeEnabled, viewModeEnabled, linkOpacity, trayModeEnabled, penMode, penDetected, allowPinchZoom, allowWheelZoom, pinnedScripts, customPens} = api.getAppState();api.resetScene();api.updateScene({appState: { zenModeEnabled, viewModeEnabled, linkOpacity, trayModeEnabled, penMode, penDetected, allowPinchZoom, allowWheelZoom, pinnedScripts, customPens}});col=0;row=0;for(icon of icons) { id=await ea.addImage(col*(WIDTH+PADDING),row*(HEIGHT+PADDING+TEXTHEIGHT),icon); if(f!==ea.targetView.file && ea.targetView?.getViewType?.()!=='excalidraw') return; if(!id) continue; keywords=icon.basename.match(KEYWORD_GRABBER)[1].trim(); ea.style.verticalAlign='top'; ea.style.textAlign='center'; ea.style.fontSize=12; el=ea.getElement(id); ratio=el.width/WIDTH; if(el.height/ratio>HEIGHT) ratio=el.height/HEIGHT; el.width=el.width/ratio; el.height=el.height/ratio; ea.style.strokeColor='black'; boxID=ea.addText(col*(WIDTH+PADDING)-PADDING/2+10,row*(HEIGHT+PADDING+TEXTHEIGHT)+HEIGHT+PADDING/2-10,keywords,{ width:WIDTH+PADDING-20, height:TEXTHEIGHT-20, boxPadding:10, textAlign:'center', textVerticalAlign:'top', boxStrokeColor:'transparent', box:'box' }); if(++col===COLS) { row++; col=0; await ea.addElementsToView(false,false,false); ea.targetView.clearDirty(); ea.clear(); }}await ea.addElementsToView(false,false,false);ea.targetView.clearDirty();api.zoomToFit();api.updateContainerSize(ea.getViewElements().filter(el=>el.type==='rectangle'));api.setActiveTool({type: 'hand'});

#exclude

Text Elements

%%

Drawing

{
	"type": "excalidraw",
	"version": 2,
	"source": "https://github.com/zsviczian/obsidian-excalidraw-plugin/releases/tag/2.0.17",
	"elements": [],
	"appState": {
		"theme": "light",
		"viewBackgroundColor": "#ffffff",
		"currentItemStrokeColor": "#1e1e1e",
		"currentItemBackgroundColor": "transparent",
		"currentItemFillStyle": "solid",
		"currentItemStrokeWidth": 2,
		"currentItemStrokeStyle": "solid",
		"currentItemRoughness": 1,
		"currentItemOpacity": 100,
		"currentItemFontFamily": 1,
		"currentItemFontSize": 20,
		"currentItemTextAlign": "left",
		"currentItemStartArrowhead": null,
		"currentItemEndArrowhead": "arrow",
		"scrollX": 0,
		"scrollY": 0,
		"zoom": {
			"value": 1
		},
		"currentItemRoundness": "round",
		"gridSize": null,
		"gridColor": {
			"Bold": "#C9C9C9FF",
			"Regular": "#EDEDEDFF"
		},
		"currentStrokeOptions": null,
		"previousGridSize": null,
		"frameRendering": {
			"enabled": true,
			"clip": true,
			"name": true,
			"outline": true
		}
	},
	"files": {}
}

%%

@Stefan-59
Copy link

unfortunately, I am not able (yet) to do the debugging. Is simple copied the code from the above examples and the results are different. In both cases, it's the same vault, the same images (all image-xyz.png) in the same folder.
So again: it IS working with the code embedded in the drawing, so all good. BUT I'm just wondering why the standalone script would deliver different results. Sorry if I can't figure it out myself (yet), I'm just beginning to wrap my head around jacasript and the possibilities to debug with the console. If you have any tip for some recommended beginners (with the prospect of using js in Obsidian) guide, I'd be happy!

@Stefan-59
Copy link

unfortunately, I am not able (yet) to do the debugging with the console 🤷‍♂️.
I simply copied the code from the above examples and the results are different. In both cases, it's the same vault, the same images (all image-xyz.png) in the same folder.
So again: it IS working with the code embedded in the drawing, so all good. BUT I'm just wondering why the standalone script would deliver different results. Sorry if I can't figure it out myself (yet), I'm just beginning to wrap my head around JavaScript and the possibilities to debug with the console. If you have any tip for some recommended beginners guide (with the prospect of using js in Obsidian) , I'd be happy!

@zsviczian
Copy link
Author

so in both cases you see an Excalidraw drawing, but one has 18 images and the other 90?

did you modify the filter at the beginning of the code?

regarding learning Obsidian.js, I think small projects like this one are the best way.

one easy way to debug is to insert console.log(variable_name); at key points to see the value of variables.

another approach is to insert debugger; command in the code. doing this the script execution will stop when the program gets to that point (but only if developer console is open).

@Stefan-59
Copy link

Ok, now that's kind of weird. I inserted ' console.log(icons);' after the first command, now it works fine and I can see the (correct) number of icons in the console AND in the drawing.
Removed the console.log command and it still works perfectly.
I have no idea, what went wrong in my first attempts. And I promise, I didn't change anything (at least not knowingly). 🙈

So, thank you for your effort to help out and again 1.000++ thanks for your great plugins and videos!!! 🙏

@JuergenKaettnis
Copy link

Thank you for your great video and script. As always, I am on my Samsung tablet with Android. I put the script in the Script folder and I copied the file into my vault. Then I added the word 'Icon' just to a few images and the imported file loaded a library of the images. Then I renamed altogether around 30 files. After that I always get a message and button "Scroll back to content", but the canvas remains white. I cannot debug, because I just have the tablet and cannot start the developer console. And I am also not a programmer, just know very roughly how to debug. Any suggestions? Is the functionality limited on Android devices?

Because the script wasn't working, I moved most images to another folder, but the script still didn't work. I reinstalled the script and I recreated the file from the original source, but still didn't change for the better.

But knowledge discovery via Icons is something I were totally missing before and is quite interesting. And the icons add an additional input to remember and connect the information anyway.

@JuergenKaettnis
Copy link

JuergenKaettnis commented Jul 16, 2022

Update: I activated the script 'Zoom to fit selected elements' and there was my Icon library again. When the script wasn't showing anything, zoom factor display some strange symbols as well. Probably, the adding of the many icons lead to a kind of confusion.

I counted 24 files with 'icon' in file name (using Obsidian search) and most of them are displayed in the Icon Library, but not all.

By the way, in your video you opened the Icon Library in a kind of side pane. My Icon Library is more like a normal excalidraw file with icons in it. Am I doing s.th. wrong here? Or, again, is the functionality limited on Android devices?

@TheHuntyBadger
Copy link

Is there a filter option to point the script to a specific directory? I migrated from Joplin to Obsidian and I noticed it catches file with icon in the name. Even after changing it to "icon-"

@OXiOSDev
Copy link

Thank you very much for the icon library and the script.
The icon library works fine but I have some questions about the display of the icon library on your video:

  1. the name "Icon Library" does not display the ".excalibrain" as on mine (number 1)
  2. the excalidraw or image icons is on the left of the panel (number 2 and 3)
  3. on your panel, in the video, we also see, on the right of the "Icon Library", a number (number 4 and 5) and two opposite arrows (number 6 and 7).
    Display

I assume this is only a setting in Obsidian or in the Excalidraw setting but I can't find it.

Thanks for your work !!!!

@zsviczian
Copy link
Author

I did not notice these comments here:

  • The filter is now set for files beginning with "Icon -" or "icon -". You can change the filter by changing the FILENAME_FILTER=/^icon -/i; regular expression at the beginning of the script
  • @OXiOSDev: "Icon Library" vs. "Icon Library.excalidraw" is just a difference in filenames. Simply delete the ".excalidraw" part of the filename. It is not needed. You can set the default file naming in Excalidraw settings.
  • @JuergenKaettnis My library is also just an excalidraw file. You can drag any file (markdown, excalidraw, anything) to the side pane and pin it there.
  • @OXiOSDev regarding #3: I think those come from the PaneRelief plugin.

@OXiOSDev
Copy link

OXiOSDev commented Apr 2, 2023

@zsviczian Thank you very much for your answers

@LEEJOOPIL
Copy link

whein i install this script its not working.
so i debug my console, the line below

if(f!==ea.targetView.file && ea.targetView?.getViewType?.()!=="excalidraw") return;

at this line code error said return is not defined.

do you know what meaning is?

@zsviczian
Copy link
Author

@LEEJOOPIL
Did you download the .md file following this link? Icon Library.excalidraw.md

@zsviczian
Copy link
Author

I think the error message says return is not defined.

@LEEJOOPIL
Copy link

I used lcon library.excalidraw.md but still not working...
still return is not defined..

@briilt
Copy link

briilt commented May 26, 2024

Hi! Any way to automatically set "invert colors"?

@sjr001917
Copy link

I had the same issue as LEEJOOPIL.
Added the script to a new installation with only the bare minimum of community plugins.
The error is "Uncaught SyntaxError: Illegal return statement" and points to this code: if(f!==ea.targetView.file && ea.targetView?.getViewType?.()!=="excalidraw") return;
Removing that line doesn't help either.

@zsviczian
Copy link
Author

This sounds like a plugin is automatically modifying files, e.g. adding some metadata, etc, that creates a syntax error.
Disable other plugins. Delete and re-add the script. Is the error still happening?

Please share the output of command palette "Debug info" so I can see your definition of bare minimum.

@sjr001917
Copy link

Debug Info:
SYSTEM INFO:
Obsidian version: v1.5.12
Installer version: v1.5.3
Operating system: Windows 10 Home 10.0.22631
Login status: not logged in
Insider build toggle: off
Live preview: on
Base theme: dark
Community theme: AnuPpuccin v1.4.5
Snippets enabled: 3
Restricted mode: off
Plugins installed: 1
Plugins enabled: 1
1: Excalidraw v2.2.4

RECOMMENDATIONS:
Custom theme and snippets: for cosmetic issues, please first try updating your theme and disabling your snippets. If still not fixed, please try to make the issue happen in the Sandbox Vault or disable community theme and snippets.
Community plugins: for bugs, please first try updating all your plugins to latest. If still not fixed, please try to make the issue happen in the Sandbox Vault or disable community plugins.

I deleted all plugins except Excalidraw.
Deleted the Icon Library script and Icon Library.md from the vault.
Copied the raw Icon Library script.
Created a new empty drawing
opened the Console
added ea=excalidraw.automate
ea.setview('first')
pasted in the icon library script
and still the same error.
Drawing_2024-06-03_19 25 20_-_Icon-O_-_Obsidian_v12406-05

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