mjolnirapp / mjolnir Goto Github PK
View Code? Open in Web Editor NEWLightweight automation and productivity app for OS X
Lightweight automation and productivity app for OS X
It's not exactly clear what "live long" does, nor "protect" actually. But basically it's protecting it from being automatically closed when you reload your config. (They're all typically automatically closed as a way of "resetting state", along with timers, hotkeys, etc.)
Too many ways to alert!
Right now it only falls back to default config when there's no config. It should also fall back in case of any error, via the default error handler. (Our custom error handler function should also have its own secondary fallback in case the default error handler has its own user-error.) This depends on #13.
It's not great right now.
First of all, to make things simpler, it should always calculate the "closest window" in a single direction. To focus windows in other directions, their coordinates should just be rotated around the focused window, to align them to the canonical direction (for some reason, I've been assuming this is "east"). This is the easy part, but it should make the code a lot cleaner and easier to work with.
The harder part is finding which window is actually closer. Right now, we just compare midpoints, completely abandoning a window's width and height, which are actually pretty important. Imagine two windows with nearly the same midpoint, but one is much wider and it should be focused, but in fact the other one is focused instead because its midpoint is a pixel closer. I'm not sure how to solve this. Geometry and math aren't my strong-point.
A simple version has been written. It should be rewritten in terms of api.textgrid
and thus depends on #16.
api.hotkeys.keys
and remove them from that tableI found this framework bundle that might be a more idomatic way to ship it.
There should be a global hydra
already pre-loaded, with fields window
for the window submodule, app
for app submodule, etc. So you could do hydra.window.focusedwindow()
to get the focused window, without even touching a single require.
It would be really cool to have a "documentation"-specific textgrid at some point, which you could scroll and click around with the mouse, and drag-to-highlight some text so you could copy/paste it. Probably too big of a feature for 1.0 though.
Probably use the NSFileHandle class methods to replace stdout
. Then log it to an array at api.stdout
maybe? There's no need for stdin in this app, but stdout is useful for using print
and maybe showing errors in the future in a new textgrid that just prints stdout. We should probably also add an event-handler for "received stdout".
Make sure the full explanation is accessible from within the doc system! Also explain that third party libs can use the doc system just like the built-in lib does.
Add api.ext = {}
to the beginning of rawinit.lua
. This allows third-party authors to put their extensions into api.ext.whatever
without worrying whether it already exists. Document it too.
Making this issue as a placeholder where people can see the status of this project.
I think it'd be nice to have an API function that lets the user create a new borderless window containing only a webview containing only the raw HTML they pass into it via the API. Then the user could position/hide/show this window at will. This would not be a directly useful feature, but it would enable users to make new features such as window hints.
The way I'm envisioning it, the user would do something like this:
var text = api.readfile("~/.hydra/windowhint.html").format("this will replace the first %s", "this will replace the second %s, etc")
var opts = {x: 0, y: 0, w: 100, h: 100, text: text, ...}
var window = api.openwindow(opts)
// move the window around, etc
We can use -[NSWindow setIgnoresMouseEvents:], and you can pass us the value as part of the options. Maybe it should be called "ignoresMouse". And if you choose to have it not ignore the mouse, there are /some/ tricky things we can do to allow clicks within the window to trigger some function you give it. But that will be trickier.
We can also have options (I think) to disallow the window from becoming the key window, even if it does allow mouse clicks, which will allow it to be more transient.
Probably the simplest way to allow interaction in the web view would be to pass an object whose keys are names (i.e. "action1") that point to function, and you'd pass this in as the "actions" key on opts, and then you can have your JS code be like: onclick="action1()".
As for actually passing data to Hydra from that web view, that sounds probably doable also, but probably annoying with all the conversions and stuff.
It's really lame. It shouldn't quit, that's super annoying. Instead lets have it push a notification which, when you click it, opens up the panel.
I just found some code the other day that lets us capture every single key-event and override it.. not just handle it but literally switch it out. The sample code I found makes it so when you press "a" you really press "z", and vice versa. This would be an incredibly cool API to use. It could potentially also obsolete the existing hotkey API, but there may be side-effects or caveats I'm not aware of, so this should probably just be a new separate API for now.
I can't find the original site that I found it on, and the one original source that looks legit seems to be down now, but here's the code (I think):
// alterkeys.c
// http://osxbook.com
//
// Complile using the following command line:
// gcc -Wall -o alterkeys alterkeys.c -framework ApplicationServices
//
// You need superuser privileges to create the event tap, unless accessibility
// is enabled. To do so, select the "Enable access for assistive devices"
// checkbox in the Universal Access system preference pane.
#include <ApplicationServices/ApplicationServices.h>
// This callback will be invoked every time there is a keystroke.
//
CGEventRef
myCGEventCallback(CGEventTapProxy proxy, CGEventType type,
CGEventRef event, void *refcon)
{
// Paranoid sanity check.
if ((type != kCGEventKeyDown) && (type != kCGEventKeyUp))
return event;
// The incoming keycode.
CGKeyCode keycode = (CGKeyCode)CGEventGetIntegerValueField(
event, kCGKeyboardEventKeycode);
// Swap 'a' (keycode=0) and 'z' (keycode=6).
if (keycode == (CGKeyCode)0)
keycode = (CGKeyCode)6;
else if (keycode == (CGKeyCode)6)
keycode = (CGKeyCode)0;
// Set the modified keycode field in the event.
CGEventSetIntegerValueField(
event, kCGKeyboardEventKeycode, (int64_t)keycode);
// We must return the event for it to be useful.
return event;
}
int
main(void)
{
CFMachPortRef eventTap;
CGEventMask eventMask;
CFRunLoopSourceRef runLoopSource;
// Create an event tap. We are interested in key presses.
eventMask = ((1 << kCGEventKeyDown) | (1 << kCGEventKeyUp));
eventTap = CGEventTapCreate(kCGSessionEventTap, kCGHeadInsertEventTap, 0,
eventMask, myCGEventCallback, NULL);
if (!eventTap) {
fprintf(stderr, "failed to create event tap\n");
exit(1);
}
// Create a run loop source.
runLoopSource = CFMachPortCreateRunLoopSource(
kCFAllocatorDefault, eventTap, 0);
// Add to the current run loop.
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource,
kCFRunLoopCommonModes);
// Enable the event tap.
CGEventTapEnable(eventTap, true);
// Set it all running.
CFRunLoopRun();
// In a real program, one would have arranged for cleaning up.
exit(0);
}
There are a few oddities. The only one I can remember right now is that there are (at least) 2 different ways of registering callbacks. This should probably be standardized.
And let the users tap into it to override what happens when an error happens.
This way, people can load third party libraries which are essentially immutable, taking advantage of the normal caching behavior of require
.
Is there a reason for the config file path to be ~/.hydra/init.lua
instead of just ~/.hydra.lua
?
api.uuid()
returns a string
Sometimes you want them to be focused (i.e. Hydra to be made active) but sometimes you probably don't.
If it's hard as it is, then api.log.gotline
should be made easier to use. I can't remember what's hard about it, something about object lifespan or something. But I remember thinking earlier "hmm this is gonna be tricky, something about object lifespan or something" and I trust that memory, so I believe it.
Rather than checking them into the repo, perhaps use Github's Builds hosting.
There's a program called github-release that handles this. It even dogfoods itself, releasing using itself.
Ability to launch (or focus) app; get app by bundle-id or pid; get bundle-id.
Basically just import github.com/sdegutis/chaos as an API. Let's call it api.textgrid
.
Used like this:
timer.once(function()
print("hello world")
end)
It will shell out to dispatch_once. The purpose is to make it easy to have something in your config only run once at the beginning, never after you reload. For example, I just made a pathwatcher that reloads my script, and it grows exponentially at each reload.
There's a name for this thing, but I can't remember it. It's like a nonce or something?
It's awkward and inconsistent right now. Plus, the new style works better with our dynamic documentation system.
api.doc(api.hotkey)
should print a documentation string. It would be most helpful in the REPL. (Depends in #12). With no args, it should print the full list of API functions. With a submodule, it should print the sublist of API functions, and perhaps a help string for that specific submodule.
Now's the time to make breaking changes. The goal of this first release is to have a stable API so we don't break backwards compatibility (except in trivial ways) in future releases.
api.require
to make its intention more self-evidentapi.alert.show()
, keep api.alert()
I found at least one.
Specifically so they can unregister for log events.
So you could do:
api.timer(api.timer.days(5) + api.timer.minutes(5), api.update.check)
Or something.
More idiomatic for OSX users, IMO.
Should probably be api.openlog
.
Also, should we namespace all the stdout stuff? Right now we would have this:
api.stdout = {}
api.maxstdoutlen = 500
api.receivedstdout(str)
api.openlog()
Maybe it should be this instead:
api.log.lines = {}
api.log.maxlines = 500
api.log.gotline(str)
api.log.show()
(Although I'm not sure why we'd need this if we have all the docs inside the app.)
return
before statement; fallback to without it if that failsQuestion: What constitutes word-boundaries? We can't just delegate to Lua's regex word-boundary option, since it has none. Answer: for now let's just say "alphanumeric and underscore characters".
It should either be named api.doc(...)
or just doc(...)
. Preferences? I've tried to stay away from globals (besides "api"), but this one might be worth the convenience.
Fix clear_old_state
and the registration functions themselves. Pathwatchers, timers, and textgrids are almost definitely broken, since the length operator is undefined for nonsequential tables, and the unregisterers for these things just pokes holes right in the middle of their respective tables. Terrible! So let's just do the "n" trick that api.notify.registry
uses.
It's weird that print(3)
doesn't show anything in the REPL itself, only in the log window. Make it show directly in the REPL window too.
Known caveats:
It should tap into the same thing that App Store does to show you there's an update. I forget what it's called but I've used it before and it's dead-simple. We could probably get really fancy with making this API really flexible, but I just want a bare-basics one for now that helps us to close #2 (which would be obnoxious if it used an alert to let you know about available updates).
It just feels so backwards right now.
And maybe instead of doc(doc.api.alert)
it should just be doc('api.alert')
. I dismissed this idea when I first had it because the table idea seemed so cool, but the extra "doc" is redundant, and I refuse to do any more Lua magic. (I've used Ruby for far too long.)
Basically setInterval
and setTimeout
:
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.