├── .gitignore ├── Inkscape-setting ├── Inkscape-figure-manager │ ├── __init__.py │ ├── main.py │ ├── picker.py │ └── template.svg └── Inkscape-shortcut-manager │ ├── Inkscape.json │ ├── init.lua │ └── karabiner-inkscape.jsonnet ├── LICENSE ├── README.md ├── VSCode-setting ├── Snippets │ ├── latex.hsnips │ └── latex.json ├── keybindings.json └── settings.json ├── demo ├── figures │ ├── Karabiner.png │ ├── conceal.png │ ├── inkscape_example.png │ ├── inkscape_shortcut.png │ ├── sourcecode-1.png │ ├── sourcecode-2.png │ ├── sourcecode-3.png │ └── sourcecode-4.png └── gifs │ ├── demo-create-inkscape.gif │ ├── demo-edit-inkscape.gif │ ├── dm.gif │ ├── fast.gif │ ├── fm.gif │ ├── integral.gif │ ├── integral2.gif │ ├── pmatrix.gif │ ├── quiver.gif │ ├── spell.gif │ ├── table.gif │ └── useful.gif └── preview.png /.gitignore: -------------------------------------------------------------------------------- 1 | social_preview.png 2 | -------------------------------------------------------------------------------- /Inkscape-setting/Inkscape-figure-manager/__init__.py: -------------------------------------------------------------------------------- 1 | from .main import cli 2 | 3 | __version__ = "1.0.8" 4 | -------------------------------------------------------------------------------- /Inkscape-setting/Inkscape-figure-manager/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | import re 5 | import logging 6 | import subprocess 7 | import warnings 8 | from pathlib import Path 9 | from shutil import copy 10 | from daemonize import Daemonize 11 | import click 12 | import platform 13 | from .picker import pick 14 | from appdirs import user_config_dir 15 | 16 | logging.basicConfig(level=os.environ.get("LOGLEVEL", "INFO")) 17 | log = logging.getLogger("inkscape-figures") 18 | 19 | 20 | def inkscape(path): 21 | with warnings.catch_warnings(): 22 | # leaving a subprocess running after interpreter exit raises a 23 | # warning in Python3.7+ 24 | warnings.simplefilter("ignore", ResourceWarning) 25 | subprocess.Popen(["inkscape", str(path)]) 26 | 27 | 28 | def indent(text, indentation=0): 29 | lines = text.split("\n") 30 | return "\n".join(" " * indentation + line for line in lines) 31 | 32 | 33 | def beautify(name): 34 | return name.replace("_", " ").replace("-", " ").title() 35 | 36 | 37 | def import_file(name, path): 38 | import importlib.util as util 39 | 40 | spec = util.spec_from_file_location(name, path) 41 | module = util.module_from_spec(spec) 42 | spec.loader.exec_module(module) 43 | return module 44 | 45 | 46 | # Load user config 47 | 48 | user_dir = Path(user_config_dir("inkscape-figures", "Castel")) 49 | 50 | if not user_dir.is_dir(): 51 | user_dir.mkdir() 52 | 53 | roots_file = user_dir / "roots" 54 | template = user_dir / "template.svg" 55 | config = user_dir / "config.py" 56 | 57 | if not roots_file.is_file(): 58 | roots_file.touch() 59 | 60 | if not template.is_file(): 61 | source = str(Path(__file__).parent / "template.svg") 62 | destination = str(template) 63 | copy(source, destination) 64 | 65 | 66 | def add_root(path): 67 | path = str(path) 68 | roots = get_roots() 69 | if path in roots: 70 | return None 71 | 72 | roots.append(path) 73 | roots_file.write_text("\n".join(roots)) 74 | 75 | 76 | def get_roots(): 77 | return [root for root in roots_file.read_text().split("\n") if root != ""] 78 | 79 | 80 | @click.group() 81 | def cli(): 82 | pass 83 | 84 | 85 | @cli.command() 86 | @click.option("--daemon/--no-daemon", default=True) 87 | def watch(daemon): 88 | """ 89 | Watches for figures. 90 | """ 91 | if platform.system() == "Linux": 92 | watcher_cmd = watch_daemon_inotify 93 | else: 94 | watcher_cmd = watch_daemon_fswatch 95 | 96 | if daemon: 97 | daemon = Daemonize(app="inkscape-figures", pid="/tmp/inkscape-figures.pid", action=watcher_cmd) 98 | daemon.start() 99 | log.info("Watching figures.") 100 | else: 101 | log.info("Watching figures.") 102 | watcher_cmd() 103 | 104 | 105 | def maybe_recompile_figure(filepath): 106 | filepath = Path(filepath) 107 | # A file has changed 108 | if filepath.suffix != ".svg": 109 | log.debug("File has changed, but is nog an svg {}".format(filepath.suffix)) 110 | return 111 | 112 | log.info("Recompiling %s", filepath) 113 | 114 | pdf_path = filepath.parent / (filepath.stem + ".pdf") 115 | name = filepath.stem 116 | 117 | inkscape_version = subprocess.check_output(["inkscape", "--version"], universal_newlines=True) 118 | log.debug(inkscape_version) 119 | 120 | # Convert 121 | # - 'Inkscape 0.92.4 (unknown)' to [0, 92, 4] 122 | # - 'Inkscape 1.1-dev (3a9df5bcce, 2020-03-18)' to [1, 1] 123 | # - 'Inkscape 1.0rc1' to [1, 0] 124 | inkscape_version = re.findall(r"[0-9.]+", inkscape_version)[0] 125 | inkscape_version_number = [int(part) for part in inkscape_version.split(".")] 126 | 127 | # Right-pad the array with zeros (so [1, 1] becomes [1, 1, 0]) 128 | inkscape_version_number = inkscape_version_number + [0] * (3 - len(inkscape_version_number)) 129 | 130 | # Tuple comparison is like version comparison 131 | if inkscape_version_number < [1, 0, 0]: 132 | command = [ 133 | "inkscape", 134 | "--export-area-page", 135 | "--export-dpi", 136 | "300", 137 | "--export-pdf", 138 | pdf_path, 139 | "--export-latex", 140 | filepath, 141 | ] 142 | else: 143 | command = [ 144 | "inkscape", 145 | filepath, 146 | "--export-area-page", 147 | "--export-dpi", 148 | "300", 149 | "--export-type=pdf", 150 | "--export-latex", 151 | "--export-filename", 152 | pdf_path, 153 | ] 154 | 155 | log.debug("Running command:") 156 | log.debug(" ".join(str(e) for e in command)) 157 | 158 | # Recompile the svg file 159 | completed_process = subprocess.run(command) 160 | 161 | if completed_process.returncode != 0: 162 | log.error("Return code %s", completed_process.returncode) 163 | else: 164 | log.debug("Command succeeded") 165 | 166 | 167 | def watch_daemon_inotify(): 168 | import inotify.adapters 169 | from inotify.constants import IN_CLOSE_WRITE 170 | 171 | while True: 172 | roots = get_roots() 173 | 174 | # Watch the file with contains the paths to watch 175 | # When this file changes, we update the watches. 176 | i = inotify.adapters.Inotify() 177 | i.add_watch(str(roots_file), mask=IN_CLOSE_WRITE) 178 | 179 | # Watch the actual figure directories 180 | log.info("Watching directories: " + ", ".join(get_roots())) 181 | for root in roots: 182 | try: 183 | i.add_watch(root, mask=IN_CLOSE_WRITE) 184 | except Exception: 185 | log.debug("Could not add root %s", root) 186 | 187 | for event in i.event_gen(yield_nones=False): 188 | (_, type_names, path, filename) = event 189 | 190 | # If the file containing figure roots has changes, update the 191 | # watches 192 | if path == str(roots_file): 193 | log.info("The roots file has been updated. Updating watches.") 194 | for root in roots: 195 | try: 196 | i.remove_watch(root) 197 | log.debug("Removed root %s", root) 198 | except Exception: 199 | log.debug("Could not remove root %s", root) 200 | # Break out of the loop, setting up new watches. 201 | break 202 | 203 | # A file has changed 204 | path = Path(path) / filename 205 | maybe_recompile_figure(path) 206 | 207 | 208 | def watch_daemon_fswatch(): 209 | while True: 210 | roots = get_roots() 211 | log.info("Watching directories: " + ", ".join(roots)) 212 | # Watch the figures directories, as weel as the config directory 213 | # containing the roots file (file containing the figures to the figure 214 | # directories to watch). If the latter changes, restart the watches. 215 | with warnings.catch_warnings(): 216 | warnings.simplefilter("ignore", ResourceWarning) 217 | p = subprocess.Popen(["fswatch", *roots, str(user_dir)], stdout=subprocess.PIPE, universal_newlines=True) 218 | 219 | while True: 220 | filepath = p.stdout.readline().strip() 221 | 222 | # If the file containing figure roots has changes, update the 223 | # watches 224 | if filepath == str(roots_file): 225 | log.info("The roots file has been updated. Updating watches.") 226 | p.terminate() 227 | log.debug("Removed main watch %s") 228 | break 229 | maybe_recompile_figure(filepath) 230 | 231 | 232 | @cli.command() 233 | @click.argument("title") 234 | @click.argument("root", default=os.getcwd(), type=click.Path(exists=False, file_okay=False, dir_okay=True)) 235 | def create(title, root): 236 | """ 237 | Creates a figure. 238 | First argument is the title of the figure 239 | Second argument is the figure directory. 240 | """ 241 | title = title.strip() 242 | file_name = title.replace(" ", "-").lower() + ".svg" 243 | figures = Path(root).absolute() 244 | if not figures.exists(): 245 | figures.mkdir() 246 | 247 | figure_path = figures / file_name 248 | 249 | # If a file with this name already exists, append a '2'. 250 | if figure_path.exists(): 251 | print(title + " 2") 252 | return 253 | 254 | copy(str(template), str(figure_path)) 255 | add_root(figures) 256 | inkscape(figure_path) 257 | 258 | 259 | @cli.command() 260 | @click.argument("root", default=os.getcwd(), type=click.Path(exists=True, file_okay=False, dir_okay=True)) 261 | def edit(root): 262 | """ 263 | Edits a figure. 264 | First argument is the figure directory. 265 | """ 266 | 267 | figures = Path(root).absolute() 268 | 269 | # Find svg files and sort them 270 | files = figures.glob("*.svg") 271 | files = sorted(files, key=lambda f: f.stat().st_mtime, reverse=True) 272 | 273 | # Open a selection dialog using a gui picker (choose) 274 | names = [beautify(f.stem) for f in files] 275 | _, index, selected = pick(names) 276 | if selected: 277 | path = files[index] 278 | add_root(figures) 279 | inkscape(path) 280 | 281 | 282 | if __name__ == "__main__": 283 | cli() 284 | -------------------------------------------------------------------------------- /Inkscape-setting/Inkscape-figure-manager/picker.py: -------------------------------------------------------------------------------- 1 | """ 2 | Call a command line fuzzy matcher to select a figure to edit. 3 | 4 | Current supported matchers are: 5 | 6 | * rofi for Linux platforms 7 | * choose (https://github.com/chipsenkbeil/choose) on MacOS 8 | """ 9 | import subprocess 10 | import platform 11 | 12 | SYSTEM_NAME = platform.system() 13 | 14 | 15 | def get_picker_cmd(picker_args=None, fuzzy=True): 16 | """ 17 | Create the shell command that will be run to start the picker. 18 | """ 19 | if SYSTEM_NAME == "Darwin": 20 | args = ["choose"] 21 | # args = ["choose", "-u", "-n", "15", "-c", "BB33B7", "-b", "BF44C8"] 22 | elif SYSTEM_NAME == "Linux": 23 | args = ["rofi", "-sort", "-no-levenshtein-sort"] 24 | if fuzzy: 25 | args += ["-matching", "fuzzy"] 26 | args += ["-dmenu", "-p", "Select Figure", "-format", "s", "-i", "-lines", "5"] 27 | else: 28 | raise ValueError("No supported picker for {}".format(SYSTEM_NAME)) 29 | 30 | if picker_args is not None: 31 | args += picker_args 32 | 33 | return [str(arg) for arg in args] 34 | 35 | 36 | def pick(options, picker_args=None, fuzzy=True): 37 | optionstr = "\n".join(option.replace("\n", " ") for option in options) 38 | cmd = get_picker_cmd(picker_args=picker_args, fuzzy=fuzzy) 39 | result = subprocess.run(cmd, input=optionstr, stdout=subprocess.PIPE, universal_newlines=True) 40 | returncode = result.returncode 41 | stdout = result.stdout.strip() 42 | 43 | selected = stdout.strip() 44 | try: 45 | index = [opt.strip() for opt in options].index(selected) 46 | except ValueError: 47 | index = -1 48 | 49 | if returncode == 0: 50 | key = 0 51 | elif returncode == 1: 52 | key = -1 53 | elif returncode > 9: 54 | key = returncode - 9 55 | 56 | return key, index, selected 57 | -------------------------------------------------------------------------------- /Inkscape-setting/Inkscape-figure-manager/template.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 43 | 51 | 52 | 54 | 55 | 57 | image/svg+xml 58 | 60 | 61 | 62 | 63 | 64 | 69 | 70 | -------------------------------------------------------------------------------- /Inkscape-setting/Inkscape-shortcut-manager/Inkscape.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Inkscape Shortcut Manager via hammerspoon (Gilles Castel, 2019)", 3 | "rules": [ 4 | { 5 | "description": "Inkscape Shortcut Manager via hammerspoon (Gilles Castel, 2019)", 6 | "manipulators": [ 7 | { 8 | "conditions": [ 9 | { 10 | "bundle_identifiers": [ 11 | "org.inkscape.Inkscape" 12 | ], 13 | "type": "frontmost_application_if" 14 | } 15 | ], 16 | "from": { 17 | "simultaneous": [ 18 | { 19 | "key_code": "s" 20 | }, 21 | { 22 | "key_code": "f" 23 | }, 24 | { 25 | "key_code": "g" 26 | } 27 | ], 28 | "simultaneous_options": { 29 | "key_up_when": "all" 30 | } 31 | }, 32 | "to": [ 33 | { 34 | "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"s+f+g\");create_svg_and_paste({\"s\",\"f\",\"g\"});'" 35 | } 36 | ], 37 | "type": "basic" 38 | }, 39 | { 40 | "conditions": [ 41 | { 42 | "bundle_identifiers": [ 43 | "org.inkscape.Inkscape" 44 | ], 45 | "type": "frontmost_application_if" 46 | } 47 | ], 48 | "from": { 49 | "simultaneous": [ 50 | { 51 | "key_code": "s" 52 | }, 53 | { 54 | "key_code": "f" 55 | }, 56 | { 57 | "key_code": "v" 58 | } 59 | ], 60 | "simultaneous_options": { 61 | "key_up_when": "all" 62 | } 63 | }, 64 | "to": [ 65 | { 66 | "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"s+f+v\");create_svg_and_paste({\"s\",\"f\",\"v\"});'" 67 | } 68 | ], 69 | "type": "basic" 70 | }, 71 | { 72 | "conditions": [ 73 | { 74 | "bundle_identifiers": [ 75 | "org.inkscape.Inkscape" 76 | ], 77 | "type": "frontmost_application_if" 78 | } 79 | ], 80 | "from": { 81 | "simultaneous": [ 82 | { 83 | "key_code": "s" 84 | }, 85 | { 86 | "key_code": "w" 87 | }, 88 | { 89 | "key_code": "g" 90 | } 91 | ], 92 | "simultaneous_options": { 93 | "key_up_when": "all" 94 | } 95 | }, 96 | "to": [ 97 | { 98 | "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"s+w+g\");create_svg_and_paste({\"s\",\"w\",\"g\"});'" 99 | } 100 | ], 101 | "type": "basic" 102 | }, 103 | { 104 | "conditions": [ 105 | { 106 | "bundle_identifiers": [ 107 | "org.inkscape.Inkscape" 108 | ], 109 | "type": "frontmost_application_if" 110 | } 111 | ], 112 | "from": { 113 | "simultaneous": [ 114 | { 115 | "key_code": "s" 116 | }, 117 | { 118 | "key_code": "w" 119 | }, 120 | { 121 | "key_code": "v" 122 | } 123 | ], 124 | "simultaneous_options": { 125 | "key_up_when": "all" 126 | } 127 | }, 128 | "to": [ 129 | { 130 | "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"s+w+v\");create_svg_and_paste({\"s\",\"w\",\"v\"});'" 131 | } 132 | ], 133 | "type": "basic" 134 | }, 135 | { 136 | "conditions": [ 137 | { 138 | "bundle_identifiers": [ 139 | "org.inkscape.Inkscape" 140 | ], 141 | "type": "frontmost_application_if" 142 | } 143 | ], 144 | "from": { 145 | "simultaneous": [ 146 | { 147 | "key_code": "s" 148 | }, 149 | { 150 | "key_code": "a" 151 | }, 152 | { 153 | "key_code": "g" 154 | } 155 | ], 156 | "simultaneous_options": { 157 | "key_up_when": "all" 158 | } 159 | }, 160 | "to": [ 161 | { 162 | "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"s+a+g\");create_svg_and_paste({\"s\",\"a\",\"g\"});'" 163 | } 164 | ], 165 | "type": "basic" 166 | }, 167 | { 168 | "conditions": [ 169 | { 170 | "bundle_identifiers": [ 171 | "org.inkscape.Inkscape" 172 | ], 173 | "type": "frontmost_application_if" 174 | } 175 | ], 176 | "from": { 177 | "simultaneous": [ 178 | { 179 | "key_code": "s" 180 | }, 181 | { 182 | "key_code": "a" 183 | }, 184 | { 185 | "key_code": "v" 186 | } 187 | ], 188 | "simultaneous_options": { 189 | "key_up_when": "all" 190 | } 191 | }, 192 | "to": [ 193 | { 194 | "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"s+a+v\");create_svg_and_paste({\"s\",\"a\",\"v\"});'" 195 | } 196 | ], 197 | "type": "basic" 198 | }, 199 | { 200 | "conditions": [ 201 | { 202 | "bundle_identifiers": [ 203 | "org.inkscape.Inkscape" 204 | ], 205 | "type": "frontmost_application_if" 206 | } 207 | ], 208 | "from": { 209 | "simultaneous": [ 210 | { 211 | "key_code": "s" 212 | }, 213 | { 214 | "key_code": "x" 215 | }, 216 | { 217 | "key_code": "g" 218 | } 219 | ], 220 | "simultaneous_options": { 221 | "key_up_when": "all" 222 | } 223 | }, 224 | "to": [ 225 | { 226 | "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"s+x+g\");create_svg_and_paste({\"s\",\"x\",\"g\"});'" 227 | } 228 | ], 229 | "type": "basic" 230 | }, 231 | { 232 | "conditions": [ 233 | { 234 | "bundle_identifiers": [ 235 | "org.inkscape.Inkscape" 236 | ], 237 | "type": "frontmost_application_if" 238 | } 239 | ], 240 | "from": { 241 | "simultaneous": [ 242 | { 243 | "key_code": "s" 244 | }, 245 | { 246 | "key_code": "x" 247 | }, 248 | { 249 | "key_code": "v" 250 | } 251 | ], 252 | "simultaneous_options": { 253 | "key_up_when": "all" 254 | } 255 | }, 256 | "to": [ 257 | { 258 | "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"s+x+v\");create_svg_and_paste({\"s\",\"x\",\"v\"});'" 259 | } 260 | ], 261 | "type": "basic" 262 | }, 263 | { 264 | "conditions": [ 265 | { 266 | "bundle_identifiers": [ 267 | "org.inkscape.Inkscape" 268 | ], 269 | "type": "frontmost_application_if" 270 | } 271 | ], 272 | "from": { 273 | "simultaneous": [ 274 | { 275 | "key_code": "d" 276 | }, 277 | { 278 | "key_code": "f" 279 | }, 280 | { 281 | "key_code": "g" 282 | } 283 | ], 284 | "simultaneous_options": { 285 | "key_up_when": "all" 286 | } 287 | }, 288 | "to": [ 289 | { 290 | "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"d+f+g\");create_svg_and_paste({\"d\",\"f\",\"g\"});'" 291 | } 292 | ], 293 | "type": "basic" 294 | }, 295 | { 296 | "conditions": [ 297 | { 298 | "bundle_identifiers": [ 299 | "org.inkscape.Inkscape" 300 | ], 301 | "type": "frontmost_application_if" 302 | } 303 | ], 304 | "from": { 305 | "simultaneous": [ 306 | { 307 | "key_code": "d" 308 | }, 309 | { 310 | "key_code": "f" 311 | }, 312 | { 313 | "key_code": "v" 314 | } 315 | ], 316 | "simultaneous_options": { 317 | "key_up_when": "all" 318 | } 319 | }, 320 | "to": [ 321 | { 322 | "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"d+f+v\");create_svg_and_paste({\"d\",\"f\",\"v\"});'" 323 | } 324 | ], 325 | "type": "basic" 326 | }, 327 | { 328 | "conditions": [ 329 | { 330 | "bundle_identifiers": [ 331 | "org.inkscape.Inkscape" 332 | ], 333 | "type": "frontmost_application_if" 334 | } 335 | ], 336 | "from": { 337 | "simultaneous": [ 338 | { 339 | "key_code": "d" 340 | }, 341 | { 342 | "key_code": "w" 343 | }, 344 | { 345 | "key_code": "g" 346 | } 347 | ], 348 | "simultaneous_options": { 349 | "key_up_when": "all" 350 | } 351 | }, 352 | "to": [ 353 | { 354 | "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"d+w+g\");create_svg_and_paste({\"d\",\"w\",\"g\"});'" 355 | } 356 | ], 357 | "type": "basic" 358 | }, 359 | { 360 | "conditions": [ 361 | { 362 | "bundle_identifiers": [ 363 | "org.inkscape.Inkscape" 364 | ], 365 | "type": "frontmost_application_if" 366 | } 367 | ], 368 | "from": { 369 | "simultaneous": [ 370 | { 371 | "key_code": "d" 372 | }, 373 | { 374 | "key_code": "w" 375 | }, 376 | { 377 | "key_code": "v" 378 | } 379 | ], 380 | "simultaneous_options": { 381 | "key_up_when": "all" 382 | } 383 | }, 384 | "to": [ 385 | { 386 | "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"d+w+v\");create_svg_and_paste({\"d\",\"w\",\"v\"});'" 387 | } 388 | ], 389 | "type": "basic" 390 | }, 391 | { 392 | "conditions": [ 393 | { 394 | "bundle_identifiers": [ 395 | "org.inkscape.Inkscape" 396 | ], 397 | "type": "frontmost_application_if" 398 | } 399 | ], 400 | "from": { 401 | "simultaneous": [ 402 | { 403 | "key_code": "d" 404 | }, 405 | { 406 | "key_code": "a" 407 | }, 408 | { 409 | "key_code": "g" 410 | } 411 | ], 412 | "simultaneous_options": { 413 | "key_up_when": "all" 414 | } 415 | }, 416 | "to": [ 417 | { 418 | "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"d+a+g\");create_svg_and_paste({\"d\",\"a\",\"g\"});'" 419 | } 420 | ], 421 | "type": "basic" 422 | }, 423 | { 424 | "conditions": [ 425 | { 426 | "bundle_identifiers": [ 427 | "org.inkscape.Inkscape" 428 | ], 429 | "type": "frontmost_application_if" 430 | } 431 | ], 432 | "from": { 433 | "simultaneous": [ 434 | { 435 | "key_code": "d" 436 | }, 437 | { 438 | "key_code": "a" 439 | }, 440 | { 441 | "key_code": "v" 442 | } 443 | ], 444 | "simultaneous_options": { 445 | "key_up_when": "all" 446 | } 447 | }, 448 | "to": [ 449 | { 450 | "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"d+a+v\");create_svg_and_paste({\"d\",\"a\",\"v\"});'" 451 | } 452 | ], 453 | "type": "basic" 454 | }, 455 | { 456 | "conditions": [ 457 | { 458 | "bundle_identifiers": [ 459 | "org.inkscape.Inkscape" 460 | ], 461 | "type": "frontmost_application_if" 462 | } 463 | ], 464 | "from": { 465 | "simultaneous": [ 466 | { 467 | "key_code": "d" 468 | }, 469 | { 470 | "key_code": "x" 471 | }, 472 | { 473 | "key_code": "g" 474 | } 475 | ], 476 | "simultaneous_options": { 477 | "key_up_when": "all" 478 | } 479 | }, 480 | "to": [ 481 | { 482 | "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"d+x+g\");create_svg_and_paste({\"d\",\"x\",\"g\"});'" 483 | } 484 | ], 485 | "type": "basic" 486 | }, 487 | { 488 | "conditions": [ 489 | { 490 | "bundle_identifiers": [ 491 | "org.inkscape.Inkscape" 492 | ], 493 | "type": "frontmost_application_if" 494 | } 495 | ], 496 | "from": { 497 | "simultaneous": [ 498 | { 499 | "key_code": "d" 500 | }, 501 | { 502 | "key_code": "x" 503 | }, 504 | { 505 | "key_code": "v" 506 | } 507 | ], 508 | "simultaneous_options": { 509 | "key_up_when": "all" 510 | } 511 | }, 512 | "to": [ 513 | { 514 | "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"d+x+v\");create_svg_and_paste({\"d\",\"x\",\"v\"});'" 515 | } 516 | ], 517 | "type": "basic" 518 | }, 519 | { 520 | "conditions": [ 521 | { 522 | "bundle_identifiers": [ 523 | "org.inkscape.Inkscape" 524 | ], 525 | "type": "frontmost_application_if" 526 | } 527 | ], 528 | "from": { 529 | "simultaneous": [ 530 | { 531 | "key_code": "e" 532 | }, 533 | { 534 | "key_code": "f" 535 | }, 536 | { 537 | "key_code": "g" 538 | } 539 | ], 540 | "simultaneous_options": { 541 | "key_up_when": "all" 542 | } 543 | }, 544 | "to": [ 545 | { 546 | "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"e+f+g\");create_svg_and_paste({\"e\",\"f\",\"g\"});'" 547 | } 548 | ], 549 | "type": "basic" 550 | }, 551 | { 552 | "conditions": [ 553 | { 554 | "bundle_identifiers": [ 555 | "org.inkscape.Inkscape" 556 | ], 557 | "type": "frontmost_application_if" 558 | } 559 | ], 560 | "from": { 561 | "simultaneous": [ 562 | { 563 | "key_code": "e" 564 | }, 565 | { 566 | "key_code": "f" 567 | }, 568 | { 569 | "key_code": "v" 570 | } 571 | ], 572 | "simultaneous_options": { 573 | "key_up_when": "all" 574 | } 575 | }, 576 | "to": [ 577 | { 578 | "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"e+f+v\");create_svg_and_paste({\"e\",\"f\",\"v\"});'" 579 | } 580 | ], 581 | "type": "basic" 582 | }, 583 | { 584 | "conditions": [ 585 | { 586 | "bundle_identifiers": [ 587 | "org.inkscape.Inkscape" 588 | ], 589 | "type": "frontmost_application_if" 590 | } 591 | ], 592 | "from": { 593 | "simultaneous": [ 594 | { 595 | "key_code": "e" 596 | }, 597 | { 598 | "key_code": "w" 599 | }, 600 | { 601 | "key_code": "g" 602 | } 603 | ], 604 | "simultaneous_options": { 605 | "key_up_when": "all" 606 | } 607 | }, 608 | "to": [ 609 | { 610 | "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"e+w+g\");create_svg_and_paste({\"e\",\"w\",\"g\"});'" 611 | } 612 | ], 613 | "type": "basic" 614 | }, 615 | { 616 | "conditions": [ 617 | { 618 | "bundle_identifiers": [ 619 | "org.inkscape.Inkscape" 620 | ], 621 | "type": "frontmost_application_if" 622 | } 623 | ], 624 | "from": { 625 | "simultaneous": [ 626 | { 627 | "key_code": "e" 628 | }, 629 | { 630 | "key_code": "w" 631 | }, 632 | { 633 | "key_code": "v" 634 | } 635 | ], 636 | "simultaneous_options": { 637 | "key_up_when": "all" 638 | } 639 | }, 640 | "to": [ 641 | { 642 | "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"e+w+v\");create_svg_and_paste({\"e\",\"w\",\"v\"});'" 643 | } 644 | ], 645 | "type": "basic" 646 | }, 647 | { 648 | "conditions": [ 649 | { 650 | "bundle_identifiers": [ 651 | "org.inkscape.Inkscape" 652 | ], 653 | "type": "frontmost_application_if" 654 | } 655 | ], 656 | "from": { 657 | "simultaneous": [ 658 | { 659 | "key_code": "e" 660 | }, 661 | { 662 | "key_code": "a" 663 | }, 664 | { 665 | "key_code": "g" 666 | } 667 | ], 668 | "simultaneous_options": { 669 | "key_up_when": "all" 670 | } 671 | }, 672 | "to": [ 673 | { 674 | "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"e+a+g\");create_svg_and_paste({\"e\",\"a\",\"g\"});'" 675 | } 676 | ], 677 | "type": "basic" 678 | }, 679 | { 680 | "conditions": [ 681 | { 682 | "bundle_identifiers": [ 683 | "org.inkscape.Inkscape" 684 | ], 685 | "type": "frontmost_application_if" 686 | } 687 | ], 688 | "from": { 689 | "simultaneous": [ 690 | { 691 | "key_code": "e" 692 | }, 693 | { 694 | "key_code": "a" 695 | }, 696 | { 697 | "key_code": "v" 698 | } 699 | ], 700 | "simultaneous_options": { 701 | "key_up_when": "all" 702 | } 703 | }, 704 | "to": [ 705 | { 706 | "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"e+a+v\");create_svg_and_paste({\"e\",\"a\",\"v\"});'" 707 | } 708 | ], 709 | "type": "basic" 710 | }, 711 | { 712 | "conditions": [ 713 | { 714 | "bundle_identifiers": [ 715 | "org.inkscape.Inkscape" 716 | ], 717 | "type": "frontmost_application_if" 718 | } 719 | ], 720 | "from": { 721 | "simultaneous": [ 722 | { 723 | "key_code": "e" 724 | }, 725 | { 726 | "key_code": "x" 727 | }, 728 | { 729 | "key_code": "g" 730 | } 731 | ], 732 | "simultaneous_options": { 733 | "key_up_when": "all" 734 | } 735 | }, 736 | "to": [ 737 | { 738 | "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"e+x+g\");create_svg_and_paste({\"e\",\"x\",\"g\"});'" 739 | } 740 | ], 741 | "type": "basic" 742 | }, 743 | { 744 | "conditions": [ 745 | { 746 | "bundle_identifiers": [ 747 | "org.inkscape.Inkscape" 748 | ], 749 | "type": "frontmost_application_if" 750 | } 751 | ], 752 | "from": { 753 | "simultaneous": [ 754 | { 755 | "key_code": "e" 756 | }, 757 | { 758 | "key_code": "x" 759 | }, 760 | { 761 | "key_code": "v" 762 | } 763 | ], 764 | "simultaneous_options": { 765 | "key_up_when": "all" 766 | } 767 | }, 768 | "to": [ 769 | { 770 | "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"e+x+v\");create_svg_and_paste({\"e\",\"x\",\"v\"});'" 771 | } 772 | ], 773 | "type": "basic" 774 | }, 775 | { 776 | "conditions": [ 777 | { 778 | "bundle_identifiers": [ 779 | "org.inkscape.Inkscape" 780 | ], 781 | "type": "frontmost_application_if" 782 | } 783 | ], 784 | "from": { 785 | "simultaneous": [ 786 | { 787 | "key_code": "s" 788 | }, 789 | { 790 | "key_code": "f" 791 | } 792 | ], 793 | "simultaneous_options": { 794 | "key_up_when": "all" 795 | } 796 | }, 797 | "to": [ 798 | { 799 | "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"s+f\");create_svg_and_paste({\"s\",\"f\"});'" 800 | } 801 | ], 802 | "type": "basic" 803 | }, 804 | { 805 | "conditions": [ 806 | { 807 | "bundle_identifiers": [ 808 | "org.inkscape.Inkscape" 809 | ], 810 | "type": "frontmost_application_if" 811 | } 812 | ], 813 | "from": { 814 | "simultaneous": [ 815 | { 816 | "key_code": "s" 817 | }, 818 | { 819 | "key_code": "w" 820 | } 821 | ], 822 | "simultaneous_options": { 823 | "key_up_when": "all" 824 | } 825 | }, 826 | "to": [ 827 | { 828 | "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"s+w\");create_svg_and_paste({\"s\",\"w\"});'" 829 | } 830 | ], 831 | "type": "basic" 832 | }, 833 | { 834 | "conditions": [ 835 | { 836 | "bundle_identifiers": [ 837 | "org.inkscape.Inkscape" 838 | ], 839 | "type": "frontmost_application_if" 840 | } 841 | ], 842 | "from": { 843 | "simultaneous": [ 844 | { 845 | "key_code": "s" 846 | }, 847 | { 848 | "key_code": "g" 849 | } 850 | ], 851 | "simultaneous_options": { 852 | "key_up_when": "all" 853 | } 854 | }, 855 | "to": [ 856 | { 857 | "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"s+g\");create_svg_and_paste({\"s\",\"g\"});'" 858 | } 859 | ], 860 | "type": "basic" 861 | }, 862 | { 863 | "conditions": [ 864 | { 865 | "bundle_identifiers": [ 866 | "org.inkscape.Inkscape" 867 | ], 868 | "type": "frontmost_application_if" 869 | } 870 | ], 871 | "from": { 872 | "simultaneous": [ 873 | { 874 | "key_code": "s" 875 | }, 876 | { 877 | "key_code": "v" 878 | } 879 | ], 880 | "simultaneous_options": { 881 | "key_up_when": "all" 882 | } 883 | }, 884 | "to": [ 885 | { 886 | "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"s+v\");create_svg_and_paste({\"s\",\"v\"});'" 887 | } 888 | ], 889 | "type": "basic" 890 | }, 891 | { 892 | "conditions": [ 893 | { 894 | "bundle_identifiers": [ 895 | "org.inkscape.Inkscape" 896 | ], 897 | "type": "frontmost_application_if" 898 | } 899 | ], 900 | "from": { 901 | "simultaneous": [ 902 | { 903 | "key_code": "s" 904 | }, 905 | { 906 | "key_code": "a" 907 | } 908 | ], 909 | "simultaneous_options": { 910 | "key_up_when": "all" 911 | } 912 | }, 913 | "to": [ 914 | { 915 | "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"s+a\");create_svg_and_paste({\"s\",\"a\"});'" 916 | } 917 | ], 918 | "type": "basic" 919 | }, 920 | { 921 | "conditions": [ 922 | { 923 | "bundle_identifiers": [ 924 | "org.inkscape.Inkscape" 925 | ], 926 | "type": "frontmost_application_if" 927 | } 928 | ], 929 | "from": { 930 | "simultaneous": [ 931 | { 932 | "key_code": "s" 933 | }, 934 | { 935 | "key_code": "x" 936 | } 937 | ], 938 | "simultaneous_options": { 939 | "key_up_when": "all" 940 | } 941 | }, 942 | "to": [ 943 | { 944 | "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"s+x\");create_svg_and_paste({\"s\",\"x\"});'" 945 | } 946 | ], 947 | "type": "basic" 948 | }, 949 | { 950 | "conditions": [ 951 | { 952 | "bundle_identifiers": [ 953 | "org.inkscape.Inkscape" 954 | ], 955 | "type": "frontmost_application_if" 956 | } 957 | ], 958 | "from": { 959 | "simultaneous": [ 960 | { 961 | "key_code": "d" 962 | }, 963 | { 964 | "key_code": "f" 965 | } 966 | ], 967 | "simultaneous_options": { 968 | "key_up_when": "all" 969 | } 970 | }, 971 | "to": [ 972 | { 973 | "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"d+f\");create_svg_and_paste({\"d\",\"f\"});'" 974 | } 975 | ], 976 | "type": "basic" 977 | }, 978 | { 979 | "conditions": [ 980 | { 981 | "bundle_identifiers": [ 982 | "org.inkscape.Inkscape" 983 | ], 984 | "type": "frontmost_application_if" 985 | } 986 | ], 987 | "from": { 988 | "simultaneous": [ 989 | { 990 | "key_code": "d" 991 | }, 992 | { 993 | "key_code": "w" 994 | } 995 | ], 996 | "simultaneous_options": { 997 | "key_up_when": "all" 998 | } 999 | }, 1000 | "to": [ 1001 | { 1002 | "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"d+w\");create_svg_and_paste({\"d\",\"w\"});'" 1003 | } 1004 | ], 1005 | "type": "basic" 1006 | }, 1007 | { 1008 | "conditions": [ 1009 | { 1010 | "bundle_identifiers": [ 1011 | "org.inkscape.Inkscape" 1012 | ], 1013 | "type": "frontmost_application_if" 1014 | } 1015 | ], 1016 | "from": { 1017 | "simultaneous": [ 1018 | { 1019 | "key_code": "d" 1020 | }, 1021 | { 1022 | "key_code": "g" 1023 | } 1024 | ], 1025 | "simultaneous_options": { 1026 | "key_up_when": "all" 1027 | } 1028 | }, 1029 | "to": [ 1030 | { 1031 | "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"d+g\");create_svg_and_paste({\"d\",\"g\"});'" 1032 | } 1033 | ], 1034 | "type": "basic" 1035 | }, 1036 | { 1037 | "conditions": [ 1038 | { 1039 | "bundle_identifiers": [ 1040 | "org.inkscape.Inkscape" 1041 | ], 1042 | "type": "frontmost_application_if" 1043 | } 1044 | ], 1045 | "from": { 1046 | "simultaneous": [ 1047 | { 1048 | "key_code": "d" 1049 | }, 1050 | { 1051 | "key_code": "v" 1052 | } 1053 | ], 1054 | "simultaneous_options": { 1055 | "key_up_when": "all" 1056 | } 1057 | }, 1058 | "to": [ 1059 | { 1060 | "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"d+v\");create_svg_and_paste({\"d\",\"v\"});'" 1061 | } 1062 | ], 1063 | "type": "basic" 1064 | }, 1065 | { 1066 | "conditions": [ 1067 | { 1068 | "bundle_identifiers": [ 1069 | "org.inkscape.Inkscape" 1070 | ], 1071 | "type": "frontmost_application_if" 1072 | } 1073 | ], 1074 | "from": { 1075 | "simultaneous": [ 1076 | { 1077 | "key_code": "d" 1078 | }, 1079 | { 1080 | "key_code": "a" 1081 | } 1082 | ], 1083 | "simultaneous_options": { 1084 | "key_up_when": "all" 1085 | } 1086 | }, 1087 | "to": [ 1088 | { 1089 | "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"d+a\");create_svg_and_paste({\"d\",\"a\"});'" 1090 | } 1091 | ], 1092 | "type": "basic" 1093 | }, 1094 | { 1095 | "conditions": [ 1096 | { 1097 | "bundle_identifiers": [ 1098 | "org.inkscape.Inkscape" 1099 | ], 1100 | "type": "frontmost_application_if" 1101 | } 1102 | ], 1103 | "from": { 1104 | "simultaneous": [ 1105 | { 1106 | "key_code": "d" 1107 | }, 1108 | { 1109 | "key_code": "x" 1110 | } 1111 | ], 1112 | "simultaneous_options": { 1113 | "key_up_when": "all" 1114 | } 1115 | }, 1116 | "to": [ 1117 | { 1118 | "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"d+x\");create_svg_and_paste({\"d\",\"x\"});'" 1119 | } 1120 | ], 1121 | "type": "basic" 1122 | }, 1123 | { 1124 | "conditions": [ 1125 | { 1126 | "bundle_identifiers": [ 1127 | "org.inkscape.Inkscape" 1128 | ], 1129 | "type": "frontmost_application_if" 1130 | } 1131 | ], 1132 | "from": { 1133 | "simultaneous": [ 1134 | { 1135 | "key_code": "e" 1136 | }, 1137 | { 1138 | "key_code": "f" 1139 | } 1140 | ], 1141 | "simultaneous_options": { 1142 | "key_up_when": "all" 1143 | } 1144 | }, 1145 | "to": [ 1146 | { 1147 | "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"e+f\");create_svg_and_paste({\"e\",\"f\"});'" 1148 | } 1149 | ], 1150 | "type": "basic" 1151 | }, 1152 | { 1153 | "conditions": [ 1154 | { 1155 | "bundle_identifiers": [ 1156 | "org.inkscape.Inkscape" 1157 | ], 1158 | "type": "frontmost_application_if" 1159 | } 1160 | ], 1161 | "from": { 1162 | "simultaneous": [ 1163 | { 1164 | "key_code": "e" 1165 | }, 1166 | { 1167 | "key_code": "w" 1168 | } 1169 | ], 1170 | "simultaneous_options": { 1171 | "key_up_when": "all" 1172 | } 1173 | }, 1174 | "to": [ 1175 | { 1176 | "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"e+w\");create_svg_and_paste({\"e\",\"w\"});'" 1177 | } 1178 | ], 1179 | "type": "basic" 1180 | }, 1181 | { 1182 | "conditions": [ 1183 | { 1184 | "bundle_identifiers": [ 1185 | "org.inkscape.Inkscape" 1186 | ], 1187 | "type": "frontmost_application_if" 1188 | } 1189 | ], 1190 | "from": { 1191 | "simultaneous": [ 1192 | { 1193 | "key_code": "e" 1194 | }, 1195 | { 1196 | "key_code": "g" 1197 | } 1198 | ], 1199 | "simultaneous_options": { 1200 | "key_up_when": "all" 1201 | } 1202 | }, 1203 | "to": [ 1204 | { 1205 | "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"e+g\");create_svg_and_paste({\"e\",\"g\"});'" 1206 | } 1207 | ], 1208 | "type": "basic" 1209 | }, 1210 | { 1211 | "conditions": [ 1212 | { 1213 | "bundle_identifiers": [ 1214 | "org.inkscape.Inkscape" 1215 | ], 1216 | "type": "frontmost_application_if" 1217 | } 1218 | ], 1219 | "from": { 1220 | "simultaneous": [ 1221 | { 1222 | "key_code": "e" 1223 | }, 1224 | { 1225 | "key_code": "v" 1226 | } 1227 | ], 1228 | "simultaneous_options": { 1229 | "key_up_when": "all" 1230 | } 1231 | }, 1232 | "to": [ 1233 | { 1234 | "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"e+v\");create_svg_and_paste({\"e\",\"v\"});'" 1235 | } 1236 | ], 1237 | "type": "basic" 1238 | }, 1239 | { 1240 | "conditions": [ 1241 | { 1242 | "bundle_identifiers": [ 1243 | "org.inkscape.Inkscape" 1244 | ], 1245 | "type": "frontmost_application_if" 1246 | } 1247 | ], 1248 | "from": { 1249 | "simultaneous": [ 1250 | { 1251 | "key_code": "e" 1252 | }, 1253 | { 1254 | "key_code": "a" 1255 | } 1256 | ], 1257 | "simultaneous_options": { 1258 | "key_up_when": "all" 1259 | } 1260 | }, 1261 | "to": [ 1262 | { 1263 | "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"e+a\");create_svg_and_paste({\"e\",\"a\"});'" 1264 | } 1265 | ], 1266 | "type": "basic" 1267 | }, 1268 | { 1269 | "conditions": [ 1270 | { 1271 | "bundle_identifiers": [ 1272 | "org.inkscape.Inkscape" 1273 | ], 1274 | "type": "frontmost_application_if" 1275 | } 1276 | ], 1277 | "from": { 1278 | "simultaneous": [ 1279 | { 1280 | "key_code": "e" 1281 | }, 1282 | { 1283 | "key_code": "x" 1284 | } 1285 | ], 1286 | "simultaneous_options": { 1287 | "key_up_when": "all" 1288 | } 1289 | }, 1290 | "to": [ 1291 | { 1292 | "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"e+x\");create_svg_and_paste({\"e\",\"x\"});'" 1293 | } 1294 | ], 1295 | "type": "basic" 1296 | }, 1297 | { 1298 | "conditions": [ 1299 | { 1300 | "bundle_identifiers": [ 1301 | "org.inkscape.Inkscape" 1302 | ], 1303 | "type": "frontmost_application_if" 1304 | } 1305 | ], 1306 | "from": { 1307 | "simultaneous": [ 1308 | { 1309 | "key_code": "spacebar" 1310 | }, 1311 | { 1312 | "key_code": "s" 1313 | } 1314 | ], 1315 | "simultaneous_options": { 1316 | "key_up_when": "all" 1317 | } 1318 | }, 1319 | "to": [ 1320 | { 1321 | "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"spacebar+s\");create_svg_and_paste({\"spacebar\",\"s\"});'" 1322 | } 1323 | ], 1324 | "type": "basic" 1325 | }, 1326 | { 1327 | "conditions": [ 1328 | { 1329 | "bundle_identifiers": [ 1330 | "org.inkscape.Inkscape" 1331 | ], 1332 | "type": "frontmost_application_if" 1333 | } 1334 | ], 1335 | "from": { 1336 | "simultaneous": [ 1337 | { 1338 | "key_code": "spacebar" 1339 | }, 1340 | { 1341 | "key_code": "d" 1342 | } 1343 | ], 1344 | "simultaneous_options": { 1345 | "key_up_when": "all" 1346 | } 1347 | }, 1348 | "to": [ 1349 | { 1350 | "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"spacebar+d\");create_svg_and_paste({\"spacebar\",\"d\"});'" 1351 | } 1352 | ], 1353 | "type": "basic" 1354 | }, 1355 | { 1356 | "conditions": [ 1357 | { 1358 | "bundle_identifiers": [ 1359 | "org.inkscape.Inkscape" 1360 | ], 1361 | "type": "frontmost_application_if" 1362 | } 1363 | ], 1364 | "from": { 1365 | "simultaneous": [ 1366 | { 1367 | "key_code": "spacebar" 1368 | }, 1369 | { 1370 | "key_code": "e" 1371 | } 1372 | ], 1373 | "simultaneous_options": { 1374 | "key_up_when": "all" 1375 | } 1376 | }, 1377 | "to": [ 1378 | { 1379 | "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"spacebar+e\");create_svg_and_paste({\"spacebar\",\"e\"});'" 1380 | } 1381 | ], 1382 | "type": "basic" 1383 | }, 1384 | { 1385 | "conditions": [ 1386 | { 1387 | "bundle_identifiers": [ 1388 | "org.inkscape.Inkscape" 1389 | ], 1390 | "type": "frontmost_application_if" 1391 | } 1392 | ], 1393 | "from": { 1394 | "simultaneous": [ 1395 | { 1396 | "key_code": "spacebar" 1397 | }, 1398 | { 1399 | "key_code": "f" 1400 | } 1401 | ], 1402 | "simultaneous_options": { 1403 | "key_up_when": "all" 1404 | } 1405 | }, 1406 | "to": [ 1407 | { 1408 | "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"spacebar+f\");create_svg_and_paste({\"spacebar\",\"f\"});'" 1409 | } 1410 | ], 1411 | "type": "basic" 1412 | }, 1413 | { 1414 | "conditions": [ 1415 | { 1416 | "bundle_identifiers": [ 1417 | "org.inkscape.Inkscape" 1418 | ], 1419 | "type": "frontmost_application_if" 1420 | } 1421 | ], 1422 | "from": { 1423 | "simultaneous": [ 1424 | { 1425 | "key_code": "spacebar" 1426 | }, 1427 | { 1428 | "key_code": "b" 1429 | } 1430 | ], 1431 | "simultaneous_options": { 1432 | "key_up_when": "all" 1433 | } 1434 | }, 1435 | "to": [ 1436 | { 1437 | "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"spacebar+b\");create_svg_and_paste({\"spacebar\",\"b\"});'" 1438 | } 1439 | ], 1440 | "type": "basic" 1441 | }, 1442 | { 1443 | "conditions": [ 1444 | { 1445 | "bundle_identifiers": [ 1446 | "org.inkscape.Inkscape" 1447 | ], 1448 | "type": "frontmost_application_if" 1449 | } 1450 | ], 1451 | "from": { 1452 | "simultaneous": [ 1453 | { 1454 | "key_code": "spacebar" 1455 | }, 1456 | { 1457 | "key_code": "w" 1458 | } 1459 | ], 1460 | "simultaneous_options": { 1461 | "key_up_when": "all" 1462 | } 1463 | }, 1464 | "to": [ 1465 | { 1466 | "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"spacebar+w\");create_svg_and_paste({\"spacebar\",\"w\"});'" 1467 | } 1468 | ], 1469 | "type": "basic" 1470 | } 1471 | ] 1472 | } 1473 | ] 1474 | } -------------------------------------------------------------------------------- /Inkscape-setting/Inkscape-shortcut-manager/init.lua: -------------------------------------------------------------------------------- 1 | -- Make cli command `hs` available: 2 | -- After an update of hammerspoon run following two commmands once in the hammerspoon console 3 | -- hs.ipc.cliUninstall(); hs.ipc.cliInstall() 4 | require("hs.ipc") 5 | 6 | local function intersect(m,n) 7 | local r={} 8 | for i,v1 in ipairs(m) do 9 | for k,v2 in pairs(n) do 10 | if (v1==v2) then 11 | return true 12 | end 13 | end 14 | end 15 | return false 16 | end 17 | 18 | local function has_value (tab, val) 19 | for index, value in ipairs(tab) do 20 | if value == val then 21 | return true 22 | end 23 | end 24 | return false 25 | end 26 | 27 | function create_svg_and_paste(keys) 28 | 29 | pt = 1.327 -- pixels 30 | w = 2 * pt 31 | thick_width = 4 * pt 32 | very_thick_width = 8 * pt 33 | 34 | style = {} 35 | style["stroke-opacity"] = 1 36 | 37 | if intersect({"s", "a", "d", "g", "h", "x", "e"}, keys) 38 | then 39 | style["stroke"] = "black" 40 | style["stroke-width"] = w 41 | style["marker-end"] = "none" 42 | style["marker-start"] = "none" 43 | style["stroke-dasharray"] = "none" 44 | else 45 | style["stroke"] = "none" 46 | end 47 | 48 | if has_value(keys, "g") 49 | then 50 | w = thick_width 51 | style["stroke-width"] = w 52 | end 53 | 54 | if has_value(keys, "v") 55 | then 56 | w = very_thick_width 57 | style["stroke-width"] = w 58 | end 59 | 60 | if has_value(keys, "a") 61 | then 62 | style['marker-end'] = 'url(#marker-arrow-' .. tostring(w) .. ')' 63 | end 64 | 65 | if has_value(keys, "x") 66 | then 67 | style['marker-start'] = 'url(#marker-arrow-' .. tostring(w) .. ')' 68 | style['marker-end'] = 'url(#marker-arrow-' .. tostring(w) .. ')' 69 | end 70 | 71 | if has_value(keys, "d") 72 | then 73 | style['stroke-dasharray'] = tostring(w) .. ',' .. tostring(2*pt) 74 | end 75 | 76 | if has_value(keys, "e") 77 | then 78 | style['stroke-dasharray'] = tostring(3*pt) .. ',' .. tostring(3*pt) 79 | end 80 | 81 | if has_value(keys, "f") 82 | then 83 | style['fill'] = 'black' 84 | style['fill-opacity'] = 0.12 85 | end 86 | 87 | if has_value(keys, "b") 88 | then 89 | style['fill'] = 'black' 90 | style['fill-opacity'] = 1 91 | end 92 | 93 | if has_value(keys, "w") 94 | then 95 | style['fill'] = 'white' 96 | style['fill-opacity'] = 1 97 | end 98 | 99 | if intersect(keys, {"f", "b", "w"}) 100 | then 101 | style['marker-end'] = 'none' 102 | style['marker-start'] = 'none' 103 | end 104 | 105 | if not intersect(keys, {"f", "b", "w"}) 106 | then 107 | style['fill'] = 'none' 108 | style['fill-opacity'] = 1 109 | end 110 | 111 | svg = [[ 112 | 113 | 114 | ]] 115 | 116 | if (style['marker-end'] ~= nil and style['marker-end'] ~= 'none') or 117 | (style['marker-start'] ~= nil and style['marker-start'] ~= 'none') 118 | then 119 | svgtemp = [[ 120 | 121 | ' .. "\n" 127 | 128 | svgtemp = svgtemp .. ' ' .. "\n" 129 | svg = svg .. svgtemp 130 | svgtemp = [[ 131 | 135 | 136 | 137 | 138 | ]] 139 | svg = svg .. svgtemp 140 | end 141 | 142 | style_string = '' 143 | for key, value in pairs(style) do 144 | -- skips nil? 145 | style_string = style_string .. key .. ":" .. " " .. value .. ";" 146 | end 147 | 148 | svg = svg .. '' .. "\n" 149 | 150 | hs.pasteboard.writeDataForUTI("dyn.ah62d4rv4gu80w5pbq7ww88brrf1g065dqf2gnppxs3xu", svg) 151 | -- get UTI via https://github.com/sindresorhus/Pasteboard-Viewer 152 | hs.eventtap.keyStroke({"shift", "cmd"}, "v") 153 | end -------------------------------------------------------------------------------- /Inkscape-setting/Inkscape-shortcut-manager/karabiner-inkscape.jsonnet: -------------------------------------------------------------------------------- 1 | local arr = [ 2 | [x, y, z] // 24x 3 | for x in ['s', 'd', 'e'] // solid, dotted, dashed 4 | for y in ['f', 'w', 'a', 'x'] // gray, white, arrow, double-arrow 5 | for z in ['g', 'v'] // thick, very thick 6 | ] + 7 | [ 8 | [x, y] // 18x 9 | for x in ['s', 'd', 'e'] // solid, dotted, dashed 10 | for y in ['f', 'w', 'g', 'v', 'a', 'x'] 11 | ] + 12 | [ 13 | ['spacebar', x] // 6x 14 | for x in ['s', 'd', 'e', 'f', 'b', 'w'] // solid, dotted, dashed, gray, black, white 15 | ]; 16 | 17 | { 18 | title: 'Apply quickly essential shape or line styles in Inkscape using hammerspoon (Gilles Castel, 2019)', 19 | rules: [ 20 | { 21 | description: 'Apply quickly essential shape or line styles in Inkscape using hammerspoon (Gilles Castel, 2019)', 22 | manipulators: [ 23 | { 24 | local str = std.join('+', el), 25 | type: 'basic', 26 | from: { 27 | simultaneous: [{ key_code: i } for i in el], 28 | simultaneous_options: { 29 | key_up_when: 'all', 30 | }, 31 | }, 32 | to: [{ shell_command: "/usr/local/bin/hs -c 'hs.alert(\"" + str + '");create_svg_and_paste({"' + std.join('","', el) + "\"});'" }], 33 | conditions: [{ type: 'frontmost_application_if', bundle_identifiers: ['org.inkscape.Inkscape'] }], 34 | } 35 | for el in arr 36 | ], 37 | }, 38 | ], 39 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Pingbang Hu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ***VSCode-LaTeX-Inkscape*** 2 | 3 |

4 | 5 |

6 | 7 |

8 | A way to integrate LaTeX, VS Code, and Inkscape in macOS. 9 |

10 | 11 | ## Table of Content 12 | 13 | - [Abstract](#abstract) 14 | - [Disclaimer](#disclaimer) 15 | - [Setup For Typing Blasting Fast](#setup-for-typing-blasting-fast) 16 | - [Tex Conceal](#tex-conceal) 17 | - [HyperSnips](#hypersnips) 18 | - [Sympy and Mathematica](#sympy-and-mathematica) 19 | - [Correcting Spelling Mistakes on the Fly](#correcting-spelling-mistakes-on-the-fly) 20 | - [Drawing Like a Pro - With Inkscape](#drawing-like-a-pro---with-inkscape) 21 | - [Inkscape Figure Manager](#inkscape-figure-manager) 22 | - [Inkscape Shortcut Manager](#inkscape-shortcut-manager) 23 | - [Reference Card for Key Chords](#reference-card-for-key-chords) 24 | - [Summary](#summary) 25 | - [Updates](#updates) 26 | - [~~About Inkscape Shortcut Manager (09.27.21)~~](#about-inkscape-shortcut-manager-092721) 27 | - [Quiver - For commutative diagram (01.24.22)](#quiver---for-commutative-diagram-012422) 28 | - [Migrate to HyperSnips (02.18.22)](#migrate-to-hypersnips-021822) 29 | - [Documenting Inkscape Shortcut Manager (07.30.22)](#documenting-inkscape-shortcut-manager-073022) 30 | - [Credits](#credits) 31 | - [Related Project](#related-project) 32 | - [Star History](#star-history) 33 | 34 | ## Abstract 35 | 36 | I use $\LaTeX$ heavily for both academic work and professional work, and I think I'm quite proficient in terms of typing things out in $\LaTeX$. But when I see the mind-blowing blog posts from **Gilles Castel (RIP)**-[How I'm able to take notes in mathematics lectures using LaTeX and Vim](https://castel.dev/post/lecture-notes-1/) and also [How I draw figures for my mathematical lecture notes using Inkscape](https://castel.dev/post/lecture-notes-2/), I realize that I'm still far from *fast*, so I decided to adapt the whole setup from Linux-Vim to macOS-VS Code. 37 | 38 | > This setup is universal for VS Code users indeed. The only part that'll be macOS-specific is the Inkscape part ([Inkscape-figures](#inkscape-figure-manager) and [Inkscape-shortcut-manager](#inkscape-shortcut-manager)). While the first part can be replaced by [super-figure](https://github.com/Joao-Peterson/super-figure) (while I still prefer my setup, you can still try it out even if you're in macOS), and you can certainly achieve a similar result in Windows as in my [Notes](./Notes), the drawing speed will be slower without the shortcut manager. Just keep that in mind. 39 | 40 | If you still don't know what to expect, please check out my [Notes](https://github.com/sleepymalc/Notes) taken in this setup. Also, due to the VS Code recent update (1.76.1), we have the [profile](https://code.visualstudio.com/docs/editor/profiles) functionality available. Specifically, this is [my current minimal profile](https://vscode.dev/profile/github/70b175ba903a4f1cc5dcd271ce8fcb51) for $\LaTeX$ I'm currently using, but since some configurations are not included in the [profile](https://code.visualstudio.com/docs/editor/profiles), you should still read through everything. 41 | 42 | > Available: [My website](https://pbb.wtf/posts/VSCode-LaTeX-Inkscape) 43 | 44 | ## Disclaimer 45 | 46 | Please look through the two blog posts above by Gilles Castel! They are incredible and worth spending your time to understand how all things work, and what's the motivation behind all these. I'm only mimicking his workflow, with a little patience to set up the whole thing in my environment. Show respect to the original author! 47 | 48 | Before we start anything serious, just copy the [`keybindings.json`](./VSCode-setting/keybindings.json) and [`settings.json`](./VSCode-setting/settings.json) into your own `keybindings.json` and `settings.json`. Don't worry, I'll explain what they do later. 49 | 50 | 51 | 52 | 53 | 54 | Also, create a snippet file for $\LaTeX$ in the following steps: 55 | 56 | 1. Press `shift`+`cmd`+`p` to open the VS Code command. 57 | 2. Type `snippets`, and choose `Snippets: Configure Snippets`. 58 | 3. Choose `New Global Snippets file...`. 59 | 4. Enter `latex` to create a new file. 60 | 5. Paste the [`latex.json`](./VSCode-setting/Snippets/latex.json) into that file. 61 | 62 | 63 | 64 | ## Setup For Typing Blasting Fast 65 | 66 | First thing first, please set up your [VS Code](https://code.visualstudio.com/) with $\LaTeX$ properly with [LaTeX Workshop](https://marketplace.visualstudio.com/items?itemName=James-Yu.latex-workshop), there are lots of tutorials online, just check them out and set them up properly. Basically, it can be done in the following steps: 67 | 68 | 1. Download [MacTex](https://www.tug.org/mactex/). This can be replaced by something more lightweight, but in my opinion, this doesn't really help much in terms of speed or wasting your disk. But if you want something like this, check out [TeXLive](https://www.tug.org/texlive/). 69 | 2. Download [LaTeX Workshop](https://marketplace.visualstudio.com/items?itemName=James-Yu.latex-workshop) 70 | 3. Copy-pasting the following configuration file into your `settings.json` 71 | 72 | ```JSON 73 | "latex-workshop.latex.autoBuild.run": "onSave" 74 | ``` 75 | 76 | > This will save your time by compiling your $\LaTeX$ project whenever you save your file by `cmd`+`s`. 77 | 78 | Now, we go through things one by one following Gilles Castel's blog post. 79 | 80 | ### Tex Conceal 81 | 82 | To achieve a similar result as in Gilles Castel's setup, there is an extension called [vsc-conceal](https://github.com/Pancaek/vsc-conceal) for VS Code. All the setup is in the `setting.json`, and since this setup is quite straightforward, I'll just give a snapshot to show how it looks in practice. 83 | 84 |

