From fcd3edbad26edcc61d5c2289558ebedbe5aebb23 Mon Sep 17 00:00:00 2001 From: arne314 <73391160+arne314@users.noreply.github.com> Date: Thu, 31 Jul 2025 18:42:26 +0200 Subject: [PATCH 01/17] minor: `vim.loop` -> `vim.uv` --- lua/typstar/engine.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/typstar/engine.lua b/lua/typstar/engine.lua index f09bb8a..54d1dff 100644 --- a/lua/typstar/engine.lua +++ b/lua/typstar/engine.lua @@ -15,7 +15,7 @@ local ts_string_query = ts.query.parse('typst', '(string) @string') utils.generate_bool_set(cfg.exclude, exclude_triggers_set) vim.api.nvim_create_autocmd('TextChangedI', { - callback = function() last_keystroke_time = vim.loop.now() end, + callback = function() last_keystroke_time = vim.uv.now() end, }) M.in_math = function() From d4fd7c47e70bac2821c74c82d2a082a0ef3081f0 Mon Sep 17 00:00:00 2001 From: arne314 <73391160+arne314@users.noreply.github.com> Date: Fri, 1 Aug 2025 07:57:26 +0200 Subject: [PATCH 02/17] refactor: rename `excalidraw` to `drawings.lua` --- lua/typstar/{excalidraw.lua => drawings.lua} | 0 lua/typstar/init.lua | 6 +++--- 2 files changed, 3 insertions(+), 3 deletions(-) rename lua/typstar/{excalidraw.lua => drawings.lua} (100%) diff --git a/lua/typstar/excalidraw.lua b/lua/typstar/drawings.lua similarity index 100% rename from lua/typstar/excalidraw.lua rename to lua/typstar/drawings.lua diff --git a/lua/typstar/init.lua b/lua/typstar/init.lua index 5bfe148..9f1d91f 100644 --- a/lua/typstar/init.lua +++ b/lua/typstar/init.lua @@ -6,15 +6,15 @@ local luasnip = nil M.setup = function(args) config.merge_config(args) local autosnippets = require('typstar.autosnippets') - local excalidraw = require('typstar.excalidraw') + local drawings = require('typstar.drawings') local anki = require('typstar.anki') vim.api.nvim_create_user_command('TypstarToggleSnippets', autosnippets.toggle_autosnippets, {}) vim.api.nvim_create_user_command('TypstarSmartJump', function() M.smart_jump(1) end, {}) vim.api.nvim_create_user_command('TypstarSmartJumpBack', function() M.smart_jump(-1) end, {}) - vim.api.nvim_create_user_command('TypstarInsertExcalidraw', excalidraw.insert_drawing, {}) - vim.api.nvim_create_user_command('TypstarOpenExcalidraw', excalidraw.open_drawing, {}) + vim.api.nvim_create_user_command('TypstarInsertExcalidraw', drawings.insert_drawing, {}) + vim.api.nvim_create_user_command('TypstarOpenExcalidraw', drawings.open_drawing, {}) vim.api.nvim_create_user_command('TypstarAnkiScan', anki.scan, {}) vim.api.nvim_create_user_command('TypstarAnkiReimport', anki.scan_reimport, {}) From a73a7f4ade57fa8773ea5a08135193c599cce672 Mon Sep 17 00:00:00 2001 From: arne314 <73391160+arne314@users.noreply.github.com> Date: Fri, 1 Aug 2025 09:23:02 +0200 Subject: [PATCH 03/17] chore(draw): add rnote default template --- res/rnote_template.rnote | Bin 0 -> 358 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 res/rnote_template.rnote diff --git a/res/rnote_template.rnote b/res/rnote_template.rnote new file mode 100644 index 0000000000000000000000000000000000000000..6a3d6cba276f0cf74a29f5539b7120385ea44770 GIT binary patch literal 358 zcmV-s0h#_EiwFP!00000|D96XZi6rk{TDxH2)a=Uzfw&oB>|#BjGWM``1d&}YNzdC z(q3ZA@i{)n$sJCboIwi}q)E0(QV^h|mr&h7+s;^BJ1ZyGqYov8%{*!w^9xjIk_x!4 z@obdup(@Jvq)0OXy*6FXomsZoB>6@_i!{mz{j*g~2C8x!XpsyLl>Q-dLh>YyXPE~i zrKxUkKnkQ3bnFVI>tePoxF=?LE`S|nNxBGNE9d%wbx?Vl^;+XW?6)>nnMV4+87>)d zkj=3poUICDzR$~oz;2C)H9jXz(tAxowMDDpS*V@4X{h$^ylxSW&)3xtH>;QO7dz7r z@`^Jr4O(;2>Osm@__gW@oe2wLgRLA~Fww|SQ+!rzTc;Vfe~Sx_Co5P*mHCc~AG<6T zZ;0b_ah~v~S>ZUb9qXBVe}|JCX2#ih7{u!{r3-_bp3vfd0JG&~gyOP%0bIHyd7=UU E05htlp#T5? literal 0 HcmV?d00001 From 5827f6e553030f91c69b7b071970a8d207a4f7a7 Mon Sep 17 00:00:00 2001 From: arne314 <73391160+arne314@users.noreply.github.com> Date: Fri, 1 Aug 2025 09:25:27 +0200 Subject: [PATCH 04/17] feat(draw)!: insert and open rnote drawings `:TypstarOpenExcalidraw` has been renamed to `:TypstarOpenDrawing` to handle both Excalidraw and Rnote --- lua/typstar/config.lua | 22 ++++++++++----- lua/typstar/drawings.lua | 58 +++++++++++++++++++++++++++++----------- lua/typstar/init.lua | 5 ++-- 3 files changed, 62 insertions(+), 23 deletions(-) diff --git a/lua/typstar/config.lua b/lua/typstar/config.lua index 0e8d344..8dcdc92 100644 --- a/lua/typstar/config.lua +++ b/lua/typstar/config.lua @@ -13,8 +13,16 @@ local default_config = { filename = 'drawing-%Y-%m-%d-%H-%M-%S', fileExtension = '.excalidraw.md', fileExtensionInserted = '.excalidraw.svg', - uriOpenCommand = 'xdg-open', -- set depending on OS - templatePath = nil, + uriOpenCommand = 'xdg-open', -- set depending on OS; try setting it to "obsidian" directly if you encounter problems and have it in your PATH + templatePath = {}, + }, + rnote = { + assetsDir = 'assets', + filename = 'drawing-%Y-%m-%d-%H-%M-%S', + fileExtension = '.rnote', + fileExtensionInserted = '.rnote.svg', + uriOpenCommand = 'xdg-open', -- see comment above for excalidraw + templatePath = {}, }, snippets = { enable = true, @@ -38,10 +46,12 @@ function M.merge_config(args) M.config.typstarRoot = M.config.typstarRoot or debug.getinfo(1).source:match('^@(.*)/lua/typstar/config%.lua$') or '~/typstar' - M.config.excalidraw.templatePath = M.config.excalidraw.templatePath - or { - ['%.excalidraw%.md$'] = M.config.typstarRoot .. '/res/excalidraw_template.excalidraw.md', - } + vim.list_extend(M.config.excalidraw.templatePath, { + { '%.excalidraw%.md$', M.config.typstarRoot .. '/res/excalidraw_template.excalidraw.md' }, + }) + vim.list_extend(M.config.rnote.templatePath, { + { '%.rnote$', M.config.typstarRoot .. '/res/rnote_template.rnote' }, + }) end M.merge_config(nil) diff --git a/lua/typstar/drawings.lua b/lua/typstar/drawings.lua index fb6a8ab..e649a7c 100644 --- a/lua/typstar/drawings.lua +++ b/lua/typstar/drawings.lua @@ -2,22 +2,29 @@ local M = {} local config = require('typstar.config') local utils = require('typstar.utils') -local cfg = config.config.excalidraw local affix = [[ #figure( image("%s"), ) ]] +local config_excalidraw = config.config.excalidraw +local config_rnote = config.config.rnote -local function launch_obsidian(path) - print(string.format('Opening %s in Excalidraw', path)) +local function launch_excalidraw(path) + print(string.format('Opening %s in Obsidian Excalidraw', path)) utils.run_shell_command( - string.format('%s "obsidian://open?path=%s"', cfg.uriOpenCommand, utils.urlencode(path)), + string.format('%s "obsidian://open?path=%s"', config_excalidraw.uriOpenCommand, utils.urlencode(path)), false ) end -function M.insert_drawing() +local function launch_rnote(path) + print(string.format('Opening %s in Rnote', path)) + utils.run_shell_command(string.format('%s %s', config_rnote.uriOpenCommand, path), false) +end + +local function insert_drawing(provider) + local cfg = provider[1] local assets_dir = vim.fn.expand('%:p:h') .. '/' .. cfg.assetsDir local filename = os.date(cfg.filename) local path = assets_dir .. '/' .. filename .. cfg.fileExtension @@ -25,7 +32,9 @@ function M.insert_drawing() if vim.fn.isdirectory(assets_dir) == 0 then vim.fn.mkdir(assets_dir, 'p') end local found_match = false - for pattern, template_path in pairs(cfg.templatePath) do + for _, template_config in ipairs(cfg.templatePath) do + local pattern = template_config[1] + local template_path = template_config[2] if string.match(path, pattern) then found_match = true utils.run_shell_command(string.format('cat %s > %s', template_path, path), false) -- don't copy file metadata @@ -33,21 +42,40 @@ function M.insert_drawing() end end if not found_match then - print('No matching template found for the path: ' .. path) + print('No matching template found for path: ' .. path) return end - utils.insert_text_block(string.format(affix, path_inserted)) - launch_obsidian(path) + utils.insert_text_block(string.format(provider[2], path_inserted)) + provider[3](path) end +local excalidraw = { + config_excalidraw, + affix, + launch_excalidraw, +} +local rnote = { + config_rnote, + affix, + launch_rnote, +} +local providers = { excalidraw, rnote } + +function M.insert_obsidian_excalidraw() insert_drawing(excalidraw) end +function M.insert_rnote() insert_drawing(rnote) end + function M.open_drawing() - local line = vim.api.nvim_get_current_line() - local path = vim.fn.expand('%:p:h') - .. '/' - .. string.match(line, '"(.*)' .. string.gsub(cfg.fileExtensionInserted, '%.', '%%%.')) - .. '.excalidraw.md' - launch_obsidian(path) + for _, provider in pairs(providers) do + local cfg = provider[1] + local line = vim.api.nvim_get_current_line() + local filename = line:match('"(.*)' .. string.gsub(cfg.fileExtensionInserted, '%.', '%%%.')) + if filename ~= nil and filename:match('^%s*$') == nil then + local path = vim.fn.expand('%:p:h') .. '/' .. filename .. cfg.fileExtension + provider[3](path) -- launch program + break + end + end end return M diff --git a/lua/typstar/init.lua b/lua/typstar/init.lua index 9f1d91f..5be4f29 100644 --- a/lua/typstar/init.lua +++ b/lua/typstar/init.lua @@ -13,8 +13,9 @@ M.setup = function(args) vim.api.nvim_create_user_command('TypstarSmartJump', function() M.smart_jump(1) end, {}) vim.api.nvim_create_user_command('TypstarSmartJumpBack', function() M.smart_jump(-1) end, {}) - vim.api.nvim_create_user_command('TypstarInsertExcalidraw', drawings.insert_drawing, {}) - vim.api.nvim_create_user_command('TypstarOpenExcalidraw', drawings.open_drawing, {}) + vim.api.nvim_create_user_command('TypstarInsertExcalidraw', drawings.insert_obsidian_excalidraw, {}) + vim.api.nvim_create_user_command('TypstarInsertRnote', drawings.insert_rnote, {}) + vim.api.nvim_create_user_command('TypstarOpenDrawing', drawings.open_drawing, {}) vim.api.nvim_create_user_command('TypstarAnkiScan', anki.scan, {}) vim.api.nvim_create_user_command('TypstarAnkiReimport', anki.scan_reimport, {}) From 7c862f2fdc84ff6215df483832301380913727b5 Mon Sep 17 00:00:00 2001 From: arne314 <73391160+arne314@users.noreply.github.com> Date: Fri, 1 Aug 2025 15:52:58 +0200 Subject: [PATCH 05/17] feat(draw): auto export rnote on save --- lua/typstar/config.lua | 3 ++- lua/typstar/drawings.lua | 32 ++++++++++++++++++++++++++++---- lua/typstar/utils.lua | 9 +++++---- 3 files changed, 35 insertions(+), 9 deletions(-) diff --git a/lua/typstar/config.lua b/lua/typstar/config.lua index 8dcdc92..c1380aa 100644 --- a/lua/typstar/config.lua +++ b/lua/typstar/config.lua @@ -18,9 +18,10 @@ local default_config = { }, rnote = { assetsDir = 'assets', + exportCommand = 'rnote-cli export doc --no-background --no-pattern --on-conflict overwrite --output-file %s %s', filename = 'drawing-%Y-%m-%d-%H-%M-%S', fileExtension = '.rnote', - fileExtensionInserted = '.rnote.svg', + fileExtensionInserted = '.rnote.svg', -- valid rnote export type uriOpenCommand = 'xdg-open', -- see comment above for excalidraw templatePath = {}, }, diff --git a/lua/typstar/drawings.lua b/lua/typstar/drawings.lua index e649a7c..05db403 100644 --- a/lua/typstar/drawings.lua +++ b/lua/typstar/drawings.lua @@ -10,7 +10,7 @@ local affix = [[ local config_excalidraw = config.config.excalidraw local config_rnote = config.config.rnote -local function launch_excalidraw(path) +local function launch_excalidraw(path, path_inserted) print(string.format('Opening %s in Obsidian Excalidraw', path)) utils.run_shell_command( string.format('%s "obsidian://open?path=%s"', config_excalidraw.uriOpenCommand, utils.urlencode(path)), @@ -18,9 +18,32 @@ local function launch_excalidraw(path) ) end -local function launch_rnote(path) +local rnote_watched = {} + +local function auto_export_rnote(path, path_inserted) + if rnote_watched[path] then return end + rnote_watched[path] = true + local job_id = -1 + local last_export = 0 + + local run_export = function(err, filename) + local time = vim.uv.now() + if err ~= nil or time - last_export < 800 then return end + + if job_id == -1 then + last_export = time + local cmd = string.format(config_rnote.exportCommand, path_inserted, path) + job_id = utils.run_shell_command(cmd, false, nil, { on_exit = function() job_id = -1 end }) + end + end + local watcher = vim.uv.new_fs_event() + watcher:start(path, {}, vim.schedule_wrap(run_export)) +end + +local function launch_rnote(path, path_inserted) print(string.format('Opening %s in Rnote', path)) utils.run_shell_command(string.format('%s %s', config_rnote.uriOpenCommand, path), false) + auto_export_rnote(path, path_inserted) end local function insert_drawing(provider) @@ -47,7 +70,7 @@ local function insert_drawing(provider) end utils.insert_text_block(string.format(provider[2], path_inserted)) - provider[3](path) + provider[3](path, path_inserted) end local excalidraw = { @@ -72,7 +95,8 @@ function M.open_drawing() local filename = line:match('"(.*)' .. string.gsub(cfg.fileExtensionInserted, '%.', '%%%.')) if filename ~= nil and filename:match('^%s*$') == nil then local path = vim.fn.expand('%:p:h') .. '/' .. filename .. cfg.fileExtension - provider[3](path) -- launch program + local path_inserted = vim.fn.expand('%:p:h') .. '/' .. filename .. cfg.fileExtensionInserted + provider[3](path, path_inserted) -- launch program break end end diff --git a/lua/typstar/utils.lua b/lua/typstar/utils.lua index d83bdac..3fe2eea 100644 --- a/lua/typstar/utils.lua +++ b/lua/typstar/utils.lua @@ -26,8 +26,9 @@ function M.insert_text_block(snip) vim.api.nvim_buf_set_lines(vim.api.nvim_get_current_buf(), line_num, line_num, false, lines) end -function M.run_shell_command(cmd, show_output, extra_handler) +function M.run_shell_command(cmd, show_output, extra_handler, opts) extra_handler = extra_handler or function(msg) end + opts = opts or { on_exit = function() end } local handle_output = function(data, err) local msg = table.concat(data, '\n') if not string.match(msg, '^%s*$') then @@ -37,14 +38,14 @@ function M.run_shell_command(cmd, show_output, extra_handler) end end if show_output then - vim.fn.jobstart(cmd, { + return vim.fn.jobstart(cmd, vim.tbl_deep_extend({ on_stdout = function(_, data, _) handle_output(data, false) end, on_stderr = function(_, data, _) handle_output(data, true) end, stdout_buffered = false, stderr_buffered = true, - }) + }, opts, "force")) else - vim.fn.jobstart(cmd) + return vim.fn.jobstart(cmd, opts) end end From 25c7ac828955d49c1b6daf313e6883b8d4cb0aaf Mon Sep 17 00:00:00 2001 From: arne314 <73391160+arne314@users.noreply.github.com> Date: Sat, 2 Aug 2025 14:52:49 +0200 Subject: [PATCH 06/17] chore(draw): add keymaps to dev flake --- flake.nix | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/flake.nix b/flake.nix index 803b40d..f2e7871 100644 --- a/flake.nix +++ b/flake.nix @@ -42,6 +42,8 @@ lua << EOF print("Welcome to Typstar! This is just a demo.") + vim.g.mapleader = " " + require('nvim-treesitter.configs').setup { highlight = { enable = true }, } @@ -58,6 +60,12 @@ vim.keymap.set({'n', 'i'}, '', 'TypstarToggleSnippets', { silent = true, noremap = true }) vim.keymap.set({'s', 'i'}, '', 'TypstarSmartJump', { silent = true, noremap = true }) vim.keymap.set({'s', 'i'}, '', 'TypstarSmartJumpBack', { silent = true, noremap = true }) + + vim.keymap.set('n', 'e', 'TypstarInsertExcalidraw', { silent = true, noremap = true }) + vim.keymap.set('n', 'r', 'TypstarInsertRnote', { silent = true, noremap = true }) + vim.keymap.set('n', 'o', 'TypstarOpenDrawing', { silent = true, noremap = true }) + + vim.keymap.set('n', 'a', 'TypstarAnkiScan', { silent = true, noremap = true }) EOF ''; plugins = [ From 60b6073ca674c2ebfd0a39f1e6d93f9bac4b1f0a Mon Sep 17 00:00:00 2001 From: arne314 <73391160+arne314@users.noreply.github.com> Date: Sat, 2 Aug 2025 14:53:50 +0200 Subject: [PATCH 07/17] docs(draw): rnote usage and installation --- README.md | 21 +++++++++++++++------ lua/typstar/config.lua | 4 ++-- lua/typstar/drawings.lua | 2 +- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 5488179..82e3e5f 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ # Typstar -Neovim plugin for efficient note taking in Typst +Neovim plugin for efficient (mathematical) note taking in Typst ## Features - Powerful autosnippets using [LuaSnip](https://github.com/L3MON4D3/LuaSnip/) and [Tree-sitter](https://tree-sitter.github.io/) (inspired by [fastex.nvim](https://github.com/lentilus/fastex.nvim)) -- Easy insertion of drawings using [Obsidian Excalidraw](https://github.com/zsviczian/obsidian-excalidraw-plugin) +- Easy insertion of drawings using [Obsidian Excalidraw](https://github.com/zsviczian/obsidian-excalidraw-plugin) or [Rnote](https://github.com/flxzt/rnote) - Export of [Anki](https://apps.ankiweb.net/) flashcards \[No Neovim required\] ## Usage @@ -32,9 +32,12 @@ Math snippets: Note that you can [customize](#custom-snippets) (enable, disable and modify) every snippet. -### Excalidraw -- Use `:TypstarInsertExcalidraw` to create a new drawing using the configured template, insert a figure displaying it and open it in Obsidian. -- To open an inserted drawing in Obsidian, simply run `:TypstarOpenExcalidraw` while your cursor is on a line referencing the drawing. +### Excalidraw/Rnote +- Use `:TypstarInsertExcalidraw`/`:TypstarInsertRnote` to + create a new drawing using the [configured](#configuration) template, + insert a figure displaying it and open it in Obsidian/Rnote. +- To open an inserted drawing in Obsidian/Rnote, + simply run `:TypstarOpenDrawing` while your cursor is on a line referencing the drawing. ### Anki Use the `flA` snippet to create a new flashcard @@ -156,7 +159,13 @@ require('typstar').setup({ -- depending on your neovim plugin system 1. Install [Obsidian](https://obsidian.md/) and create a vault in your typst note taking directory 2. Install the [obsidian-excalidraw-plugin](https://github.com/zsviczian/obsidian-excalidraw-plugin) and enable `Auto-export SVG` (in plugin settings at `Embedding Excalidraw into your Notes and Exporting > Export Settings > Auto-export Settings`) 3. Have the `xdg-open` command working or set a different command at `uriOpenCommand` in the [config](#configuration) -4. If you encounter issues try cloning the repo into `~/typstar` or setting the `typstarRoot` config accordingly, feel free to open an issue +4. If you encounter issues with the file creation of drawings, try cloning the repo into `~/typstar` or setting the `typstarRoot` config accordingly; feel free to open an issue + +### Rnote +1. Install [Rnote](https://github.com/flxzt/rnote?tab=readme-ov-file#installation); I recommend not using flatpak as that might cause issues with file permissions. +2. Make sure `rnote-cli` is available in your `PATH` or set a different command at `exportCommand` in the [config](#configuration) +3. Have the `xdg-open` command working with Rnote files or set a different command at `openCommand` in the [config](#configuration) +4. See comment 4 above at Excalidraw ### Anki 0. Typst version `0.12.0` or higher is required diff --git a/lua/typstar/config.lua b/lua/typstar/config.lua index c1380aa..d04d101 100644 --- a/lua/typstar/config.lua +++ b/lua/typstar/config.lua @@ -1,7 +1,7 @@ local M = {} local default_config = { - typstarRoot = nil, + typstarRoot = nil, -- typstar installation location required to use default drawing templates (usually determined automatically) anki = { typstarAnkiCmd = 'typstar-anki', typstCmd = 'typst', @@ -22,7 +22,7 @@ local default_config = { filename = 'drawing-%Y-%m-%d-%H-%M-%S', fileExtension = '.rnote', fileExtensionInserted = '.rnote.svg', -- valid rnote export type - uriOpenCommand = 'xdg-open', -- see comment above for excalidraw + openCommand = 'xdg-open', -- see comment above for excalidraw templatePath = {}, }, snippets = { diff --git a/lua/typstar/drawings.lua b/lua/typstar/drawings.lua index 05db403..701b303 100644 --- a/lua/typstar/drawings.lua +++ b/lua/typstar/drawings.lua @@ -42,7 +42,7 @@ end local function launch_rnote(path, path_inserted) print(string.format('Opening %s in Rnote', path)) - utils.run_shell_command(string.format('%s %s', config_rnote.uriOpenCommand, path), false) + utils.run_shell_command(string.format('%s %s', config_rnote.openCommand, path), false) auto_export_rnote(path, path_inserted) end From 60bacb22adefe490aebc04edaf2ac5e6ec77eb83 Mon Sep 17 00:00:00 2001 From: arne314 <73391160+arne314@users.noreply.github.com> Date: Sat, 2 Aug 2025 15:10:36 +0200 Subject: [PATCH 08/17] minor: fix wrong argument order --- lua/typstar/utils.lua | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/lua/typstar/utils.lua b/lua/typstar/utils.lua index 3fe2eea..0addcad 100644 --- a/lua/typstar/utils.lua +++ b/lua/typstar/utils.lua @@ -38,12 +38,15 @@ function M.run_shell_command(cmd, show_output, extra_handler, opts) end end if show_output then - return vim.fn.jobstart(cmd, vim.tbl_deep_extend({ - on_stdout = function(_, data, _) handle_output(data, false) end, - on_stderr = function(_, data, _) handle_output(data, true) end, - stdout_buffered = false, - stderr_buffered = true, - }, opts, "force")) + return vim.fn.jobstart( + cmd, + vim.tbl_deep_extend('force', { + on_stdout = function(_, data, _) handle_output(data, false) end, + on_stderr = function(_, data, _) handle_output(data, true) end, + stdout_buffered = false, + stderr_buffered = true, + }, opts) + ) else return vim.fn.jobstart(cmd, opts) end From d3fdae824bf711918d2aaeb533b604b6cdad7f65 Mon Sep 17 00:00:00 2001 From: arne314 <73391160+arne314@users.noreply.github.com> Date: Sat, 2 Aug 2025 16:48:48 +0200 Subject: [PATCH 09/17] feat(draw): manually choose program to open file --- README.md | 3 ++- lua/typstar/drawings.lua | 13 ++++++++----- lua/typstar/init.lua | 2 ++ 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 82e3e5f..9b5f58f 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,8 @@ Note that you can [customize](#custom-snippets) (enable, disable and modify) eve create a new drawing using the [configured](#configuration) template, insert a figure displaying it and open it in Obsidian/Rnote. - To open an inserted drawing in Obsidian/Rnote, - simply run `:TypstarOpenDrawing` while your cursor is on a line referencing the drawing. + simply run `:TypstarOpenDrawing` (or `:TypstarOpenExcalidraw`/`:TypstarOpenRnote` if you are using the same file extension for both) + while your cursor is on a line referencing the drawing. ### Anki Use the `flA` snippet to create a new flashcard diff --git a/lua/typstar/drawings.lua b/lua/typstar/drawings.lua index 701b303..cdb5555 100644 --- a/lua/typstar/drawings.lua +++ b/lua/typstar/drawings.lua @@ -85,11 +85,8 @@ local rnote = { } local providers = { excalidraw, rnote } -function M.insert_obsidian_excalidraw() insert_drawing(excalidraw) end -function M.insert_rnote() insert_drawing(rnote) end - -function M.open_drawing() - for _, provider in pairs(providers) do +local open_drawing = function(prov) + for _, provider in ipairs(prov) do local cfg = provider[1] local line = vim.api.nvim_get_current_line() local filename = line:match('"(.*)' .. string.gsub(cfg.fileExtensionInserted, '%.', '%%%.')) @@ -102,4 +99,10 @@ function M.open_drawing() end end +function M.insert_obsidian_excalidraw() insert_drawing(excalidraw) end +function M.insert_rnote() insert_drawing(rnote) end +function M.open_obsidian_excalidraw() open_drawing({ excalidraw }) end +function M.open_rnote() open_drawing({ rnote }) end +function M.open_drawing() open_drawing(providers) end + return M diff --git a/lua/typstar/init.lua b/lua/typstar/init.lua index 5be4f29..058dfdc 100644 --- a/lua/typstar/init.lua +++ b/lua/typstar/init.lua @@ -15,6 +15,8 @@ M.setup = function(args) vim.api.nvim_create_user_command('TypstarInsertExcalidraw', drawings.insert_obsidian_excalidraw, {}) vim.api.nvim_create_user_command('TypstarInsertRnote', drawings.insert_rnote, {}) + vim.api.nvim_create_user_command('TypstarOpenExcalidraw', drawings.open_obsidian_excalidraw, {}) + vim.api.nvim_create_user_command('TypstarOpenRnote', drawings.open_rnote, {}) vim.api.nvim_create_user_command('TypstarOpenDrawing', drawings.open_drawing, {}) vim.api.nvim_create_user_command('TypstarAnkiScan', anki.scan, {}) From c157b4dcf9a5b8baf52f04a3ec7fd3e13a15dd32 Mon Sep 17 00:00:00 2001 From: arne314 <73391160+arne314@users.noreply.github.com> Date: Sun, 3 Aug 2025 12:17:22 +0200 Subject: [PATCH 10/17] minor(draw): rnote export strokes not full page --- lua/typstar/config.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/typstar/config.lua b/lua/typstar/config.lua index d04d101..4e11e35 100644 --- a/lua/typstar/config.lua +++ b/lua/typstar/config.lua @@ -18,7 +18,7 @@ local default_config = { }, rnote = { assetsDir = 'assets', - exportCommand = 'rnote-cli export doc --no-background --no-pattern --on-conflict overwrite --output-file %s %s', + exportCommand = 'rnote-cli export selection --no-background --no-pattern --on-conflict overwrite --output-file %s all %s', -- can be modified to e.g. export full pages filename = 'drawing-%Y-%m-%d-%H-%M-%S', fileExtension = '.rnote', fileExtensionInserted = '.rnote.svg', -- valid rnote export type From 21336037b3167df6818738b705c7dfc3163d79fe Mon Sep 17 00:00:00 2001 From: arne314 <73391160+arne314@users.noreply.github.com> Date: Sun, 3 Aug 2025 12:19:24 +0200 Subject: [PATCH 11/17] docs(draw): templates --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index 9b5f58f..e1ae3af 100644 --- a/README.md +++ b/README.md @@ -205,6 +205,16 @@ with pkgs; [ ## Configuration Configuration options can be intuitively derived from the table [here](./lua/typstar/config.lua). +### Excalidraw/Rnote templates +The `templatePath` option expects a table that maps file patterns to template locations. +To for example have a specific template for lectures, you could configure it like this +```Lua +templatePath = { + { 'lectures/.*%.excalidraw%.md$', '~/Templates/lecture_excalidraw.excalidraw.md' }, -- path contains "lectures" + { '%.excalidraw%.md$', '~/Templates/default_excalidraw.excalidraw.md' }, -- fallback +}, +``` + ### Custom snippets The [config](#configuration) allows you to - disable all snippets via `snippets.enable = false` From 517d60b72a93e797f5d2ea000cb08dcc9ac65018 Mon Sep 17 00:00:00 2001 From: arne314 <73391160+arne314@users.noreply.github.com> Date: Sun, 3 Aug 2025 12:00:15 +0200 Subject: [PATCH 12/17] feat(snip)!: update latin greek map with reasons --- README.md | 11 ++++++++--- lua/typstar/snippets/letters.lua | 19 ++++++++++--------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 5488179..5a415a2 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,14 @@ To efficiently navigate insert nodes and avoid overlapping ones, use `:TypstarSmartJump` and `:TypstarSmartJumpBack`. Available snippets can mostly be intuitively derived from [here](././lua/typstar/snippets), they include: +Universal snippets: +- Alphanumeric characters: `:` → `$$ ` in markup (e.g. `:X` → `$X$ `, `:5` → `$5$ `) +- Greek letters: `;` → `` in math and `$$ ` in markup (e.g. `;a` → `alpha`/`$alpha$ `) +- Common indices (numbers and letters `i-n`): ` ` → `_ ` in math and `$$ ` → `$_$ ` in markup (e.g `A314 ` → `A_314 `, `$alpha$n ` → `$alpha_n$ `) + +You can find a complete map of latin to greek letters including reasons for the less intuitive ones [here](./lua/typstar/snippets/letters.lua). +Note that some greek letters have multiple latin ones mapped to them. + Markup snippets: - Begin inline math with `ll` and multiline math with `dm` - [Markup shorthands](./lua/typstar/snippets/markup.lua) (e.g. `HIG` → `#highlight[]`, `IMP` → `$==>$ `) @@ -23,9 +31,6 @@ Markup snippets: Math snippets: - [Many shorthands](./lua/typstar/snippets/math.lua) for mathematical expressions -- Alphanumeric characters: `:` → `$$ ` in markup (e.g. `:X` → `$X$ `, `:5` → `$5$ `) -- Greek letters: `;` → `` in math and `$$ ` in markup (e.g. `;a` → `alpha`/`$alpha$ `) -- Common indices (numbers and letters `i-n`): ` ` → `_ ` in math and `$$ ` → `$_$ ` in markup (e.g `A314 ` → `A_314 `, `$alpha$n ` → `$alpha_n$ `) - Series of numbered letters: ` ot ` → `_1, _2, ... ` (e.g. `a ot ` → `a_1, a_2, ... `, `a ot4 ` → `a_1, a_2, a_3, a_4 `, `alpha otk ` → `alpha_1, alpha_2, ..., alpha_k `) - Wrapping of any mathematical expression (see [operations](./lua/typstar/snippets/visual.lua), works nested, multiline and in visual mode via the [selection key](#installation)): `` → `()` (e.g. `(a^2+b^2)rt` → `sqrt(a^2+b^2)`, `lambdatd` → `tilde(lambda)`, `(1+1)sQ` → `[1+1]`, `(1+1)sq` → `[(1+1)]`) - Matrices: `ma` and `lma` (e.g. `23ma` → 2x3 matrix) diff --git a/lua/typstar/snippets/letters.lua b/lua/typstar/snippets/letters.lua index 8728d81..7eaed12 100644 --- a/lua/typstar/snippets/letters.lua +++ b/lua/typstar/snippets/letters.lua @@ -16,31 +16,32 @@ local greek_letters_map = { ['c'] = 'chi', ['d'] = 'delta', ['e'] = 'epsilon', - ['f'] = 'phi', + ['f'] = 'phi', -- sound ['g'] = 'gamma', - ['h'] = 'eta', + ['h'] = 'eta', -- look ['i'] = 'iota', - ['j'] = 'theta', ['k'] = 'kappa', ['l'] = 'lambda', ['m'] = 'mu', ['n'] = 'nu', - ['o'] = 'omega', + ['o'] = 'omikron', ['p'] = 'psi', - ['q'] = 'eta', + ['q'] = 'theta', -- look? ['r'] = 'rho', ['s'] = 'sigma', ['t'] = 'tau', - ['v'] = 'nu', - ['w'] = 'omega', + ['u'] = 'upsilon', + ['v'] = 'nu', -- look + ['w'] = 'omega', -- look ['x'] = 'xi', - ['y'] = 'upsilon', + ['y'] = 'upsilon', -- look ['z'] = 'zeta', } + local greek_keys = {} local greek_letters_set = {} local common_indices = { '\\d+', '[i-n]' } --- buitins and caligraphic letters from github.com/lentilus/readable-typst +-- builtins and calligraphic letters from github.com/lentilus/typst-scribe local index_conflicts = { 'Im', 'in', 'ln', 'Pi', 'pi', 'Xi', 'xi', 'Ii', 'Jj', 'Kk', 'Ll', 'Mm', 'Nn' } local index_conflicts_set = {} local punctuation_prepend_space = { ',', ';' } From b9a9512ee970d3cfadda3a7924687088bf33f513 Mon Sep 17 00:00:00 2001 From: arne314 <73391160+arne314@users.noreply.github.com> Date: Sun, 3 Aug 2025 12:13:00 +0200 Subject: [PATCH 13/17] minor(snip)!: require space after simple functions --- README.md | 1 + lua/typstar/snippets/math.lua | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5a415a2..7d03ca9 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ Math snippets: - [Many shorthands](./lua/typstar/snippets/math.lua) for mathematical expressions - Series of numbered letters: ` ot ` → `_1, _2, ... ` (e.g. `a ot ` → `a_1, a_2, ... `, `a ot4 ` → `a_1, a_2, a_3, a_4 `, `alpha otk ` → `alpha_1, alpha_2, ..., alpha_k `) - Wrapping of any mathematical expression (see [operations](./lua/typstar/snippets/visual.lua), works nested, multiline and in visual mode via the [selection key](#installation)): `` → `()` (e.g. `(a^2+b^2)rt` → `sqrt(a^2+b^2)`, `lambdatd` → `tilde(lambda)`, `(1+1)sQ` → `[1+1]`, `(1+1)sq` → `[(1+1)]`) +- Simple functions: `fo ` → `f() ` (e.g. `fox ` → `f(x) `, `ao5 ` → `a(5) `) - Matrices: `ma` and `lma` (e.g. `23ma` → 2x3 matrix) Note that you can [customize](#custom-snippets) (enable, disable and modify) every snippet. diff --git a/lua/typstar/snippets/math.lua b/lua/typstar/snippets/math.lua index c885cc5..5799f07 100644 --- a/lua/typstar/snippets/math.lua +++ b/lua/typstar/snippets/math.lua @@ -73,9 +73,9 @@ return { snip('Oo', 'compose ', {}, math), snip('iso', 'tilde.equiv ', {}, math), snip('cc', 'cases(\n\t<>\n)\\', { i(1, '1') }, math), - snip('([A-Za-z])o([A-Za-z0-9])', '<>(<>) ', { cap(1), cap(2) }, math, 100, { - maxTrigLength = 3, - blacklist = { 'bot', 'col', 'com', 'con', 'cos', 'cot', 'dol', 'dot', 'log', 'loz', 'mod', 'roo', 'top', 'won', 'xor' }, + snip('([A-Za-z])o([A-Za-z0-9]) ', '<>(<>) ', { cap(1), cap(2) }, math, 100, { + maxTrigLength = 4, + blacklist = { 'bot ', 'cos ', 'cot ', 'dot ', 'log ', 'mod ', 'top ', 'won ', 'xor ' }, }), snip('(K|M|N|Q|R|S|Z)([\\dn]) ', '<><>^<> ', { cap(1), cap(1), cap(2) }, math), From 64523e2db7094e66b410168669da6c157cf46dad Mon Sep 17 00:00:00 2001 From: arne314 <73391160+arne314@users.noreply.github.com> Date: Mon, 4 Aug 2025 13:03:08 +0200 Subject: [PATCH 14/17] chore: remove .idea dir --- .idea/.gitignore | 3 --- .idea/inspectionProfiles/profiles_settings.xml | 6 ------ .idea/misc.xml | 7 ------- .idea/modules.xml | 8 -------- .idea/typstar.iml | 10 ---------- .idea/vcs.xml | 6 ------ 6 files changed, 40 deletions(-) delete mode 100644 .idea/.gitignore delete mode 100644 .idea/inspectionProfiles/profiles_settings.xml delete mode 100644 .idea/misc.xml delete mode 100644 .idea/modules.xml delete mode 100644 .idea/typstar.iml delete mode 100644 .idea/vcs.xml diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 26d3352..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml deleted file mode 100644 index 105ce2d..0000000 --- a/.idea/inspectionProfiles/profiles_settings.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index b218c30..0000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index 688e9bc..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/typstar.iml b/.idea/typstar.iml deleted file mode 100644 index 13f2b0f..0000000 --- a/.idea/typstar.iml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 35eb1dd..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file From a76daa88a9745a2ae93ee672ebe3dd3ca328d09c Mon Sep 17 00:00:00 2001 From: arne314 <73391160+arne314@users.noreply.github.com> Date: Mon, 4 Aug 2025 13:07:11 +0200 Subject: [PATCH 15/17] minor(draw): rename rnote open command config --- README.md | 2 +- lua/typstar/config.lua | 2 +- lua/typstar/drawings.lua | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index cf1de6e..294eea4 100644 --- a/README.md +++ b/README.md @@ -171,7 +171,7 @@ require('typstar').setup({ -- depending on your neovim plugin system ### Rnote 1. Install [Rnote](https://github.com/flxzt/rnote?tab=readme-ov-file#installation); I recommend not using flatpak as that might cause issues with file permissions. 2. Make sure `rnote-cli` is available in your `PATH` or set a different command at `exportCommand` in the [config](#configuration) -3. Have the `xdg-open` command working with Rnote files or set a different command at `openCommand` in the [config](#configuration) +3. Have the `xdg-open` command working with Rnote files or set a different command at `uriOpenCommand` in the [config](#configuration) 4. See comment 4 above at Excalidraw ### Anki diff --git a/lua/typstar/config.lua b/lua/typstar/config.lua index 4e11e35..60ca39e 100644 --- a/lua/typstar/config.lua +++ b/lua/typstar/config.lua @@ -22,7 +22,7 @@ local default_config = { filename = 'drawing-%Y-%m-%d-%H-%M-%S', fileExtension = '.rnote', fileExtensionInserted = '.rnote.svg', -- valid rnote export type - openCommand = 'xdg-open', -- see comment above for excalidraw + uriOpenCommand = 'xdg-open', -- see comment above for excalidraw templatePath = {}, }, snippets = { diff --git a/lua/typstar/drawings.lua b/lua/typstar/drawings.lua index cdb5555..bfcb56f 100644 --- a/lua/typstar/drawings.lua +++ b/lua/typstar/drawings.lua @@ -42,7 +42,7 @@ end local function launch_rnote(path, path_inserted) print(string.format('Opening %s in Rnote', path)) - utils.run_shell_command(string.format('%s %s', config_rnote.openCommand, path), false) + utils.run_shell_command(string.format('%s %s', config_rnote.uriOpenCommand, path), false) auto_export_rnote(path, path_inserted) end From 5231bc66f32c883aa0148768df5e2c14c9352dce Mon Sep 17 00:00:00 2001 From: arne314 <73391160+arne314@users.noreply.github.com> Date: Mon, 4 Aug 2025 13:04:26 +0200 Subject: [PATCH 16/17] chore: bump version to `1.4.0` --- pyproject.toml | 2 +- uv.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 067f1f9..83ed06d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "pdm.backend" [project] name = "typstar" -version = "1.3.4" +version = "1.4.0" description = "Neovim plugin for efficient note taking in Typst" authors = [ { name = "arne314" } diff --git a/uv.lock b/uv.lock index 5a54c4a..fc49898 100644 --- a/uv.lock +++ b/uv.lock @@ -438,7 +438,7 @@ wheels = [ [[package]] name = "typstar" -version = "1.3.4" +version = "1.4.0" source = { editable = "." } dependencies = [ { name = "aiohttp" }, From 00f484ce15409a47f434339d07cb4597c96a5ae2 Mon Sep 17 00:00:00 2001 From: arne314 <73391160+arne314@users.noreply.github.com> Date: Mon, 4 Aug 2025 17:11:19 +0200 Subject: [PATCH 17/17] fix(draw): handle empty documents --- lua/typstar/config.lua | 3 ++- lua/typstar/drawings.lua | 29 +++++++++++++++++------------ 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/lua/typstar/config.lua b/lua/typstar/config.lua index 60ca39e..0b283bf 100644 --- a/lua/typstar/config.lua +++ b/lua/typstar/config.lua @@ -18,7 +18,8 @@ local default_config = { }, rnote = { assetsDir = 'assets', - exportCommand = 'rnote-cli export selection --no-background --no-pattern --on-conflict overwrite --output-file %s all %s', -- can be modified to e.g. export full pages + -- can be modified to e.g. export full pages; default is to try to export strokes only and otherwise export the entire document + exportCommand = 'rnote-cli export selection --no-background --no-pattern --on-conflict overwrite --output-file %s all %s || rnote-cli export doc --no-background --no-pattern --on-conflict overwrite --output-file %s %s', filename = 'drawing-%Y-%m-%d-%H-%M-%S', fileExtension = '.rnote', fileExtensionInserted = '.rnote.svg', -- valid rnote export type diff --git a/lua/typstar/drawings.lua b/lua/typstar/drawings.lua index bfcb56f..8825b86 100644 --- a/lua/typstar/drawings.lua +++ b/lua/typstar/drawings.lua @@ -10,7 +10,7 @@ local affix = [[ local config_excalidraw = config.config.excalidraw local config_rnote = config.config.rnote -local function launch_excalidraw(path, path_inserted) +local function launch_excalidraw(path, path_export) print(string.format('Opening %s in Obsidian Excalidraw', path)) utils.run_shell_command( string.format('%s "obsidian://open?path=%s"', config_excalidraw.uriOpenCommand, utils.urlencode(path)), @@ -20,7 +20,7 @@ end local rnote_watched = {} -local function auto_export_rnote(path, path_inserted) +local function auto_export_rnote(path, path_export) if rnote_watched[path] then return end rnote_watched[path] = true local job_id = -1 @@ -32,7 +32,7 @@ local function auto_export_rnote(path, path_inserted) if job_id == -1 then last_export = time - local cmd = string.format(config_rnote.exportCommand, path_inserted, path) + local cmd = string.format(config_rnote.exportCommand, path_export, path, path_export, path) job_id = utils.run_shell_command(cmd, false, nil, { on_exit = function() job_id = -1 end }) end end @@ -40,10 +40,10 @@ local function auto_export_rnote(path, path_inserted) watcher:start(path, {}, vim.schedule_wrap(run_export)) end -local function launch_rnote(path, path_inserted) +local function launch_rnote(path, path_export) print(string.format('Opening %s in Rnote', path)) utils.run_shell_command(string.format('%s %s', config_rnote.uriOpenCommand, path), false) - auto_export_rnote(path, path_inserted) + auto_export_rnote(path, path_export) end local function insert_drawing(provider) @@ -51,7 +51,8 @@ local function insert_drawing(provider) local assets_dir = vim.fn.expand('%:p:h') .. '/' .. cfg.assetsDir local filename = os.date(cfg.filename) local path = assets_dir .. '/' .. filename .. cfg.fileExtension - local path_inserted = cfg.assetsDir .. '/' .. filename .. cfg.fileExtensionInserted + local path_export = assets_dir .. '/' .. filename .. cfg.fileExtensionInserted + local path_insert = cfg.assetsDir .. '/' .. filename .. cfg.fileExtensionInserted -- local relative path if vim.fn.isdirectory(assets_dir) == 0 then vim.fn.mkdir(assets_dir, 'p') end local found_match = false @@ -60,7 +61,14 @@ local function insert_drawing(provider) local template_path = template_config[2] if string.match(path, pattern) then found_match = true - utils.run_shell_command(string.format('cat %s > %s', template_path, path), false) -- don't copy file metadata + -- use cat as we don't want to copy file metadata + utils.run_shell_command(string.format('cat %s > %s', template_path, path), false, nil, { + on_exit = function() + -- insert text and launch program + utils.insert_text_block(string.format(provider[2], path_insert)) + provider[3](path, path_export) + end, + }) break end end @@ -68,9 +76,6 @@ local function insert_drawing(provider) print('No matching template found for path: ' .. path) return end - - utils.insert_text_block(string.format(provider[2], path_inserted)) - provider[3](path, path_inserted) end local excalidraw = { @@ -92,8 +97,8 @@ local open_drawing = function(prov) local filename = line:match('"(.*)' .. string.gsub(cfg.fileExtensionInserted, '%.', '%%%.')) if filename ~= nil and filename:match('^%s*$') == nil then local path = vim.fn.expand('%:p:h') .. '/' .. filename .. cfg.fileExtension - local path_inserted = vim.fn.expand('%:p:h') .. '/' .. filename .. cfg.fileExtensionInserted - provider[3](path, path_inserted) -- launch program + local path_export = vim.fn.expand('%:p:h') .. '/' .. filename .. cfg.fileExtensionInserted + provider[3](path, path_export) -- launch program break end end