Skip to content

Instantly share code, notes, and snippets.

@kiryph
Last active January 27, 2022 16:21
Show Gist options
  • Save kiryph/bcbab4a710bfe4328f0eeb164d3b1c8e to your computer and use it in GitHub Desktop.
Save kiryph/bcbab4a710bfe4328f0eeb164d3b1c8e to your computer and use it in GitHub Desktop.
Key chords for essential shape & line styles in Inkscape using Karabiner & Hammerspoon on macOS (inspired by Gilles Castel 2019)
-- Add this to your ~/.hammerspoon/init.lua
-- based on https://castel.dev/post/lecture-notes-2/ and
-- https://github.com/gillescastel/inkscape-shortcut-manager
-- Make cli command `hs` available:
-- After an update of hammerspoon run once following two commmands
-- hs.ipc.cliUninstall(); hs.ipc.cliInstall()
require("hs.ipc")
local function intersect(m,n)
local r={}
for i,v1 in ipairs(m) do
for k,v2 in pairs(n) do
if (v1==v2) then
return true
end
end
end
return false
end
local function has_value (tab, val)
for index, value in ipairs(tab) do
if value == val then
return true
end
end
return false
end
function create_svg_and_paste(keys)
print(hs.inspect.inspect(keys))
pt = 1.327 -- pixels
w = 1 * pt
thick_width = 4 * pt
very_thick_width = 8 * pt
style = {}
style["stroke-opacity"] = 1
if intersect({"s", "a", "d", "g", "h", "x", "e"}, keys)
then
style["stroke"] = "black"
style["stroke-width"] = w
style["marker-end"] = "none"
style["marker-start"] = "none"
style["stroke-dasharray"] = "none"
else
style["stroke"] = "none"
end
if has_value(keys, "g")
then
w = thick_width
style["stroke-width"] = w
end
if has_value(keys, "v")
then
w = very_thick_width
style["stroke-width"] = w
end
if has_value(keys, "a")
then
style['marker-end'] = 'url(#marker-arrow-' .. tostring(w) .. ')'
end
if has_value(keys, "x")
then
style['marker-start'] = 'url(#marker-arrow-' .. tostring(w) .. ')'
style['marker-end'] = 'url(#marker-arrow-' .. tostring(w) .. ')'
end
if has_value(keys, "d")
then
style['stroke-dasharray'] = tostring(w) .. ',' .. tostring(2*pt)
end
if has_value(keys, "e")
then
style['stroke-dasharray'] = tostring(3*pt) .. ',' .. tostring(3*pt)
end
if has_value(keys, "f")
then
style['fill'] = 'black'
style['fill-opacity'] = 0.12
end
if has_value(keys, "b")
then
style['fill'] = 'black'
style['fill-opacity'] = 1
end
if has_value(keys, "w")
then
style['fill'] = 'white'
style['fill-opacity'] = 1
end
if intersect(keys, {"f", "b", "w"})
then
style['marker-end'] = 'none'
style['marker-start'] = 'none'
end
if not intersect(keys, {"f", "b", "w"})
then
style['fill'] = 'none'
style['fill-opacity'] = 1
end
svg = [[
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg>
]]
print(hs.inspect.inspect(style)) -- ENABLE ONLY FOR DEBUGGING
if (style['marker-end'] ~= nil and style['marker-end'] ~= 'none') or
(style['marker-start'] ~= nil and style['marker-start'] ~= 'none')
then
svgtemp = [[
<defs id="marker-defs">
<marker
]]
svgtemp = svgtemp .. 'id="marker-arrow-' .. tostring(w) .. "\"\n"
svgtemp = svgtemp .. 'orient="auto-start-reverse"' .. "\n"
svgtemp = svgtemp .. 'refY="0" refX="0"' .. "\n"
svgtemp = svgtemp .. 'markerHeight="3" markerWidth="2">' .. "\n"
svgtemp = svgtemp .. ' <g transform="scale('.. tostring((2.40 * w + 3.87)/(4.5*w)) .. ')">' .. "\n"
svg = svg .. svgtemp
svgtemp = [[
<path
d="M -1.55415,2.0722 C -1.42464,1.29512 0,0.1295 0.38852,0 0,-0.1295 -1.42464,-1.29512 -1.55415,-2.0722"
style="fill:none;stroke:#000000;stroke-width:{0.6};stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1
inkscape:connector-curvature="0" />
</g>
</marker>
</defs>
]]
svg = svg .. svgtemp
end
style_string = ''
for key, value in pairs(style) do
-- skips nil?
style_string = style_string .. key .. ":" .. " " .. value .. ";"
end
svg = svg .. '<inkscape:clipboard style="' .. style_string .. '" />' .. "\n</svg>"
print(svg) -- ENABLE ONLY FOR DEBUGGING
hs.pasteboard.writeDataForUTI("dyn.ah62d4rv4gu80w5pbq7ww88brrf1g065dqf2gnppxs3xu", svg)
-- get UTI via https://github.com/sindresorhus/Pasteboard-Viewer
hs.eventtap.keyStroke({"shift", "cmd"}, "v")
end
// 1. $ jsonnet karabiner-inkscape.jsonnet > ~/.config/karabiner/assets/complex_modifications/karabiner-inkscape.json
// 2. Enable in Karabiner Elements UI the complex modifications
local arr = [
[x, y, z] // 24x
for x in ['s', 'd', 'e'] // solid, dotted, dashed
for y in ['f', 'w', 'a', 'x'] // gray, white, arrow, double-arrow
for z in ['g', 'v'] // thick, very thick
] +
[
[x, y] // 18x
for x in ['s', 'd', 'e'] // solid, dotted, dashed
for y in ['f', 'w', 'g', 'v', 'a', 'x']
] +
[
['spacebar', x] // 6x
for x in ['s', 'd', 'e', 'f', 'b', 'w'] // solid, dotted, dashed, gray, black, white
];
{
title: 'Apply quickly essential shape or line styles in Inkscape using hammerspoon (Gilles Castel, 2019)',
rules: [
{
description: 'Apply quickly essential shape or line styles in Inkscape using hammerspoon (Gilles Castel, 2019)',
manipulators: [
{
local str = std.join('+', el),
type: 'basic',
from: {
simultaneous: [{ key_code: i } for i in el],
simultaneous_options: {
key_up_when: 'all',
},
},
to: [{ shell_command: "/usr/local/bin/hs -c 'hs.alert(\"" + str + '");create_svg_and_paste({"' + std.join('","', el) + "\"});'" }],
conditions: [{ type: 'frontmost_application_if', bundle_identifiers: ['org.inkscape.Inkscape'] }],
}
for el in arr
],
},
],
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment