[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[nongnu] elpa/dslide 1123a4ecbe 11/21: Keyboard MACRO ACTION
From: |
ELPA Syncer |
Subject: |
[nongnu] elpa/dslide 1123a4ecbe 11/21: Keyboard MACRO ACTION |
Date: |
Tue, 17 Dec 2024 13:00:53 -0500 (EST) |
branch: elpa/dslide
commit 1123a4ecbe93d75816faa3ba9539d6aef4ed7ca3
Author: Psionik K <73710933+psionic-k@users.noreply.github.com>
Commit: Psionik K <73710933+psionic-k@users.noreply.github.com>
Keyboard MACRO ACTION
---
NEWS.org | 2 +
doc/README.org | 1 +
doc/manual.org | 41 +++++++++++-----
dslide.el | 149 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--
test/demo.org | 29 +++++++++++
5 files changed, 208 insertions(+), 14 deletions(-)
diff --git a/NEWS.org b/NEWS.org
index 014c6be377..7b18247e42 100644
--- a/NEWS.org
+++ b/NEWS.org
@@ -12,7 +12,9 @@
#+export_file_name: RELEASE.md
* v0.6.0 Fighting Spam ๐ :latest:
+- New actions (*KMACROS!*) fit a more clear pattern
** Added โ
+- ๐งฌ Experimental new kmacro action can run keyboard kmacros to script "live
demonstrations". Describe ~dslide-action-kmacro~ to view the documentation.
There is a demo in the usual [] file. All related functions and variables are
under the =dslide-action-kmacro= or =dslide-kmacro= prefixes.
- Babel blocks, which will now all be executed by default, respect the =:eval=
parameter. Values such as =never= or =never-export= will skip the block.
Other values are equivalent to =yes=. Values like =query= do *not* ask yet.
Expect that in 0.7.0. ๐ง
#+begin_src org
,#+begin_src elisp :eval never
diff --git a/doc/README.org b/doc/README.org
index e104cf3c0d..2272d610c1 100644
--- a/doc/README.org
+++ b/doc/README.org
@@ -19,6 +19,7 @@
https://github.com/positron-solutions/dslide/assets/73710933/06a66e42-a172-48ba-
** Programmable Org Presentation ๐ฆ
- Per-element configurable behavior through extensible actions
- Script steps in your presentation with *Org babel blocks*
+- Incorporate anything Emacs does with *keyboard macro playback*
- Convenient API for quickly writing reliable custom actions for reuse
- Decent out-of-the-box results with existing org documents
*** Status ๐น
diff --git a/doc/manual.org b/doc/manual.org
index 4794e6ed20..f17c1dc405 100644
--- a/doc/manual.org
+++ b/doc/manual.org
@@ -27,7 +27,7 @@ Dslide is conducts presentations in Emacs and can utilize
anything and everythin
What dslide primarily adds to Emacs and Org Mode:
- Simplified user interface suitable for a presentation remote controller
-- Data model that adapts Emacs to this interface
+- Data model that adapts Emacs to this interface, including Org babel and
keyboard macros
- Method of attaching reusable, configurable programmed behavior to org mode
content
- Extensible framework for creating custom programmed behavior
- Presenter tools such as viewing hidden content invisible to the audience
@@ -39,8 +39,10 @@ Granular configurability was the first goal. Dslide's
predecessor, ~org-tree-sl
Programmability quickly became primary motivation to develop dslide further.
Org babel blocks can be used as steps of a dslide presentation. The action
system is a framework for attaching reusable, configurable programmed behavior
to multiple org elements.
-High productivity was another goal. With a decent org configuration, use
basic markup to obtain a decent presentation. Org mode's properties and
keywords are used to attach and configure reusable behavior to elements and
headings. Dslide respects export settings, allowing content to vary between
presentation and export. You can use the presentation org document itself the
same way you use other org documents, to store, organize, and publish
information.
-** Strengths
+If Emacs does it, dslide can present it. As Emacs can use all of your
programs and programming tools, so can dslide. This includes both scripting
through Elisp and command-based keyboard macros. Dslide aims to integrate with
other buffers and Emacs automation seemlessly.
+
+High productivity was another goal. By respecting export settings and
cleaning up markup, dslide aims to re-use the same documents you already use
for storing, organizing, and publishing information. Dslide adds a bit of
flair so that, with a decent org configuration, you get a decent result with
little extra effort.
+** Strengths ๐ฆพ
:PROPERTIES:
:DESCRIPTION: When Dslide is Perfect
:END:
@@ -102,6 +104,7 @@ Select org mode terms more frequently used by dslide.
Don't miss [[info:org#Org
Documents should "just work" and obtain decent results.
- Add behavior to elements by enabling and configuring [[Actions ๐ช][actions]]
+- Add custom behavior with babel blocks and keyboard macros
- Create custom actions to use different kinds of data in reusable ways
** Actions ๐ช
:PROPERTIES:
@@ -120,6 +123,7 @@ To browse all actions, because they are all EIEIO classes,
you can use ~eieio-br
+--dslide-action-propertize
+--dslide-action-image
+--dslide-action-babel
+ +--dslide-action-kmacro
+--dslide-action-item-reveal
+--dslide-action-hide-markup
+--dslide-slide-action
@@ -147,13 +151,13 @@ Some actions, such as ~dslide-action-propertize~, can't
decide which elements to
๐ง This is the preferred style of configuration moving forward.
โน๏ธ Affiliated keywords /must/ have the =attr= prefix or they will not apply to
the content they precede. Affiliated keywords cannot be attached to headings,
which must use their property drawer to attach data.
+*** Keyword
+:PROPERTIES:
+:DESCRIPTION: The markup is the content
+:END:
+For some actions, the configuration /is/ the content. They use a keyword.
The value of the keyword is the content for the action. As elsewhere, use
plist =:key value= style configuration.
#+begin_src org
- ,* Full Screen Images
- :PROPERTIES:
- :DSLIDE_ACTIONS: dslide-action-images
- :END:
- ,#+attr_html: :width 50%
- [[./images/emacsen4.jpeg]] [[./images/before-google3.jpeg]]
+ ,#+dslide_kmacro: :keys [134217848 97 110]
#+end_src
*** Property Drawer
:PROPERTIES:
@@ -205,8 +209,12 @@ Configuring is usually done by adding plist-style =:key
value= arguments after t
:DSLIDE_ACTIONS: dslide-action-item-reveal :inline t
:END:
- ,#+attr_dslide_propertize: face (:background "#ddddff" :foreground "#000000"
:weight bold :height 2.0)
- This is some fancy text
+ # A keyword configuration
+ ,#+dslide_kmacro: :frequency 0.08 :jitter 0.5 :keys [134217848 97 110 ]
+
+ # An affiliated keyword configuration
+ ,#+attr_dslide_propertize: face '(:background "#ddddff")
+ This text will be propertized
#+end_src
๐ง After class names, the current plist read implementation splits the string
rather than using ~read-string~ and is therefore not smart enough to parse
lists as arguments. However ~dslide-action-propertize~ demonstrates doing this
correctly and shows that it will be possible if needed.
** Babel Scripting ๐งโ๐ป
@@ -313,6 +321,17 @@ The callback function should accept a DIRECTION argument.
DIRECTION is forward,
#+end_src
โน๏ธ You can also use ~dslide-push-step~ in actions for implementing tricky
action behaviors. The image action uses this currently.
+** Keyboard Macros ๐ค
+:PROPERTIES:
+:DESCRIPTION: Acting like you type well while talking
+:END:
+๐งช Experimental new feature. Hopefully the configuration argument names are
good. Hopefully.
+
+The ~dslide-action-kmacro~ will run pre-recorded sequences of keystrokes as if
you are controlling the computer. Through =:frequency= and =:jitter=, it plays
back strokes at a human-feeling pace.
+
+By playing back keyboard macros, you can encode real Emacs workflows as steps
in a presentation. Don't just talk about how your software works. Use the
software with fully reproducible steps that users can understand in a tactile,
human way.
+
+๐ The jitter uses a Laplace distribution to sample a perturbation power. This
power is mapped onto the zero-to-infinity factor range by raising e to the
power of jitter. This is multiplied by =:frequency=, which is a duration. As
a result, while the jitter is usually pretty small, it does have some wild
variation, which does look a bit more human.
** Hiding Markup ๐ฅท๐ฟ
:PROPERTIES:
:DESCRIPTION: No More Ugly
diff --git a/dslide.el b/dslide.el
index c0afa2a469..da9af4f6d6 100644
--- a/dslide.el
+++ b/dslide.el
@@ -41,9 +41,10 @@
;; DSL IDE creates presentations out of org mode documents. Every single step
;; in a presentation can be individually configured or customized. Org
;; headings and elements are configured with extensible actions. Custom steps
-;; can be scripted with babel blocks. Frequent customizations can be made
-;; into custom actions. DSL IDE achieves a good result with no preparation
-;; but can achieve anything Emacs can display if you need it to.
+;; can be scripted with babel blocks. Keyboard macros can play back real
+;; command sequences. Frequent customizations can be made into custom
+;; actions. DSL IDE achieves a good result with no preparation but can
+;; achieve anything Emacs can display if you need it to.
;;
;; To try it out, install this package and load the demo.org found in the test
;; directory of the repository. `dslide-deck-start' will begin the
@@ -319,6 +320,7 @@ keyword."
;; TODO test the use of plist args
(defcustom dslide-default-actions '(dslide-action-hide-markup
dslide-action-propertize
+ dslide-action-kmacro
dslide-action-babel
dslide-action-image)
"Actions that run within the section display action lifecycle.
@@ -400,6 +402,9 @@ See `dslide-base-follows-slide'.")
(defvar dslide--animation-timers nil)
(defvar-local dslide--animation-overlays nil)
+(defvar dslide--kmacro-timer nil
+ "Allow cleanup and prevent macros from running concurrently.")
+
;; Tell the compiler that these variables exist
(defvar dslide-mode)
@@ -1854,6 +1859,144 @@ Only affects standalone-display.")
(overlay-put overlay 'invisible t)
(push overlay dslide-overlays))))))
+;; ** Kmacro Action
+
+(defclass dslide-action-kmacro (dslide-action)
+ ((frequency
+ :initform 0.07
+ :initarg :frequency
+ :documentation "Peak duration between keystrokes.
+Peak is not a maximum. Most durations will be around this value. Jitter will
+result in some being larger and smaller.")
+ (jitter
+ :initform 0.4
+ :initarg :jitter
+ :documentation "Shape parameter for jitter between keystrokes.
+This simulates a human typing. The math uses a long-tailed
+Laplace distribution (the only good distribution for serious
+work). Larger values of jitter will result in more variation in
+frequency. Values of 0.1 to 1.0 are reasonable. Must be
+greater than 0.0. Values of 0.0 will result in no jitter."))
+ "๐ง UNSTABLE! Playback keyboard macros stored in keywords.
+We hope the new format is flexible enough to add features. But
+we will break it as soon as possible to avoid pain if necessary
+and then advise you how to update inside the NEWS (Release notes
+on Github)
+
+Beware! Macros run amok will sell your entire stock portfoloio,
+upload all your personal data to pastebin, and confess to
+multiple crimes you probably didn't commit. They are not
+portable from configuration to configuration. They are however
+very special in that they replicate how you use Emacs.")
+
+(cl-defmethod dslide-final :after ((_obj dslide-action-kmacro))
+ ;; todo clean up any animation
+ (when dslide--kmacro-timer
+ (cancel-timer dslide--kmacro-timer)
+ (setq dslide--kmacro-timer nil)))
+
+;; Default end method behavior plays all steps forward, which would wreck the
+;; last hope of every nightmare.
+(cl-defmethod dslide-end ((obj dslide-action-kmacro))
+ (dslide-marker obj (org-element-property :end (dslide-heading obj))))
+
+(cl-defmethod dslide-forward ((obj dslide-action-kmacro))
+ ;; TODO Erroring within an action usually allows retry. Let's find out. ๐ค
+ (when dslide--kmacro-timer
+ (user-error "Dslide keyboard macro already running"))
+ (when-let ((keyword (dslide-section-next
+ obj 'keyword
+ (lambda (e)
+ (when (string= (org-element-property :key e)
+ "DSLIDE_KMACRO")
+ e)))))
+ (let* ((value (org-element-property :value keyword))
+ (params (dslide-read-plist value)))
+ ;; TODO warn when direction is wrong.
+ (when (or (eq 'forward (plist-get params :direction))
+ (not (member :direction params)))
+ (dslide--kmacro-reanimate
+ :last-input last-command-event
+ :frequency (or (plist-get params :frequency) (oref obj frequency))
+ :jitter (or (plist-get params :jitter) (oref obj jitter))
+ :index 0
+ :keys (plist-get params :keys))
+ (org-element-property :begin keyword)))))
+
+(cl-defmethod dslide-backward ((obj dslide-action-kmacro))
+ (when dslide--kmacro-timer
+ (user-error "Dslide keboard macro already running"))
+ (when-let ((keyword (dslide-section-next
+ obj 'keyword
+ (lambda (e) (when (string= (org-element-property :key e)
+ "DSLIDE_KMACRO")
+ e)))))
+ (let* ((value (org-element-property :value keyword))
+ (params (dslide-read-plist value)))
+ (when (eq 'backwards (plist-get params :direction))
+ (dslide--kmacro-reanimate
+ :last-input last-command-event
+ :frequency (or (plist-get params :frequency) (oref obj frequency))
+ :jitter (or (plist-get params :jitter) (oref obj jitter))
+ :index 0
+ :keys (plist-get params :keys))
+ (org-element-property :begin keyword)))))
+
+;; โ ๏ธ The implementation needs to return or else the normal command loop is in
+;; the way of the keys being read and only the last event pops out. Thanks,
+;; Elisp. For now, a recursive timer is the only way I've found to make this
+;; work. Maybe threads will work. We'll see.
+(defun dslide--kmacro-reanimate (&rest args)
+ "Play keys in ARGS back like the piano man.
+This function recursively calls itself via a timer until it runs
+out of keys in ARGS. It compares the most recent input with
+`last-input-event' so that it can detect if it has been
+interrupted by some other input event and quit."
+ (let ((frequency (plist-get args :frequency))
+ (jitter (plist-get args :jitter))
+ (keys (plist-get args :keys))
+ (last-input (plist-get args :last-input))
+ (index (plist-get args :index)))
+ (if (eq last-input last-input-event)
+ (if (length> keys index)
+ (let ((k (aref keys index)))
+ (setq unread-command-events (list k))
+ (setq dslide--kmacro-timer
+ (run-with-timer
+ (dslide--laplace-jitter frequency jitter)
+ nil #'dslide--kmacro-reanimate
+ :last-input k
+ :keys keys :index (1+ index)
+ :frequency frequency :jitter jitter)))
+ (setq dslide--kmacro-timer nil))
+ ;; TODO attempt to block unwanted input. Test other implementations.
+ (setq dslide--kmacro-timer nil)
+ (message "Out-of-band input detected: %s" last-input-event)
+ (message "Aborting playback."))))
+
+(defun dslide--laplace-jitter (freq jitter)
+ "Mutate FREQ by JITTER shape parameter.
+Jitter is the b parameter of the Laplace distribution, the only
+good distribution for serious work. Larger b causes a fatter
+tail. In practice, small JITTER will be more predictable, but
+never without some larger variations.
+
+Because the Laplace distribution has a fat tail, you aren't
+forced to choose between good local refinement and good mixing
+speeds and lower auto-correlation. ๐ก"
+ (cond ((integerp jitter) (error "Jitter was integer: %s" jitter))
+ ((= jitter 0.0) freq)
+ ((< jitter 0.0) (error "Jitter too low: %s" jitter))
+ ;; Elisp has no random float because ๐ฉ
+ (t (let* ((p (/ (random 1000000) 1000000.0))
+ (p (min 0.99 (max 0.01 p))) ; clamp extreme 2%
+ (sample (if (> p 0.5)
+ ;; - (self.b * (2.0 - 2.0 * p).ln())
+ (- (* jitter (log (- 2.0 (* 2.0 p)) float-e)))
+ ;; self.b * (2.0 * p).ln()
+ (* jitter (log (* 2.0 p) float-e)))))
+ (* freq (expt float-e sample))))))
+
;; * Slide Actions
;; A slide action will generally control the restriction, hydrate children, and
;; pass through `dslide-stateful-sequence' calls to children. There could be
diff --git a/test/demo.org b/test/demo.org
index b62780d058..f4e6efcc95 100644
--- a/test/demo.org
+++ b/test/demo.org
@@ -438,6 +438,35 @@ Massachusetts, in particular, has always been one of the
laboratories of democra
Democracy depends on an informed citizenry and the social cohesion that those
citizens can show even when they disagree.
The essence of democracy is the resolve of individuals working together to
shape our institutions and our society in ways that allow all of us to flourish.
+* Keyboard Macros
+/New feature!/ You can play back keyboard macros.
+
+** Keyboard Macro Example โจ
+Press โก๏ธ to run the (invisible) recorded macro ๐ค .
+
+# M-x animate-birthday-present dslide
+#+dslide_kmacro: :frequency 0.09 :jitter 0.4 :keys [134217848 97 110 105 109
97 116 101 45 98 105 114 116 104 100 97 121 45 112 114 101 115 101 110 116
return 100 115 108 105 100 101 return]
+
+๐ง Please try this feature. Open issues. Keep in mind that keyboard macros
are not very reproducible. They likely only work on your configuration. It is
a great way to demonstrate using your shell etc while not having to type. ๐ก
+
+# If the user has M-x remapped, we should at least warn them.
+#+begin_src elisp :direction init :exports none :results none
+ (unless (or (eq (key-binding (kbd "M-x")) #'execute-extended-command)
+ (eq (command-remapping 'execute-extended-command)
+ (key-binding (kbd "M-x"))))
+ (org-back-to-heading)
+ (re-search-forward "๐ค ." nil t)
+ (let ((o (make-overlay (1- (point)) (point))))
+ (overlay-put o 'after-string
+ (propertize " โ ๏ธ Your M-x appears remapped! Press โฌ๏ธ and
skip this example." 'face 'warning))
+ (push o dslide-overlays)))
+#+end_src
+
+# Clean up the buffer if it was created
+#+begin_src elisp :direction final :exports none :results none
+ (when-let ((birthday (get-buffer "*A-Present-for-Dslide*")))
+ (kill-buffer birthday))
+#+end_src
* Enjoy!
- This package use used to create videos on Positron's own
[[https://www.youtube.com/channel/UCqM0zDcFNdAHj7uQkprLszg/][YouTube ๏
ช]] channel
- File issues and request features to give us ideas about usage and need
- [nongnu] elpa/dslide fbf801fc91 10/21: Notes about org-confirm-babel-evaluate, (continued)
- [nongnu] elpa/dslide fbf801fc91 10/21: Notes about org-confirm-babel-evaluate, ELPA Syncer, 2024/12/17
- [nongnu] elpa/dslide 7d527c9032 14/21: Generated files, ELPA Syncer, 2024/12/17
- [nongnu] elpa/dslide 0ad1ec8218 15/21: Fix for window scroll when opening contents, ELPA Syncer, 2024/12/17
- [nongnu] elpa/dslide 2323468110 16/21: small corrections in the demo, ELPA Syncer, 2024/12/17
- [nongnu] elpa/dslide e585c80697 18/21: Re-generate documentation, ELPA Syncer, 2024/12/17
- [nongnu] elpa/dslide c90b3d65ef 04/21: Documentation Kaizen, ELPA Syncer, 2024/12/17
- [nongnu] elpa/dslide a32f81b03a 07/21: cruft removal / refactor, ELPA Syncer, 2024/12/17
- [nongnu] elpa/dslide b22f94dd96 17/21: Overhaul of start functions, introducing dslide-deck-develop, ELPA Syncer, 2024/12/17
- [nongnu] elpa/dslide 6015821eda 20/21: half-hot fix for action duplication, ELPA Syncer, 2024/12/17
- [nongnu] elpa/dslide ac35d9c35b 13/21: 0.6.0 News word salad, ELPA Syncer, 2024/12/17
- [nongnu] elpa/dslide 1123a4ecbe 11/21: Keyboard MACRO ACTION,
ELPA Syncer <=
- [nongnu] elpa/dslide 129e16c562 19/21: Extra test files, ELPA Syncer, 2024/12/17
- [nongnu] elpa/dslide b8e1399cc5 21/21: Small news correction, ELPA Syncer, 2024/12/17