85 | 86 |

87 | 88 | Note that I set the `"conceal.revealOn"` to `"active-line"`, which is why you will see the source code in line 51. There are other options you can choose, see the original repo for details. 89 | 90 | ### HyperSnips 91 | 92 | If you look around in the VS Code extension marketplace to find UltiSnips' equivalence, you probably will find [Vsnips](https://marketplace.visualstudio.com/items?itemName=corvofeng.Vsnips). But I'm not sure why this is the case, I can't figure out how to set it up properly. Hence, I find another alternative, which is [HyperSnips](https://marketplace.visualstudio.com/items?itemName=draivin.hsnips). Please first download [HyperSnips](https://marketplace.visualstudio.com/items?itemName=draivin.hsnips) and just follow the instructions, copy [`latex.hsnips`](./VSCode-setting/Snippets/latex.hsnips) into `$HOME/Library/Application Support/Code/User/globalStorage/draivin.hsnips/hsnips/`, and you're good to go! 93 | 94 | To modify this file, you can either go to this file in your finder or use the VS Code built-in command function. For commands function, 95 | 96 | 1. Press `shift+cmd+space` to type in some commands to VS Code. 97 | 2. Type `>HyperSnips: Open Snippet File` 98 | 3. Choose `latex.hsnips` 99 | 100 | After doing this, you're all set. But a big question is, what exactly is a snippet? 101 | 102 | #### Snippets 103 | 104 | A snippet is a short reusable piece of text that can be triggered by some other text. For example, when I type `dm` (stands for display math), the word `dm` will be expanded to a display math environment: 105 | 106 |

107 | 108 |

109 | 110 | If you are a math guy, you may need to type some inline math like `\(\)`, which is kind of painful. But with snippets, you can have 111 | 112 |

113 | 114 |

115 | 116 | See? You just type `fm` (not the best choice here, but since `im` is a common prefix, so can't really use that as our snippet 🥲), and then your snippet not only automatically types `\(\)` for you, but it also sends your cursor between `\(\)`! With this, you can type something **really** fast: 117 | 118 |

119 | 120 |

121 | 122 | Note that in the above demo, I use a very common snippet, `qs` for `^{2}`. 123 | 124 | As you can imagine, this can be quite complex. For example, you can even have something like this: 125 | 126 |

127 | 128 |

129 | 130 | or this: 131 | 132 |

133 | 134 |

135 | 136 | For the first snippet, I type `table2 5`, and then it generates a table with 2 rows and 5 columns. For the second one, I type `pmat` for matrix, and then type `2 5` to indicate that I want a 2 by 5 matrix, then boom! My snippets do that for me in an instant! 137 | 138 | My snippet file includes commonly used snippets as suggested in the original posts, you can look into it to better understand how it works. And maybe you can create your snippets also! Here are some useful snippets for you. 139 | 140 |

141 | 142 |

143 | 144 | #### Math Environment 145 | 146 | In the recent update of [HyperSnips](https://marketplace.visualstudio.com/items?itemName=draivin.hsnips), the *context* functionality is implemented, which is very useful, and you should understand how it works. If you look at the top of the snippet file, you will see 147 | 148 | ```javascript 149 | global 150 | function math(context) { 151 | return context.scopes.findLastIndex(s => s.startsWith("meta.math")) > context.scopes.findLastIndex(s => s.startsWith("comment") || s.startsWith("meta.text.normal.tex")); 152 | } 153 | endglobal 154 | ``` 155 | 156 | And for some snippets, you will see `context math(context)` in front of which, e.g., the *greater or equal* snippet: 157 | 158 | ```javascript 159 | context math(context) 160 | snippet `>=|(? s.startsWith("meta.math")) && !context.scopes.some(s => s.startsWith("comment") || s.startsWith("meta.text.normal.tex")); 181 | } 182 | endglobal 183 | ``` 184 | 185 | However, this leads to some problems. For example, sometimes, in the equation, I want to write 186 | 187 | ```latex 188 | \[ 189 | x_n = x \text{ for some \(n\) large enough}, 190 | \] 191 | ``` 192 | 193 | Such a case is fine since the math I want to write in the text scope is simple, just `\(n\)`. However, in the current (and perhaps most popular) scope function, it happens that `\(\text{ \( not in the math mode \) }\)`. 194 | 195 | To overcome this, I write the following more generic version: 196 | 197 | ```javascript 198 | global 199 | function math(context) { 200 | return context.scopes.findLastIndex(s => s.startsWith("meta.math")) > context.scopes.findLastIndex(s => s.startsWith("comment") || s.startsWith("meta.text.normal.tex")); 201 | } 202 | endglobal 203 | ``` 204 | 205 | So, since the nested environment is ordered in the scope, this will always return the correct mode. Even better, there will be no undefined behavior since if the `.findLastIndex` can't find either, it will return `-1` instead of something undefined, so everything is handled. 206 | 207 | ### Sympy and Mathematica 208 | 209 | Unlike Gilles Castel's approach, there is an available extension out there for you to simplify your math calculation already! Please go check out [Latex SYMPY Calculator](https://marketplace.visualstudio.com/items?itemName=OrangeX4.latex-sympy-calculator). It works like follows: 210 | 211 |

212 | 213 |

214 | 215 | Magic right? Let's set it up! First, please look at the installation document provided by [Latex Sympy Calculator](https://marketplace.visualstudio.com/items?itemName=OrangeX4.latex-sympy-calculator). After your installation, you can set up the keybinding for calculating the math expression. I use `shift`+`e`, where `e` stands for evaluating, to calculate so that it will append an equal sign and the answer right after your formula, just like above. If you want to avoid showing the intermediate steps of your calculation, you can use `shift`+`r`, where `r` stands for replacing, to directly replace the whole formula and give me the answer only. See the demo below: 216 | 217 |

218 | 219 |

220 | 221 | > This plugin is indeed more powerful than just this, see the documentation for detail. 222 | 223 | Let's go to the last thing covered in Gilles Castel's post, correcting spelling mistakes. 224 | 225 | ### Correcting Spelling Mistakes on the Fly 226 | 227 | Although my typing speed is quite high, I have typos all the time. So this is a must for me. And surprisingly, this is the hardest thing until now for me to set it upright. Let's see how we can configure this functionality in VS Code! There are three plugins we need: 228 | 229 | 1. [multi-command](https://marketplace.visualstudio.com/items?itemName=ryuta46.multi-command): This is a very powerful extension, which allows you to do a sequence of actions in one shortcut. We will use this later on also, and that's the place it shines. 230 | 231 | 2. [Code Spell Checker](https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker): This is a popular spelling checker out there that meets our needs. 232 | 233 | 3. [LTeX](https://marketplace.visualstudio.com/items?itemName=valentjn.VSCode-ltex): If you are bad at grammar like me, you definitely want to install to check some simple grammar mistakes for you. Although it's not as powerful as [Grammarly](https://www.grammarly.com/), not even comparable, it's still a good reference for you to keep your eyes on some simple mistakes you may overlook. 234 | 235 | > There is an unofficial API for Grammarly, and the plugin can be found [here](https://marketplace.visualstudio.com/items?itemName=znck.grammarly). Though it's quite slow... 236 | 237 | Here is a quick demo of how it works when typing: 238 | 239 |

240 | 241 |

242 | 243 | Additionally, if you also want to correct your grammar error, I use the shortcut `cmd`+`k` to trigger a quick-fix for a general error. 244 | 245 |
246 |

Detail Explanation

247 | 248 | > You can skip this part if you don't want to know the working mechanism. But if you're interested, please follow! The following code snippet in `settings.json` is responsible for correcting your spelling mistakes by just clicking `cmd`+`l`. 249 | > 250 | > ```json 251 | > { 252 | > "key": "cmd+l", 253 | > "command": "extension.multiCommand.execute", 254 | > "args": { 255 | > "sequence": [ 256 | > "cSpell.goToPreviousSpellingIssue", 257 | > { 258 | > "command": "editor.action.codeAction", 259 | > "args": { 260 | > "kind": "quickfix", 261 | > "apply": "first" 262 | > } 263 | > }, 264 | > "cursorUndo", 265 | > ] 266 | > } 267 | > }, 268 | > ``` 269 | > 270 | > > Make sure that the curly braces above have a trailing comma, otherwise, VS Code will complain about it. 271 | > 272 | > The working mechanism is as follows. When you press `cmd`+`l`, the [multi-command](https://marketplace.visualstudio.com/items?itemName=ryuta46.multi-command) will do the following: 273 | > 274 | > 1. Use one of the default function from cSpell's: `goToPreviousSpellingIssue`, which jump your cursor on that spelling error word 275 | > 2. Triggered a default editor action, with the argument being `quickfix` to open a quick fix drop-down list, and choose the `first` suggestion 276 | > 3. Move your cursor back by `cursorUndo` 277 | > 278 | > And likewise, the following code snippet is responsible for correcting grammar mistakes. 279 | > 280 | > ```json 281 | > { 282 | > "key": "cmd+k", 283 | > "command": "extension.multiCommand.execute", 284 | > "args": { 285 | > "sequence": [ 286 | > "editor.action.marker.prev", 287 | > { 288 | > "command": "editor.action.codeAction", 289 | > "args": { 290 | > "kind": "quickfix", 291 | > "apply": "first" 292 | > } 293 | > }, 294 | > "cursorUndo", 295 | > ] 296 | > } 297 | > }, 298 | > ``` 299 | 300 |
301 | 302 | Now, the first part is over. Let's go to the next truly beautiful, elegant, and exciting world, drawing with [Inkscape](https://inkscape.org/zh-hant/). 303 | 304 | ## Drawing Like a Pro - With Inkscape 305 | 306 |

307 | 308 |

309 | 310 | For more examples, check out the original blog. Or for more figures I draw, you can check out [Note](https://github.com/sleepymalc/Notes). 311 | 312 | One last thing is that I'll assume you have already installed [VS Code Vim](https://marketplace.visualstudio.com/items?itemName=vscodevim.vim). While this is not required, if you don't want to use it, then you'll need to assign different keybinding. Anyway, you'll see what I mean until then! 313 | 314 | ### Inkscape 315 | 316 | A big question is, why Inkscape? In the original blog, he had already explained it. One reason is that although $\texttt{TikZ}$ can do the job of drawing vector figures in $\LaTeX$ with original support, it's too slow to set all diagrams right. This is so true since my experience with $\texttt{TikZ}$ is *nice looking* and *intuitive* but also *slow* and *bulky*. Also, the $\texttt{TikZ}$ code tends to be **long**. A large file will take [*latexindent*](https://ctan.org/pkg/latexindent) and *pdfLaTeX* **a minute** to compile for one save. That's not efficient at all, especially when you want some instant feedback for some small changes. 317 | 318 | #### Download Inkscape 319 | 320 | You need to install [Inkscape](https://inkscape.org/zh-hant/) first. I recommend you install this in a terminal. I assume that you have your [`homebrew`](https://brew.sh/) installed. Then, just type the following into your terminal: 321 | 322 | ```sh 323 | > brew install --cask inkscape 324 | ``` 325 | 326 | #### Set up the Environment in LaTeX 327 | 328 | First thing first, include the following in your preamble 329 | 330 | ```latex 331 | \usepackage{import} 332 | \usepackage{xifthen} 333 | \usepackage{pdfpages} 334 | \usepackage{transparent} 335 | 336 | \newcommand{\incfig}[1]{% 337 | \def\svgwidth{\columnwidth} 338 | \import{./Figures/}{#1.pdf_tex} 339 | } 340 | ``` 341 | 342 | And to use it in your code, it's like the following: 343 | 344 | ```latex 345 | \begin{figure}[H] 346 | \centering 347 | \incfig{figure's name} 348 | \caption{Your caption} 349 | \label{fig:label} 350 | \end{figure} 351 | ``` 352 | 353 | And then you're done! Also, the compilation time for this is shorter than you can ever expect. Let's get started then! 354 | 355 | This assumes that your $\LaTeX$ project's home directory looks like this: 356 | 357 | ```sh 358 | LaTeX_project 359 | ├── main.tex 360 | ├── main.pdf 361 | ├── Figures 362 | │ ├── fig.pdf 363 | │ ├── fig.pdf_tex 364 | │ ├── fig.svg 365 | │ . 366 | . 367 | ``` 368 | 369 | Now, let's get into the fun part, i.e., setting up the shortcut for this. 370 | 371 | ### Inkscape Figure Manager 372 | 373 | This is a figure manager developed by Gilles Castel, and here is the [repo](https://github.com/gillescastel/inkscape-figures). I recommend you follow the installation instructions there. Here are just some guidelines for you. 374 | 375 | 1. Install [choose](https://github.com/chipsenkbeil/choose) (specifically for macOS, [rofi](https://github.com/davatorium/rofi) for Linux instead): 376 | 377 | ```sh 378 | > brew install choose-gui 379 | ``` 380 | 381 | 2. Install [fswatch](https://github.com/emcrisostomo/fswatch): 382 | 383 | ```sh 384 | > brew install fswatch 385 | ``` 386 | 387 | 3. Install the Inkscape figure manager: 388 | 389 | ```sh 390 | > pip3 install inkscape-figures 391 | ``` 392 | 393 | > After installing it, type `inkscape-figures` in your terminal to make sure you have corrected install it. 394 | 395 | If you're using Linux and Vim, then you are done already. But since you're using macOS and VS Code, please follow me, there are some more things for you to configure. 396 | 397 | > If you're using Windows, then check out [super-figure](https://github.com/Joao-Peterson/super-figure). It implements similar functionalities but in a more chunky way. Even if you're using macOS, you can try it too, although I prefer my setup. 398 | 399 | #### Set up Inkscape Figure Manager 400 | 401 | Firstly, install the [Command Runner](https://marketplace.visualstudio.com/items?itemName=edonet.vscode-command-runner). This will allow you to send commands into a terminal with the shortcut. The configuration is in [`settings.json`](./VSCode-setting/settings.json), and we'll see how it works later. Now, this is a tricky part: you need to find the source code of the inkscape-figures manager. In my case, it's in `/Users/pbb/opt/anaconda3/lib/python3.8/site-packages/inkscapefigures`. 402 | 403 | > Using global finding may be helpful... 404 | 405 | Open this directory by VS Code, there is something for you to modify. Ok, I know you probably don't have that much patience now, so I have a modified version available [here](./Inkscape-setting/Inkscape-figure-manager/). Just replace the whole directory with mine, and you're good to go. 406 | 407 | > Notice that the directory in this repo is named `Inkscape-figure-manager`, while in your system, it should be `inkscapefigures`. 408 | 409 |
410 |

Detail Explanation

411 | 412 | > In Gilles Castel's approach, he uses the shortcut `ctrl`+`f` to trigger this script, which will copy the whole line's content depending on the cursor's position, and the script will send the snippets by the function 413 | > 414 | > ```python 415 | > def latex_template(name, title): 416 | > return '\n'.join((r"\begin{figure}[ht]", 417 | > r" This is a custom LaTeX template!", 418 | > r" \centering", 419 | > rf" \incfig[1]{{{name}}}", 420 | > rf" \caption{{{title}}}", 421 | > rf" \label{{fig:{name}}}", 422 | > r"\end{figure}")) 423 | > ``` 424 | > 425 | > to `stdout`, and then create a figure by the `name`, which is the content of the line. 426 | > 427 | > But this in VS Code is impossible, hence we don't need this, we'll use command line. And if we leave this function as it was, then it will send all these snippets into our terminal, which is quite annoying. So the modified version just removes this snippet completely. 428 | > 429 | > But let me explain it to you, in case you want to modify it to meet your need later on. First thing first, we see that in the given code in [`keybindings.json`](./VSCode-setting/keybindings.json) and [`settings.json`](./VSCode-setting/settings.json), we're using [Command Runner](https://marketplace.visualstudio.com/items?itemName=edonet.vscode-command-runner), so let me tell you how to set this up first. 430 | 431 |
432 | 433 | We're now prepared to see a detailed explanation of commands provided in [Inkscape figure manager](https://github.com/gillescastel/inkscape-figures). There are three different commands in the [Inkscape figure manager](https://github.com/gillescastel/inkscape-figures). We break it down one by one. 434 | 435 | #### Watch 436 | 437 | Since Inkscape by default does not save the file in `pdf+latex`, we need [Inkscape figure manager](https://github.com/gillescastel/inkscape-figures) to help us. We need to first open the file watcher to *watch* the file for any changes. If there is any, then the file watcher will tell Inkscape to save the file in `pdf+latex` format. 438 | 439 | To open the file watcher, you can type `inkscape-figures watch` in the terminal. But remember the [Command Runner](https://marketplace.visualstudio.com/items?itemName=edonet.vscode-command-runner) we just installed? We can assign this command with a keybinding! In my case, since I don't want to introduce more than one keybinding for Inkscape-figures manager, I use `mode` provided by `vim` to help us. In `VISUAL` mode (enter by `v` in `NORMAL` mode), press `ctrl`+`f`. 440 | 441 | > You should trigger this at the beginning. i.e., use this after you open your project folder. To check whether `watch` is triggered correctly, you can simply open the terminal and see what's the output when you press `ctrl`+`f`: If it's already triggered, then it'll show 442 | > 443 | > ```sh 444 | > > inkscape-figures watch 445 | > Unable to lock on the pidfile. 446 | > ``` 447 | > 448 | > Otherwise it'll simply show nothing. (Remember to select the terminal corresponds to `runCommand`!) 449 | 450 |
451 |
Detail Explanation
452 | 453 | > In [`keybindings.json`](./VSCode-setting/keybindings.json), we have 454 | > 455 | > ```json 456 | > { 457 | > "key": "ctrl+f", 458 | > "command": "command-runner.run", 459 | > "args": { 460 | > "command": "inkscapeStart", 461 | > "terminal": { 462 | > "name": "runCommand", 463 | > "shellArgs": [], 464 | > "autoClear": true, 465 | > "autoFocus": false 466 | > } 467 | > }, 468 | > "when": "editorTextFocus && vim.active && vim.use && !inDebugRepl && vim.mode == 'Visual'" 469 | > } 470 | > ``` 471 | > 472 | > for starting the [Inkscape figure manager](https://github.com/gillescastel/inkscape-figures). And the command is defined in [`settings.json`](./VSCode-setting/settings.json): 473 | > 474 | > ```json 475 | > "command-runner.commands": { 476 | > "inkscapeStart": "inkscape-figures watch" 477 | > } 478 | > ``` 479 | > 480 | > In detail, we just use [Command Runner](https://marketplace.visualstudio.com/items?itemName=edonet.vscode-command-runner) to run the command we defined in [`settings.json`](./VSCode-setting/settings.json), in this case, I explicitly tell the keybinding `ctrl`+`f` will trigger `inkscapeStart` when I'm in `VISUAL` mode in Vim, which is just `inkscape-figures watcher` as defined above. 481 | > 482 | > Notice that we set the `autoFocus=false` for the terminal [Command Runner](https://marketplace.visualstudio.com/items?itemName=edonet.vscode-command-runner) uses since we don't want a pop-up terminal to distract us. If you want to see whether the command is triggered correctly every time, you can set it to `true`. 483 | 484 |
485 | 486 | #### Create 487 | 488 | Same as above, we also use `ctrl`+`f` to trigger `inkscape-figures create` command. But in this case, we use `INSERT` for creating a new Inkscape figure. Specifically, we first type out the image's name we want our image to be called, then, in this case, we're already in `INSERT` mode, we just press `ctrl`+`f` to create this image after naming. 489 | 490 |
491 |
Detail Explanation
492 | 493 | > We set up our [`keybindings.json`](./VSCode-setting/keybindings.json) as 494 | > 495 | > ```json 496 | > { 497 | > "key": "ctrl+f", 498 | > "command": "extension.multiCommand.execute", 499 | > "args": { 500 | > "sequence": [ 501 | > "editor.action.clipboardCopyAction", 502 | > "editor.action.insertLineAfter", 503 | > "cursorUp", 504 | > "editor.action.deleteLines", 505 | > { 506 | > "command": "editor.action.insertSnippet", 507 | > "args": { 508 | > "name": "incfig" 509 | > } 510 | > }, 511 | > { 512 | > "command": "command-runner.run", 513 | > "args": { 514 | > "command": "inkscapeCreate", 515 | > }, 516 | > "terminal": { 517 | > "name": "runCommand", 518 | > "shellArgs": [], 519 | > "autoClear": true, 520 | > "autoFocus": false 521 | > } 522 | > }, 523 | > ] 524 | > }, 525 | > "when": "editorTextFocus && vim.active && vim.use && !inDebugRepl && vim.mode == 'Insert'" 526 | > }, 527 | > ``` 528 | > 529 | > and also in [`settings.json`](./VSCode-setting/settings.json): 530 | > 531 | > ```json 532 | > "command-runner.commands": { 533 | > "inkscapeCreate": "inkscape-figures create ${selectedText} ${fileDirname}/Figures/" 534 | > } 535 | > ``` 536 | > 537 | > We break down what `ctrl`+`f` do in `INSERT` mode exactly step by step. We see that when we press `ctrl`+`f` in `INSERT` mode, we trigger `multiCommand.execute` to execute a sequence of instructions, which are 538 | > 539 | > 1. Copy the content into your clipboard of the line your cursor at 540 | > 2. Insert a blank line after since we need to insert a snippet, and that will delete an additional line. You can try to delete this and the next instruction, and see what happens. 541 | > 3. Move back our cursor after inserting that new line. 542 | > 4. Delete that copied content by removing this line. 543 | > 5. Insert a snippet defined in [`latex.json`](./VSCode-setting/Snippets/latex.json). **Notice that this is the default snippet functionality built-in VS Code, not [HyperSnips](https://marketplace.visualstudio.com/items?itemName=draivin.hsnips) we have used before**. I'll explain where to copy this file in a minute. 544 | > 6. Lastly, we send a command in a terminal by [Command Runner](https://marketplace.visualstudio.com/items?itemName=edonet.vscode-command-runner), with the command `inkscapeCreate` we defined in [`settings.json`](./VSCode-setting/settings.json). 545 | > 546 | > In the fifth instruction, the snippet we used is 547 | > 548 | > 549 | > 550 | > which is just the snippet we remove from [Inkscape figure manager](https://github.com/gillescastel/inkscape-figures)'s source code! It's back again, in a different approach. 551 | 552 |
553 | 554 |

555 | 556 |

557 | 558 | Let me break it down for you. Firstly, I changed into `INSERT` mode in VS Code Vim and typed my new figure's name `figure-test`. Then, I press `ctrl`+`f` to trigger the keybinding, which will automatically create an Inkscape figure named `figure-test` for me and open it. 559 | 560 | > The three files will be created along the way: `figure-test.pdf`, `figure-test.pdf_tex` and `figure-test.svg`. Unfortunately, to rename a file, you'll need to manually rename three of them. 561 | 562 | #### Edit 563 | 564 | Again, we also use `ctrl`+`f` to trigger `inkscape-figures edit` command, but this time in `NOMAL` mode. Here, [choose](https://github.com/chipsenkbeil/choose) comes into play. After you select the image you want to edit in Inkscape, you simply press `enter` and it'll open that image for you to edit. 565 | 566 | > You can modify the styling of [choose](https://github.com/chipsenkbeil/choose). For example, in [`picker.py`](./Inkscape-setting/Inkscape-figure-manager/picker.py), we have the following: 567 | > 568 | > ```python 569 | > def get_picker_cmd(picker_args=None, fuzzy=True): 570 | > """ 571 | > Create the shell command that will be run to start the picker. 572 | > """ 573 | > if SYSTEM_NAME == "Darwin": 574 | > args = ["choose"] 575 | > # args = ["choose", "-u", "-n", "15", "-c", "BB33B7", "-b", "BF44C8"] 576 | > ``` 577 | > 578 | > We see that we don't have any additional argument for `choose`, but if you want, you can replace this line by the next line, which modify the style of `choose`. For detail information, type `choose -h` to see all the options. 579 | 580 |
581 |
Detail Explanation
582 | 583 | > The corresponding keybinding in ['keybindings.json'](./VSCode-setting/keybindings.json) is: 584 | > 585 | > ```json 586 | > { 587 | > "key": "ctrl+f", 588 | > "command": "command-runner.run", 589 | > "args": { 590 | > "command": "inkscapeEdit", 591 | > "terminal": { 592 | > "name": "runCommand", 593 | > "shellArgs": [], 594 | > "autoClear": true, 595 | > "autoFocus": false 596 | > } 597 | > }, 598 | > "when": "editorTextFocus && vim.active && vim.use && !inDebugRepl && vim.mode == 'Normal'" 599 | > }, 600 | > ``` 601 | > 602 | > and also in [`settings.json`](./VSCode-setting/settings.json): 603 | > 604 | > ```json 605 | > "command-runner.commands": { 606 | > "inkscapeEdit": "inkscape-figures edit ${fileDirname}/Figures/" 607 | > } 608 | > ``` 609 | > 610 | > I think now it's clear enough how all these work together to trigger the corresponding command. When you press `ctrl`+`f` in `NORMAL` mode, you'll trigger the `inkscape-figures edit` command, and it'll look into your `Figures/` subfolder to see what figures you have and pop out a window for you to choose, which is the functionality provided by [choose](https://github.com/chipsenkbeil/choose). 611 | 612 |
613 | 614 | In the following demo, I create another figure named `figure-test2`, then modify it a little, and compile it again. 615 | 616 |

617 | 618 |

619 | 620 | ### Inkscape Shortcut Manager 621 | 622 | In this section, we'll set up a very efficient shortcut manager to help you draw any mathematical figures faster than you can ever imagine! Notice that this setup is quite complicated, but the result is quite good. It depends on 623 | 624 | - [Hammerspoon](https://www.hammerspoon.org/): For windows focus. 625 | - [Karabiner Elements](https://karabiner-elements.pqrs.org/): For capturing the overlapping key chords. 626 | 627 | Please download the above two apps. 628 | 629 | > This section is contributed **purely** by [@kiryph](https://github.com/kiryph) in [#1](https://github.com/sleepymalc/VSCode-LaTeX-Inkscape/issues/1). 630 | 631 | #### Karabiner Elements 632 | 633 | We'll need [Karabiner Elements](https://karabiner-elements.pqrs.org)' [Complex Modifications](https://karabiner-elements.pqrs.org/docs/json/root-data-structure/#custom-json-file-in-configkarabinerassetscomplex_modifications) to help us. The steps are the following (adapted from [️⌨ How to type?](https://pbb.wtf/posts/How2TypeFast#import-settings)). 634 | 635 | 1. Open [Karabiner-Elements](https://karabiner-elements.pqrs.org/), go to *Misc* and click on *Export & Import*. 636 |
637 | 638 |
639 | 2. Copy [`Inkscape.json`](https://github.com/sleepymalc/VSCode-LaTeX-Inkscape/blob/main/Inkscape-setting/Inkscape-shortcut-manager/Inkscape.json) into `.config/karabiner/assets/complex_modifications`. 640 |
641 | 642 |
643 | 3. Again open [Karabiner-Elements](https://karabiner-elements.pqrs.org/), go to *Complex Modifications* and click on *Add rule*. 644 |
645 | 646 |
647 | 4. Enable it. 648 |
649 | 650 |
651 | 652 | If you're interested in how [`Inkscape.json`](https://github.com/sleepymalc/VSCode-LaTeX-Inkscape/blob/main/Inkscape-setting/Inkscape-shortcut-manager/Inkscape.json) is created, see the following. 653 | 654 |
655 | 656 | Detail Explanation 657 | 658 | The [`Inkscape.json`](https://github.com/sleepymalc/VSCode-LaTeX-Inkscape/blob/main/Inkscape-setting/Inkscape-shortcut-manager/Inkscape.json) is created by using a [`jsonnet`](https://jsonnet.org) file. The file can be found [here](https://github.com/sleepymalc/VSCode-LaTeX-Inkscape/blob/main/Inkscape-setting/Inkscape-shortcut-manager/karabiner-Inkscape.jsonnet), 659 | 660 | 661 | 662 | and the `jsonnet` tool can be installed via `> brew install jsonnet`. 663 | 664 | Converting the `jsonnet` file into the `json` file for [Karabiner Elements](https://karabiner-elements.pqrs.org/) can be done as follows 665 | 666 | ```sh 667 | > jsonnet karabiner-Inkscape.jsonnet > ~/.config/karabiner/assets/complex_modifications/karabiner-Inkscape.json 668 | ``` 669 | 670 |
671 | 672 | #### Hammerspoon 673 | 674 | Firstly, open the [Hammerspoon](https://www.hammerspoon.org/) console and run `hs.ipc.cliInstall()` to install the cli command `hs`. Then, just add the following code to your [`~/.hammerspoon/init.lua`](./Inkscape-setting/Inkscape-shortcut-manager/init.lua). 675 | 676 | 677 | 678 | #### Reference Card for Key Chords 679 | 680 | As a reference for the key chords, I added the original picture from [the original blog](https://castel.dev/post/lecture-notes-2/) but with the key chords included in the picture. 681 | 682 |

683 | 684 |

685 | 686 | #### Missing Key Chords 687 | 688 | I did not add the *ergonomic* rebinding `x`, `w`, `f`, and `shift`+`z`. This should be possible in Inkscape itself. This setup also misses the bindings `t`, `shift`+`t`, `a`, `shift`+`a`, `s`, and `shift`+`s`. Since I encountered issues I did not pursue these. 689 | 690 | ### Summary 691 | 692 | This is the whole setup I have, and let's wrap this up since I know this may be quite overwhelming. 693 | 694 | 1. Before you start your project, enter the `VISUAL` mode by pressing `v` in `NORMAL` mode. And then press `ctrl`+`f`. This will set up the file watcher. 695 | 2. When you want to create a new figure, go into a new line, type the name of your figure in `INSERT` mode, then press `ctrl`+`f`. This will create a new figure with the name you typed, and open it in Inkscape for you. 696 | 3. When you have drawn your figure, as long as you press `cmd`+`s` in Inkscape, it will automatically save the figure in `pdf+latex` for you, then you can close Inkscape. 697 | 4. When you want to edit one of your figures, you press `ctrl`+`f` in `NORMAL` mode, it will pop out a window for you to choose the figure you want to edit. And the rest is the same as 3. 698 | 699 | ## Updates 700 | 701 | ### ~~About Inkscape Shortcut Manager (09.27.21)~~ 702 | 703 | ~~After some research, although there is a way to let the original script in [inkscape-shortcut-manager](https://github.com/gillescastel/inkscape-shortcut-manager) run correctly since it depends on `xlib`, which is no longer used by macOS for almost every application(including Inkscape, as expected), hence the only thing I can do now is to give up. In a perceivable future, if I have time to find an alternative way to interrupt the window activity in macOS, I'll try to configure it for macOS.~~ 704 | 705 | > Now the Inkscape Shortcut Manager is fully functional, see [here](#inkscape-shortcut-manager). 706 | 707 | ### Quiver - For commutative diagram (01.24.22) 708 | 709 | I have been working on Category Theory for a while, and I found out that [quiver](https://q.uiver.app/) is quite appealing, hence I integrate it into my workflow. You can also pull it to your local environment, configure the VS Code Task, and combine it with a hotkey to use it **locally**. Specifically, I added the following code to my [`keybindings.json`](./VSCode-setting/keybindings.json): 710 | 711 | ```json 712 | { 713 | "key": "ctrl+c", 714 | "command": "command-runner.run", 715 | "args": { 716 | "command": "quiver", 717 | "terminal": { 718 | "name": "runCommand", 719 | "shellArgs": [], 720 | "autoClear": true, 721 | "autoFocus": false 722 | } 723 | }, 724 | "when": "editorTextFocus" 725 | }, 726 | ``` 727 | 728 | and also, define the command `quiver` as 729 | 730 | ```json 731 | "command-runner.commands": { 732 | "quiver": "open -na 'Google Chrome' --args --new-window /quiver/src/index.html" 733 | }, 734 | ``` 735 | 736 | Notice that you'll need to build it first if you want to use it offline! Please follow the tutorial [here](https://github.com/varkor/quiver). Otherwise, it's totally fine to use `"quiver": "open -na 'Google Chrome' --args --new-window https://q.uiver.app/"` as your command. 737 | 738 | This is what the workflow looks like. 739 | 740 |

741 | 742 |

743 | 744 | To use the package `tikz-cd`, you need to include the following in your header: 745 | 746 | ```latex 747 | % quiver style 748 | \usepackage{tikz-cd} 749 | % `calc` is necessary to draw curved arrows. 750 | \usetikzlibrary{calc} 751 | % `pathmorphing` is necessary to draw squiggly arrows. 752 | \usetikzlibrary{decorations.pathmorphing} 753 | 754 | % A TikZ style for curved arrows of a fixed height, due to AndréC. 755 | \tikzset{curve/.style={settings={#1},to path={(\tikztostart) 756 | .. controls ($(\tikztostart)!\pv{pos}!(\tikztotarget)!\pv{height}!270:(\tikztotarget)$) 757 | and ($(\tikztostart)!1-\pv{pos}!(\tikztotarget)!\pv{height}!270:(\tikztotarget)$) 758 | .. (\tikztotarget)\tikztonodes}}, 759 | settings/.code={\tikzset{quiver/.cd,#1} 760 | \def\pv##1{\pgfkeysvalueof{/tikz/quiver/##1}}}, 761 | quiver/.cd,pos/.initial=0.35,height/.initial=0} 762 | 763 | % TikZ arrowhead/tail styles. 764 | \tikzset{tail reversed/.code={\pgfsetarrowsstart{tikzcd to}}} 765 | \tikzset{2tail/.code={\pgfsetarrowsstart{Implies[reversed]}}} 766 | \tikzset{2tail reversed/.code={\pgfsetarrowsstart{Implies}}} 767 | % TikZ arrow styles. 768 | \tikzset{no body/.style={/tikz/dash pattern=on 0 off 1mm}} 769 | ``` 770 | 771 | You can certainly follow my [Template](https://github.com/sleepymalc/Academic-Template), which already includes all the requirement headers for you. 772 | 773 | ### Migrate to HyperSnips (02.18.22) 774 | 775 | Now, instead of using [HyperSnips for Math](https://marketplace.visualstudio.com/items?itemName=OrangeX4.hsnips), we're using [HyperSnips](https://marketplace.visualstudio.com/items?itemName=draivin.hsnips), namely the **original one**! Since I just found out that we can trigger the snippets **only in math mode** by using the special keyword called `context`, I migrated to the original one. To migrate, you just need to uninstall [HyperSnips for Math](https://marketplace.visualstudio.com/items?itemName=OrangeX4.hsnips), install [HyperSnips](https://marketplace.visualstudio.com/items?itemName=draivin.hsnips) with the updated [latex.hsnips](./VSCode-setting/Snippets/latex.hsnips) I prepared for you, and then enjoy! 776 | 777 | ### Documenting Inkscape Shortcut Manager (07.30.22) 778 | 779 | I finally have time to document the configuration of the [Inkscape shortcut manager](#inkscape-shortcut-manager) and make some changes to make this document more readable. Personally, I have used this workflow for more than half of a year, so I think this is stable and will not be changed shortly. 780 | 781 | ## Credits 782 | 783 | Again, thanks to Gilles Castel, this workflow fits my style. Although it originally worked in Linux+Vim only, the idea is the most important thing. Without his wonderful post, I can't even imagine this is possible. But now it is! Go to his original post to show him some love. 784 | 785 | ## Related Project 786 | 787 | 1. [LaTeX-Template](https://github.com/sleepymalc/LaTeX-Template) 788 | 2. [Notes](https://github.com/sleepymalc/Notes) 789 | 3. [gillescastel/inkscape-figures](https://github.com/gillescastel/inkscape-figures) 790 | 4. [gillescastel/inkscape-shortcut-manager](https://github.com/gillescastel/inkscape-shortcut-manager) 791 | 5. [chipsenkbeil/choose](https://github.com/chipsenkbeil/choose) 792 | 6. [varkor/quiver](https://github.com/varkor/quiver) 793 | 794 | ## Star History 795 | 796 |

797 | 798 |

799 | -------------------------------------------------------------------------------- /VSCode-setting/Snippets/latex.hsnips: -------------------------------------------------------------------------------- 1 | global 2 | function math(context) { 3 | return context.scopes.findLastIndex(s => s.startsWith("meta.math")) > context.scopes.findLastIndex(s => s.startsWith("comment") || s.startsWith("meta.text.normal.tex")); 4 | } 5 | function notmath(context) { 6 | return context.scopes.findLastIndex(s => s.startsWith("meta.math")) <= context.scopes.findLastIndex(s => s.startsWith("comment") || s.startsWith("meta.text.normal.tex")); 7 | } 8 | endglobal 9 | 10 | context notmath(context) 11 | snippet `table([1-9]{1})\ ([1-9]{1})` "Table environment" bA 12 | \begin{table}[H] 13 | \centering 14 | \begin{tabular}{`` 15 | let len = m[2]; 16 | let results = ""; 17 | for(var i=0; i=|(?~` "less or equal to (up to constant)" A 374 | \gtrsim $0 375 | endsnippet 376 | 377 | context math(context) 378 | snippet `<=|(? "inclusion" iA 445 | \hookrightarrow $0 446 | endsnippet 447 | 448 | context math(context) 449 | snippet || "mid" A 450 | \mid $0 451 | endsnippet 452 | 453 | context math(context) 454 | snippet `([a-zA-Z])\1` "subscript" A 455 | _{``rv = m[1]``} $0 456 | endsnippet 457 | 458 | context math(context) 459 | snippet `([A-Za-z\d)}])\_([A-Za-z\d\-+][A-Za-z\d-+])` "auto subscript" A 460 | `` rv = m[1]``_{``rv = m[2]``$1} $0 461 | endsnippet 462 | 463 | priority 200 464 | context math(context) 465 | snippet `([A-Za-z\d)}])\^([A-Za-z\d\-+][A-Za-z\d-+])` "auto supscript" A 466 | `` rv = m[1]``^{``rv = m[2]``$1} $0 467 | endsnippet 468 | 469 | context math(context) 470 | snippet `(b|B)(ar)` "bar" A 471 | \overline{$1} $0 472 | endsnippet 473 | 474 | priority 200 475 | context math(context) 476 | snippet `(\\?[a-zA-Z]\w*|\\[^(^\s]+\})(b|B)(ar)` "bar" A 477 | \overline{``rv = m[1]``} $0 478 | endsnippet 479 | 480 | context math(context) 481 | snippet `(t|T)(d)` "tilde" A 482 | \widetilde{$1} $0 483 | endsnippet 484 | 485 | priority 200 486 | context math(context) 487 | snippet `(\\?[a-zA-Z]\w*|\\[^(^\s]+\})(t|T)(d)` "tilde" A 488 | \widetilde{``rv = m[1]``} $0 489 | endsnippet 490 | 491 | context math(context) 492 | snippet `(\\?[a-zA-Z]\w*|\\[^(^\s]+\})(h|H)(t)` "hat" A 493 | \hat{``rv = m[1]``} $0 494 | endsnippet 495 | 496 | context math(context) 497 | snippet `(\\?[a-zA-Z]\w*|\\[^(^\s]+\})(b|B)(f)` "mathbf" A 498 | \mathbf{``rv = m[1]``} $0 499 | endsnippet 500 | 501 | context math(context) 502 | snippet `(\\?[a-zA-Z]\w*|\\[^(^\s]+\})(b|B)(m)` "bm" A 503 | \bm{``rv = m[1]``} $0 504 | endsnippet 505 | 506 | context math(context) 507 | snippet `(\\?[a-zA-Z]\w*|\\[^(^\s]+\})(,\.|\.,)` "Vector postfix" A 508 | \vec{``rv = m[1]``} $0 509 | endsnippet 510 | 511 | context math(context) 512 | snippet 1..n "sequence" iA 513 | $1_1, \dots, $1_n $0 514 | endsnippet 515 | 516 | context notmath(context) 517 | snippet fig "Figure environment" b 518 | \begin{figure}[${1:H}] 519 | \centering 520 | \includegraphics[width=0.8\textwidth]{$2} 521 | \caption{$3} 522 | \label{fig:$4} 523 | \end{figure} 524 | endsnippet 525 | 526 | context math(context) 527 | snippet ... "dots" iA 528 | \dots $0 529 | endsnippet 530 | 531 | context math(context) 532 | snippet => "implies" iA 533 | \implies $0 534 | endsnippet 535 | 536 | context math(context) 537 | snippet =< "implied by" iA 538 | \impliedby $0 539 | endsnippet 540 | 541 | priority 200 542 | context math(context) 543 | snippet `(? "<>" iA 566 | \langle $1 \rangle $0 567 | endsnippet 568 | 569 | context math(context) 570 | snippet lr, "<>" iA 571 | \left\langle $1 \right\\rangle $0 572 | endsnippet 573 | 574 | context math(context) 575 | snippet lrd "()" iA 576 | \left( $1 \right) $0 577 | endsnippet 578 | 579 | context math(context) 580 | snippet {} "{}" iA 581 | \\{ $1 \\} $0 582 | endsnippet 583 | 584 | context math(context) 585 | snippet lra "{}" iA 586 | \\left\\{ $1 \\right\\} $0 587 | endsnippet 588 | 589 | context math(context) 590 | snippet lrq "[]" iA 591 | \left[ $1 \right] $0 592 | endsnippet 593 | 594 | context math(context) 595 | snippet ceil "ceil" iA 596 | \lceil $1 \rceil $0 597 | endsnippet 598 | 599 | context math(context) 600 | snippet Ceil "ceil" iA 601 | \left\lceil $1 \right\rceil $0 602 | endsnippet 603 | 604 | context math(context) 605 | snippet flr "floor" iA 606 | \lfloor $1 \rfloor$0 607 | endsnippet 608 | 609 | context math(context) 610 | snippet Flr "floor" iA 611 | \left\lfloor $1 \right\rfloor$0 612 | endsnippet 613 | 614 | context math(context) 615 | snippet abs "abs value" iA 616 | \vert ${1} \vert $0 617 | endsnippet 618 | 619 | context math(context) 620 | snippet Abs "abs value" iA 621 | \left\vert ${1} \right\vert $0 622 | endsnippet 623 | 624 | priority 200 625 | context math(context) 626 | snippet norm "norm" iA 627 | \lVert $1 \rVert $0 628 | endsnippet 629 | 630 | context math(context) 631 | snippet Norm "Norm" iA 632 | \left\lVert $1 \right\rVert $0 633 | endsnippet 634 | 635 | context math(context) 636 | snippet `(?|(?` "to" A 892 | \leftrightarrow $0 893 | endsnippet 894 | 895 | context math(context) 896 | snippet !> "mapsto" iA 897 | \mapsto $0 898 | endsnippet 899 | 900 | priority 100 901 | context math(context) 902 | snippet dint "integral" iA 903 | \int_{${1:-\infty}}^{${2:\infty}} ${3} \\,\mathrm{d}${4:x} $0 904 | endsnippet 905 | 906 | context math(context) 907 | snippet `(?> ">>" iA 1003 | \gg $0 1004 | endsnippet 1005 | 1006 | context math(context) 1007 | snippet << "<<" iA 1008 | \ll $0 1009 | endsnippet 1010 | 1011 | context math(context) 1012 | snippet ind "indicator function" iA 1013 | \mathbbm{1}_{$1} $0 1014 | endsnippet 1015 | 1016 | priority 200 1017 | context math(context) 1018 | snippet spt "support" iA 1019 | \mathop{\mathrm{supp}}($1) $0 1020 | endsnippet 1021 | 1022 | context math(context) 1023 | snippet mean "Expectation" iA 1024 | \mathbb{E}_{$1}\left[$2 \right] $0 1025 | endsnippet 1026 | 1027 | context math(context) 1028 | snippet `(? && !inDebugRepl && vim.mode == 'Insert'" 90 | }, 91 | { 92 | "key": "ctrl+f", 93 | "command": "command-runner.run", 94 | "args": { 95 | "command": "inkscapeEdit", 96 | "terminal": { 97 | "name": "runCommand", 98 | "shellArgs": [], 99 | "autoClear": true, 100 | "autoFocus": false 101 | } 102 | }, 103 | "when": "editorTextFocus && vim.active && vim.use && !inDebugRepl && vim.mode == 'Normal'" 104 | }, 105 | { 106 | "key": "ctrl+f", 107 | "command": "command-runner.run", 108 | "args": { 109 | "command": "inkscapeStart", 110 | "terminal": { 111 | "name": "runCommand", 112 | "shellArgs": [], 113 | "autoClear": true, 114 | "autoFocus": false 115 | } 116 | }, 117 | "when": "editorTextFocus && vim.active && vim.use && !inDebugRepl && vim.mode == 'Visual'" 118 | }, 119 | { 120 | "key": "ctrl+c", 121 | "command": "command-runner.run", 122 | "args": { 123 | "command": "quiver", 124 | "terminal": { 125 | "name": "runCommand", 126 | "shellArgs": [], 127 | "autoClear": true, 128 | "autoFocus": false 129 | } 130 | }, 131 | "when": "editorTextFocus" 132 | } 133 | ] -------------------------------------------------------------------------------- /VSCode-setting/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | // Auto Correction setup 3 | "cSpell.diagnosticLevel": "Warning", 4 | // Inkscape setup 5 | "command-runner.terminal.name": "runCommand", 6 | "command-runner.terminal.autoClear": true, 7 | "command-runner.terminal.autoFocus": false, 8 | "command-runner.commands": { 9 | "inkscapeCreate": "inkscape-figures create ${selectedText} \"${fileDirname}/Figures/\"", 10 | "inkscapeEdit": "inkscape-figures edit \"${fileDirname}/Figures/\"", 11 | "inkscapeStart": "inkscape-figures watch", 12 | "quiver": "open -na 'Google Chrome' --args --new-window /quiver/src/index.html" 13 | }, 14 | // vsc-conceal 15 | "conceal.revealOn": "active-line", 16 | "conceal.substitutions": [ 17 | { 18 | "language": [ 19 | "latex", 20 | { 21 | "pattern": "**/*.{tex}" 22 | } 23 | ], 24 | "substitutions": [ 25 | { 26 | "ugly": "\\\\iiint", 27 | "pretty": "∭" 28 | }, 29 | { 30 | "ugly": "\\\\iint", 31 | "pretty": "∬" 32 | }, 33 | { 34 | "ugly": "\\\\int", 35 | "pretty": "∫" 36 | }, 37 | { 38 | "ugly": "\\\\forall", 39 | "pretty": "∀" 40 | }, 41 | { 42 | "ugly": "\\\\exists", 43 | "pretty": "∃" 44 | }, 45 | { 46 | "ugly": "\\\\nexists", 47 | "pretty": "∄" 48 | }, 49 | { 50 | "ugly": "\\\\leq", 51 | "pretty": "≤" 52 | }, 53 | { 54 | "ugly": "\\\\geq", 55 | "pretty": "≥" 56 | }, 57 | { 58 | "ugly": "\\\\gg", 59 | "pretty": "≫" 60 | }, 61 | { 62 | "ugly": "\\\\ll", 63 | "pretty": "≪" 64 | }, 65 | { 66 | "ugly": "\\\\notin", 67 | "pretty": "∉", 68 | "post": " " 69 | }, 70 | { 71 | "ugly": "\\\\in", 72 | "pretty": "∈", 73 | "post": " " 74 | }, 75 | { 76 | "ugly": "\\\\sim", 77 | "pretty": "ᯈ" 78 | }, 79 | { 80 | "ugly": "\\\\coloneqq", 81 | "pretty": "≔" 82 | }, 83 | { 84 | "ugly": "\\\\eqqcolon", 85 | "pretty": "≕" 86 | }, 87 | { 88 | "ugly": "\\\\subseteq", 89 | "pretty": "⊆" 90 | }, 91 | { 92 | "ugly": "\\\\subsetneq", 93 | "pretty": "⊊" 94 | }, 95 | { 96 | "ugly": "\\\\nsubseteq", 97 | "pretty": "⊈" 98 | }, 99 | { 100 | "ugly": "\\\\subset", 101 | "pretty": "⊂" 102 | }, 103 | { 104 | "ugly": "\\\\supseteq", 105 | "pretty": "⊇" 106 | }, 107 | { 108 | "ugly": "\\\\supsetneq", 109 | "pretty": "⊋" 110 | }, 111 | { 112 | "ugly": "\\\\nsupseteq", 113 | "pretty": "⊉" 114 | }, 115 | { 116 | "ugly": "\\\\supset", 117 | "pretty": "⊃" 118 | }, 119 | { 120 | "ugly": "\\\\sqcup", 121 | "pretty": "⊔" 122 | }, 123 | { 124 | "ugly": "\\\\cup", 125 | "pretty": "∪" 126 | }, 127 | { 128 | "ugly": "\\\\cap", 129 | "pretty": "∩" 130 | }, 131 | { 132 | "ugly": "\\\\times", 133 | "pretty": "×" 134 | }, 135 | { 136 | "ugly": "\\\\sqrt", 137 | "pretty": "√" 138 | }, 139 | { 140 | "ugly": "\\\\infty", 141 | "pretty": "∞" 142 | }, 143 | { 144 | "ugly": "\\\\bot", 145 | "pretty": "⊥" 146 | }, 147 | { 148 | "ugly": "\\\\prep", 149 | "pretty": "⊥" 150 | }, 151 | { 152 | "ugly": "\\\\top", 153 | "pretty": "⊤" 154 | }, 155 | { 156 | "ugly": "\\\\land", 157 | "pretty": "∧" 158 | }, 159 | { 160 | "ugly": "\\\\lor", 161 | "pretty": "∨" 162 | }, 163 | { 164 | "ugly": "\\\\lnot", 165 | "pretty": "¬" 166 | }, 167 | { 168 | "ugly": "\\\\setminus", 169 | "pretty": "∖" 170 | }, 171 | { 172 | "ugly": "\\\\equiv", 173 | "pretty": "≡" 174 | }, 175 | { 176 | "ugly": "\\\\circ", 177 | "pretty": "◯" 178 | }, 179 | { 180 | "ugly": "\\\\mid", 181 | "pretty": "|" 182 | }, 183 | { 184 | "ugly": "\\\\colon", 185 | "pretty": ":" 186 | }, 187 | { 188 | "ugly": "\\\\partial", 189 | "pretty": "∂" 190 | }, 191 | { 192 | "ugly": "\\\\oplus", 193 | "pretty": "⊕" 194 | }, 195 | { 196 | "ugly": "\\\\otimes", 197 | "pretty": "⊗" 198 | }, 199 | { 200 | "ugly": "\\\\leftarrow", 201 | "pretty": "←" 202 | }, 203 | { 204 | "ugly": "\\\\longleftarrow", 205 | "pretty": "⟵" 206 | }, 207 | { 208 | "ugly": "\\\\Leftarrow", 209 | "pretty": "⇐" 210 | }, 211 | { 212 | "ugly": "\\\\impliedby", 213 | "pretty": "⇐" 214 | }, 215 | { 216 | "ugly": "\\\\Longleftarrow", 217 | "pretty": "⟸" 218 | }, 219 | { 220 | "ugly": "\\\\rightarrow", 221 | "pretty": "→" 222 | }, 223 | { 224 | "ugly": "\\\\longrightarrow", 225 | "pretty": "⟶" 226 | }, 227 | { 228 | "ugly": "\\\\Rightarrow", 229 | "pretty": "⇒" 230 | }, 231 | { 232 | "ugly": "\\\\implies", 233 | "pretty": "⇒" 234 | }, 235 | { 236 | "ugly": "\\\\Longrightarrow", 237 | "pretty": "⟹" 238 | }, 239 | { 240 | "ugly": "\\\\iff", 241 | "pretty": "⇔" 242 | }, 243 | { 244 | "ugly": "\\\\leftrightarrow", 245 | "pretty": "↔" 246 | }, 247 | { 248 | "ugly": "\\\\uparrow", 249 | "pretty": "↑" 250 | }, 251 | { 252 | "ugly": "\\\\downarrow", 253 | "pretty": "↓" 254 | }, 255 | { 256 | "ugly": "\\\\to", 257 | "pretty": "→", 258 | "post": " " 259 | }, 260 | { 261 | "ugly": "\\\\gets", 262 | "pretty": "←" 263 | }, 264 | { 265 | "ugly": "\\\\Uparrow", 266 | "pretty": "⇑" 267 | }, 268 | { 269 | "ugly": "\\\\Downarrow", 270 | "pretty": "⇓" 271 | }, 272 | { 273 | "ugly": "\\\\approx", 274 | "pretty": "≈" 275 | }, 276 | { 277 | "ugly": "\\\\cdot", 278 | "pretty": "·" 279 | }, 280 | { 281 | "ugly": "\\\\left", 282 | "pretty": "\\l" 283 | }, 284 | { 285 | "ugly": "\\\\right", 286 | "pretty": "\\r" 287 | }, 288 | { 289 | "ugly": "\\\\langle", 290 | "pretty": "〈" 291 | }, 292 | { 293 | "ugly": "\\\\rangle", 294 | "pretty": "〉" 295 | }, 296 | { 297 | "ugly": "\\\\vert", 298 | "pretty": "|" 299 | }, 300 | { 301 | "ugly": "\\\\lVert", 302 | "pretty": "‖" 303 | }, 304 | { 305 | "ugly": "\\\\rVert", 306 | "pretty": "‖" 307 | }, 308 | { 309 | "ugly": "\\\\lfloor", 310 | "pretty": "⌊" 311 | }, 312 | { 313 | "ugly": "\\\\rfloor", 314 | "pretty": "⌋" 315 | }, 316 | { 317 | "ugly": "\\\\lceil", 318 | "pretty": "⌈" 319 | }, 320 | { 321 | "ugly": "\\\\rceil", 322 | "pretty": "⌉" 323 | }, 324 | { 325 | "ugly": "\\^\\{\\\\ast\\}", 326 | "pretty": "^*" 327 | }, 328 | { 329 | "ugly": "\\\\ast", 330 | "pretty": "*" 331 | }, 332 | { 333 | "ugly": "\\\\star", 334 | "pretty": "★" 335 | }, 336 | { 337 | "ugly": "\\^\\{\\\\prime\\}", 338 | "pretty": "'" 339 | }, 340 | { 341 | "ugly": "\\\\prime", 342 | "pretty": "'" 343 | }, 344 | { 345 | "ugly": "\\\\varnothing", 346 | "pretty": "∅" 347 | }, 348 | { 349 | "ugly": "\\\\alpha", 350 | "pretty": "α" 351 | }, 352 | { 353 | "ugly": "\\\\beta", 354 | "pretty": "β" 355 | }, 356 | { 357 | "ugly": "\\\\gamma", 358 | "pretty": "γ" 359 | }, 360 | { 361 | "ugly": "\\\\Gamma", 362 | "pretty": "Γ" 363 | }, 364 | { 365 | "ugly": "\\\\zeta", 366 | "pretty": "ζ" 367 | }, 368 | { 369 | "ugly": "\\\\delta", 370 | "pretty": "δ" 371 | }, 372 | { 373 | "ugly": "\\\\Delta", 374 | "pretty": "Δ" 375 | }, 376 | { 377 | "ugly": "\\\\eta", 378 | "pretty": "η" 379 | }, 380 | { 381 | "ugly": "\\\\varepsilon", 382 | "pretty": "ε" 383 | }, 384 | { 385 | "ugly": "\\\\epsilon", 386 | "pretty": "ϵ" 387 | }, 388 | { 389 | "ugly": "\\\\rho", 390 | "pretty": "ρ" 391 | }, 392 | { 393 | "ugly": "\\\\sigma", 394 | "pretty": "σ" 395 | }, 396 | { 397 | "ugly": "\\\\Sigma", 398 | "pretty": "Σ" 399 | }, 400 | { 401 | "ugly": "\\\\sum", 402 | "pretty": "𝚺" 403 | }, 404 | { 405 | "ugly": "\\\\theta", 406 | "pretty": "θ" 407 | }, 408 | { 409 | "ugly": "\\\\Theta", 410 | "pretty": "Θ" 411 | }, 412 | { 413 | "ugly": "\\\\tau", 414 | "pretty": "τ" 415 | }, 416 | { 417 | "ugly": "\\\\iota", 418 | "pretty": "ɩ" 419 | }, 420 | { 421 | "ugly": "\\\\phi", 422 | "pretty": "ϕ" 423 | }, 424 | { 425 | "ugly": "\\\\varphi", 426 | "pretty": "φ" 427 | }, 428 | { 429 | "ugly": "\\\\Phi", 430 | "pretty": "Φ" 431 | }, 432 | { 433 | "ugly": "\\\\psi", 434 | "pretty": "ψ" 435 | }, 436 | { 437 | "ugly": "\\\\Psi", 438 | "pretty": "Ψ" 439 | }, 440 | { 441 | "ugly": "\\\\lambda", 442 | "pretty": "λ" 443 | }, 444 | { 445 | "ugly": "\\\\Lambda", 446 | "pretty": "Λ" 447 | }, 448 | { 449 | "ugly": "\\\\kappa", 450 | "pretty": "κ" 451 | }, 452 | { 453 | "ugly": "\\\\nabla", 454 | "pretty": "∇" 455 | }, 456 | { 457 | "ugly": "\\\\mu", 458 | "pretty": "μ" 459 | }, 460 | { 461 | "ugly": "\\\\psi", 462 | "pretty": "ψ" 463 | }, 464 | { 465 | "ugly": "\\\\pi", 466 | "pretty": "π" 467 | }, 468 | { 469 | "ugly": "\\\\Pi", 470 | "pretty": "Π" 471 | }, 472 | { 473 | "ugly": "\\\\omega", 474 | "pretty": "ω" 475 | }, 476 | { 477 | "ugly": "\\\\Omega", 478 | "pretty": "Ω" 479 | }, 480 | { 481 | "ugly": "\\\\chi", 482 | "pretty": "χ" 483 | }, 484 | { 485 | "ugly": "\\\\xi", 486 | "pretty": "ξ" 487 | }, 488 | { 489 | "ugly": "\\\\Xi", 490 | "pretty": "Ξ" 491 | }, 492 | { 493 | "ugly": "\\\\ell", 494 | "pretty": "ℓ" 495 | }, 496 | { 497 | "ugly": "\\\\mathbb{A}", 498 | "pretty": "𝔸" 499 | }, 500 | { 501 | "ugly": "\\\\mathbb{B}", 502 | "pretty": "𝔹" 503 | }, 504 | { 505 | "ugly": "\\\\mathbb{C}", 506 | "pretty": "ℂ" 507 | }, 508 | { 509 | "ugly": "\\\\mathbb{D}", 510 | "pretty": "ⅅ" 511 | }, 512 | { 513 | "ugly": "\\\\mathbb{E}", 514 | "pretty": "𝔼" 515 | }, 516 | { 517 | "ugly": "\\\\mathbb{F}", 518 | "pretty": "𝔽" 519 | }, 520 | { 521 | "ugly": "\\\\mathbb{G}", 522 | "pretty": "𝔾" 523 | }, 524 | { 525 | "ugly": "\\\\mathbb{H}", 526 | "pretty": "ℍ" 527 | }, 528 | { 529 | "ugly": "\\\\mathbb{I}", 530 | "pretty": "𝕀" 531 | }, 532 | { 533 | "ugly": "\\\\mathbb{J}", 534 | "pretty": "𝕁" 535 | }, 536 | { 537 | "ugly": "\\\\mathbb{K}", 538 | "pretty": "𝕂" 539 | }, 540 | { 541 | "ugly": "\\\\mathbb{L}", 542 | "pretty": "𝕃" 543 | }, 544 | { 545 | "ugly": "\\\\mathbb{M}", 546 | "pretty": "𝕄" 547 | }, 548 | { 549 | "ugly": "\\\\mathbb{N}", 550 | "pretty": "ℕ" 551 | }, 552 | { 553 | "ugly": "\\\\mathbb{O}", 554 | "pretty": "𝕆" 555 | }, 556 | { 557 | "ugly": "\\\\mathbb{P}", 558 | "pretty": "ℙ" 559 | }, 560 | { 561 | "ugly": "\\\\mathbb{Q}", 562 | "pretty": "ℚ" 563 | }, 564 | { 565 | "ugly": "\\\\mathbb{R}", 566 | "pretty": "ℝ" 567 | }, 568 | { 569 | "ugly": "\\\\mathbb{S}", 570 | "pretty": "𝕊" 571 | }, 572 | { 573 | "ugly": "\\\\mathbb{T}", 574 | "pretty": "𝕋" 575 | }, 576 | { 577 | "ugly": "\\\\mathbb{U}", 578 | "pretty": "𝕌" 579 | }, 580 | { 581 | "ugly": "\\\\mathbb{V}", 582 | "pretty": "𝕍" 583 | }, 584 | { 585 | "ugly": "\\\\mathbb{W}", 586 | "pretty": "𝕎" 587 | }, 588 | { 589 | "ugly": "\\\\mathbb{X}", 590 | "pretty": "𝕏" 591 | }, 592 | { 593 | "ugly": "\\\\mathbb{Y}", 594 | "pretty": "𝕐" 595 | }, 596 | { 597 | "ugly": "\\\\mathbb{Z}", 598 | "pretty": "ℤ" 599 | }, 600 | { 601 | "ugly": "\\\\mathcal{A}", 602 | "pretty": "𝒜" 603 | }, 604 | { 605 | "ugly": "\\\\mathcal{B}", 606 | "pretty": "ℬ" 607 | }, 608 | { 609 | "ugly": "\\\\mathcal{C}", 610 | "pretty": "𝒞" 611 | }, 612 | { 613 | "ugly": "\\\\mathcal{D}", 614 | "pretty": "𝒟" 615 | }, 616 | { 617 | "ugly": "\\\\mathcal{E}", 618 | "pretty": "ℰ" 619 | }, 620 | { 621 | "ugly": "\\\\mathcal{F}", 622 | "pretty": "ℱ" 623 | }, 624 | { 625 | "ugly": "\\\\mathcal{G}", 626 | "pretty": "𝒢" 627 | }, 628 | { 629 | "ugly": "\\\\mathcal{H}", 630 | "pretty": "ℋ" 631 | }, 632 | { 633 | "ugly": "\\\\mathcal{I}", 634 | "pretty": "ℐ" 635 | }, 636 | { 637 | "ugly": "\\\\mathcal{J}", 638 | "pretty": "𝒥" 639 | }, 640 | { 641 | "ugly": "\\\\mathcal{K}", 642 | "pretty": "𝒦" 643 | }, 644 | { 645 | "ugly": "\\\\mathcal{L}", 646 | "pretty": "ℒ" 647 | }, 648 | { 649 | "ugly": "\\\\mathcal{M}", 650 | "pretty": "ℳ" 651 | }, 652 | { 653 | "ugly": "\\\\mathcal{N}", 654 | "pretty": "𝒩" 655 | }, 656 | { 657 | "ugly": "\\\\mathcal{O}", 658 | "pretty": "𝒪" 659 | }, 660 | { 661 | "ugly": "\\\\mathcal{P}", 662 | "pretty": "𝒫" 663 | }, 664 | { 665 | "ugly": "\\\\mathcal{Q}", 666 | "pretty": "𝒬" 667 | }, 668 | { 669 | "ugly": "\\\\mathcal{R}", 670 | "pretty": "ℛ" 671 | }, 672 | { 673 | "ugly": "\\\\mathcal{S}", 674 | "pretty": "𝒮" 675 | }, 676 | { 677 | "ugly": "\\\\mathcal{T}", 678 | "pretty": "𝒯" 679 | }, 680 | { 681 | "ugly": "\\\\mathcal{U}", 682 | "pretty": "𝒰" 683 | }, 684 | { 685 | "ugly": "\\\\mathcal{V}", 686 | "pretty": "𝒱" 687 | }, 688 | { 689 | "ugly": "\\\\mathcal{W}", 690 | "pretty": "𝒲" 691 | }, 692 | { 693 | "ugly": "\\\\mathcal{X}", 694 | "pretty": "𝒳" 695 | }, 696 | { 697 | "ugly": "\\\\mathcal{Y}", 698 | "pretty": "𝒴" 699 | }, 700 | { 701 | "ugly": "\\\\mathcal{Z}", 702 | "pretty": "𝒵" 703 | }, 704 | { 705 | "ugly": "\\\\mathscr{A}", 706 | "pretty": "𝓐" 707 | }, 708 | { 709 | "ugly": "\\\\mathscr{B}", 710 | "pretty": "𝓑" 711 | }, 712 | { 713 | "ugly": "\\\\mathscr{C}", 714 | "pretty": "𝓒" 715 | }, 716 | { 717 | "ugly": "\\\\mathscr{D}", 718 | "pretty": "𝓓" 719 | }, 720 | { 721 | "ugly": "\\\\mathscr{E}", 722 | "pretty": "𝓔" 723 | }, 724 | { 725 | "ugly": "\\\\mathscr{F}", 726 | "pretty": "𝓕" 727 | }, 728 | { 729 | "ugly": "\\\\mathscr{G}", 730 | "pretty": "𝓖" 731 | }, 732 | { 733 | "ugly": "\\\\mathscr{H}", 734 | "pretty": "𝓗" 735 | }, 736 | { 737 | "ugly": "\\\\mathscr{I}", 738 | "pretty": "𝓘" 739 | }, 740 | { 741 | "ugly": "\\\\mathscr{J}", 742 | "pretty": "𝓙" 743 | }, 744 | { 745 | "ugly": "\\\\mathscr{K}", 746 | "pretty": "𝓚" 747 | }, 748 | { 749 | "ugly": "\\\\mathscr{L}", 750 | "pretty": "𝓛" 751 | }, 752 | { 753 | "ugly": "\\\\mathscr{M}", 754 | "pretty": "𝓜" 755 | }, 756 | { 757 | "ugly": "\\\\mathscr{N}", 758 | "pretty": "𝓝" 759 | }, 760 | { 761 | "ugly": "\\\\mathscr{O}", 762 | "pretty": "𝓞" 763 | }, 764 | { 765 | "ugly": "\\\\mathscr{P}", 766 | "pretty": "𝓟" 767 | }, 768 | { 769 | "ugly": "\\\\mathscr{Q}", 770 | "pretty": "𝓠" 771 | }, 772 | { 773 | "ugly": "\\\\mathscr{R}", 774 | "pretty": "𝓡" 775 | }, 776 | { 777 | "ugly": "\\\\mathscr{S}", 778 | "pretty": "𝓢" 779 | }, 780 | { 781 | "ugly": "\\\\mathscr{T}", 782 | "pretty": "𝓣" 783 | }, 784 | { 785 | "ugly": "\\\\mathscr{U}", 786 | "pretty": "𝓤" 787 | }, 788 | { 789 | "ugly": "\\\\mathscr{V}", 790 | "pretty": "𝓥" 791 | }, 792 | { 793 | "ugly": "\\\\mathscr{W}", 794 | "pretty": "𝓦" 795 | }, 796 | { 797 | "ugly": "\\\\mathscr{X}", 798 | "pretty": "𝓧" 799 | }, 800 | { 801 | "ugly": "\\\\mathscr{Y}", 802 | "pretty": "𝓨" 803 | }, 804 | { 805 | "ugly": "\\\\mathscr{Z}", 806 | "pretty": "𝓩" 807 | }, 808 | { 809 | "ugly": "\\\\mathfrak{A}", 810 | "pretty": "𝔄" 811 | }, 812 | { 813 | "ugly": "\\\\mathfrak{B}", 814 | "pretty": "𝔅" 815 | }, 816 | { 817 | "ugly": "\\\\mathfrak{C}", 818 | "pretty": "ℭ" 819 | }, 820 | { 821 | "ugly": "\\\\mathfrak{D}", 822 | "pretty": "𝔇" 823 | }, 824 | { 825 | "ugly": "\\\\mathfrak{E}", 826 | "pretty": "𝔈" 827 | }, 828 | { 829 | "ugly": "\\\\mathfrak{F}", 830 | "pretty": "𝔉" 831 | }, 832 | { 833 | "ugly": "\\\\mathfrak{G}", 834 | "pretty": "𝔊" 835 | }, 836 | { 837 | "ugly": "\\\\mathfrak{H}", 838 | "pretty": "ℌ" 839 | }, 840 | { 841 | "ugly": "\\\\mathfrak{I}", 842 | "pretty": "ℑ" 843 | }, 844 | { 845 | "ugly": "\\\\mathfrak{J}", 846 | "pretty": "𝔍" 847 | }, 848 | { 849 | "ugly": "\\\\mathfrak{K}", 850 | "pretty": "𝔎" 851 | }, 852 | { 853 | "ugly": "\\\\mathfrak{L}", 854 | "pretty": "𝔏" 855 | }, 856 | { 857 | "ugly": "\\\\mathfrak{M}", 858 | "pretty": "𝔐" 859 | }, 860 | { 861 | "ugly": "\\\\mathfrak{N}", 862 | "pretty": "𝔑" 863 | }, 864 | { 865 | "ugly": "\\\\mathfrak{O}", 866 | "pretty": "𝔒" 867 | }, 868 | { 869 | "ugly": "\\\\mathfrak{P}", 870 | "pretty": "𝔓" 871 | }, 872 | { 873 | "ugly": "\\\\mathfrak{Q}", 874 | "pretty": "𝔔" 875 | }, 876 | { 877 | "ugly": "\\\\mathfrak{R}", 878 | "pretty": "ℜ" 879 | }, 880 | { 881 | "ugly": "\\\\mathfrak{S}", 882 | "pretty": "𝔖" 883 | }, 884 | { 885 | "ugly": "\\\\mathfrak{T}", 886 | "pretty": "𝔗" 887 | }, 888 | { 889 | "ugly": "\\\\mathfrak{U}", 890 | "pretty": "𝔘" 891 | }, 892 | { 893 | "ugly": "\\\\mathfrak{V}", 894 | "pretty": "𝔙" 895 | }, 896 | { 897 | "ugly": "\\\\mathfrak{W}", 898 | "pretty": "𝔚" 899 | }, 900 | { 901 | "ugly": "\\\\mathfrak{X}", 902 | "pretty": "𝔛" 903 | }, 904 | { 905 | "ugly": "\\\\mathfrak{Y}", 906 | "pretty": "𝔜" 907 | }, 908 | { 909 | "ugly": "\\\\mathfrak{Z}", 910 | "pretty": "ℨ" 911 | }, 912 | ] 913 | } 914 | ], 915 | } -------------------------------------------------------------------------------- /demo/figures/Karabiner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sleepymalc/VSCode-LaTeX-Inkscape/7798dea2bb37053299d8cd04f7cac2d642cb0a3d/demo/figures/Karabiner.png -------------------------------------------------------------------------------- /demo/figures/conceal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sleepymalc/VSCode-LaTeX-Inkscape/7798dea2bb37053299d8cd04f7cac2d642cb0a3d/demo/figures/conceal.png -------------------------------------------------------------------------------- /demo/figures/inkscape_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sleepymalc/VSCode-LaTeX-Inkscape/7798dea2bb37053299d8cd04f7cac2d642cb0a3d/demo/figures/inkscape_example.png -------------------------------------------------------------------------------- /demo/figures/inkscape_shortcut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sleepymalc/VSCode-LaTeX-Inkscape/7798dea2bb37053299d8cd04f7cac2d642cb0a3d/demo/figures/inkscape_shortcut.png -------------------------------------------------------------------------------- /demo/figures/sourcecode-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sleepymalc/VSCode-LaTeX-Inkscape/7798dea2bb37053299d8cd04f7cac2d642cb0a3d/demo/figures/sourcecode-1.png -------------------------------------------------------------------------------- /demo/figures/sourcecode-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sleepymalc/VSCode-LaTeX-Inkscape/7798dea2bb37053299d8cd04f7cac2d642cb0a3d/demo/figures/sourcecode-2.png -------------------------------------------------------------------------------- /demo/figures/sourcecode-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sleepymalc/VSCode-LaTeX-Inkscape/7798dea2bb37053299d8cd04f7cac2d642cb0a3d/demo/figures/sourcecode-3.png -------------------------------------------------------------------------------- /demo/figures/sourcecode-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sleepymalc/VSCode-LaTeX-Inkscape/7798dea2bb37053299d8cd04f7cac2d642cb0a3d/demo/figures/sourcecode-4.png -------------------------------------------------------------------------------- /demo/gifs/demo-create-inkscape.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sleepymalc/VSCode-LaTeX-Inkscape/7798dea2bb37053299d8cd04f7cac2d642cb0a3d/demo/gifs/demo-create-inkscape.gif -------------------------------------------------------------------------------- /demo/gifs/demo-edit-inkscape.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sleepymalc/VSCode-LaTeX-Inkscape/7798dea2bb37053299d8cd04f7cac2d642cb0a3d/demo/gifs/demo-edit-inkscape.gif -------------------------------------------------------------------------------- /demo/gifs/dm.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sleepymalc/VSCode-LaTeX-Inkscape/7798dea2bb37053299d8cd04f7cac2d642cb0a3d/demo/gifs/dm.gif -------------------------------------------------------------------------------- /demo/gifs/fast.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sleepymalc/VSCode-LaTeX-Inkscape/7798dea2bb37053299d8cd04f7cac2d642cb0a3d/demo/gifs/fast.gif -------------------------------------------------------------------------------- /demo/gifs/fm.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sleepymalc/VSCode-LaTeX-Inkscape/7798dea2bb37053299d8cd04f7cac2d642cb0a3d/demo/gifs/fm.gif -------------------------------------------------------------------------------- /demo/gifs/integral.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sleepymalc/VSCode-LaTeX-Inkscape/7798dea2bb37053299d8cd04f7cac2d642cb0a3d/demo/gifs/integral.gif -------------------------------------------------------------------------------- /demo/gifs/integral2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sleepymalc/VSCode-LaTeX-Inkscape/7798dea2bb37053299d8cd04f7cac2d642cb0a3d/demo/gifs/integral2.gif -------------------------------------------------------------------------------- /demo/gifs/pmatrix.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sleepymalc/VSCode-LaTeX-Inkscape/7798dea2bb37053299d8cd04f7cac2d642cb0a3d/demo/gifs/pmatrix.gif -------------------------------------------------------------------------------- /demo/gifs/quiver.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sleepymalc/VSCode-LaTeX-Inkscape/7798dea2bb37053299d8cd04f7cac2d642cb0a3d/demo/gifs/quiver.gif -------------------------------------------------------------------------------- /demo/gifs/spell.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sleepymalc/VSCode-LaTeX-Inkscape/7798dea2bb37053299d8cd04f7cac2d642cb0a3d/demo/gifs/spell.gif -------------------------------------------------------------------------------- /demo/gifs/table.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sleepymalc/VSCode-LaTeX-Inkscape/7798dea2bb37053299d8cd04f7cac2d642cb0a3d/demo/gifs/table.gif -------------------------------------------------------------------------------- /demo/gifs/useful.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sleepymalc/VSCode-LaTeX-Inkscape/7798dea2bb37053299d8cd04f7cac2d642cb0a3d/demo/gifs/useful.gif -------------------------------------------------------------------------------- /preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sleepymalc/VSCode-LaTeX-Inkscape/7798dea2bb37053299d8cd04f7cac2d642cb0a3d/preview.png --------------------------------------------------------------------------------