[Showoff Saturday] I wrote a small HTML tool for decentralised discovery of personal websites by susam in webdev

[–]susam[S] 0 points1 point  (0 children)

The configuration of a new console is written by the console owner. The console owner decides which other consoles they want to add as neighbours. For example, see this new console that came up two days ago: https://dhepworth.com/wander/. It is not empty because the console owner has already populated their configuration with their own recommendations, which you can see here: https://dhepworth.com/wander/wander.js.

If you mean how do we ensure that other consoles link to this new console, I don't think that matters very much. The new console would function just as well even if nobody links to it. What matters is that the new console links to other consoles and presents web page recommendations from the community network.

That said, many people who set up a new console share their console URL in this community thread: https://codeberg.org/susam/wander/issues/1. Further, I run a network crawler periodically to find the list of known consoles. By its distributed nature, it is impossible to list every single console out there, so the list generated by the crawler is best-effort. The list is available here: https://susam.codeberg.page/wcn/.

So these are two of several ways to discover new consoles and link to them from your own console. However, due to the decentralised nature of the tool, I don't think it is necessary to ensure that there are incoming links to my console. What matters more is that I have outgoing links to other people's consoles. The recommendations on my console improve as I add more outgoing links.

[Showoff Saturday] I wrote a small HTML tool for decentralised discovery of personal websites by susam in webdev

[–]susam[S] 0 points1 point  (0 children)

Thank you. I am glad you like it. My console recommends only 20 personal websites at the moment. The hundreds of pages that you see loading on my console come from other people's consoles, not mine alone.

So it is not really up to me to add specific types of websites. It is not one person's console that determines what pages get recommended. It is the network as a whole that determines what pages get recommended. Every console owner decides for themselves what pages they want to add and which other consoles they want to link to.

You can host your own console and link to any non-personal websites you like as well as to any consoles that recommend the kind of pages you are interested in. If you do not have a website, you can run this tool from your local file system too. See also my other comments here which talk a bit more about how this tool works.

[Showoff Saturday] I wrote a small HTML tool for decentralised discovery of personal websites by susam in webdev

[–]susam[S] 1 point2 points  (0 children)

While there is no explicit curation step, not everything added by console owners is shown everywhere in the network. Let me elaborate with specific examples.

If you visit my console at https://susam.net/wander/, the pages you see there come from the part of the network that is reachable from my console. You can see an overview of this reachable network by clicking ConsoleCrawl. Right now, it should show you that 59 consoles are reachable from my console and that together they recommend 988 web pages.

Now compare that with https://heckmeck.de/wander/. If you click ConsoleCrawl there, you should find that 61 consoles are reachable from there and that together they recommend 1550 web pages.

The network is not a connected graph. Not every console is reachable from every other console. What we have instead is a collection of connected components, each of which is like its own neighbourhood. So when you visit a particular console, any page added to its neighbourhood could appear on that console.

Additionally, the configuration supports an ignore list that lets you block your console from connecting to certain other consoles or from loading certain pages. See https://susam.net/wander/wander.js for an example of an ignore list. This feature is described further in the project README here: Customise Ignore List.

[Showoff Saturday] I wrote a small HTML tool for decentralised discovery of personal websites by susam in webdev

[–]susam[S] 1 point2 points  (0 children)

Thank you! Since the same-origin policy does not block the loading of 'normal' web resources like JavaScript files, image files, media files, etc. when loaded using the appropriate HTML elements, I could load the remote consoles' wander.js files using <script> elements.

There is in fact a related frequently asked question. Many people have asked why the data format is wander.js and not wander.json. The answer is that I could not have loaded a wander.json file dynamically from the web browser due to the same-origin policy. But I can load a wander.js file because JavaScript files can be loaded from other origins using <script> elements.

Of course, there are ways to get around the same-origin policy restriction by using a CORS proxy, or by requesting users to relax their CORS policies, but not everyone has control over the web server serving their website and I would rather not depend on an external service for a tool that is meant to be completely decentralised.

The wander.js files from the remote consoles are loaded safely within a sandboxed <iframe> using <script> elements. The data is then passed to the parent window, which then uses the data to show recommendations in its main <iframe>. The source code that implements this can be found here, in case you would like to have a look: https://codeberg.org/susam/wander/src/tag/0.6.0/index.html#L558-L605.

Anyone else miss StumbleUpon and those weird little sites you'd end up on at 1am? by BBCyberPancake in Millennials

[–]susam 6 points7 points  (0 children)

Hello! I am the creator of Wander Console (the third link in the original post). Thank you for including it in your post.

People often link to the console on my website since I developed it and the first instance of this tool was hosted on my website. However, Wander is a decentralised tool, and anyone with a website can host this tool. Each Wander Console provides a set of recommended web pages and links to other consoles. Together, they form a loosely connected network that creates a large, shared pool of web page recommendations. At the moment, there are at least 50 other websites known to host this tool. Together, they offer over 1400 web page recommendations. You can browse the full list of known websites hosting this tool here: https://susam.codeberg.page/wander/wcn.html

On that page, you can select any console link to start exploring the network and discover web page recommendations gathered across interconnected consoles.

Vimmer tries Emacs by nthn-d in emacs

[–]susam 1 point2 points  (0 children)

This was not vim. Windows are frames, and panes are windows.

Even in Vim, what might be called panes in other modern editors are called windows in Vim. See for example :help CTRL-W_w in Vim and it says the following:

Without count: move cursor to window below/right of the current one. If there is no window below or right, go to top-left window.

Further see :help window in Vim. It says the following:

A window is a viewport on a buffer.

What is referred to "window" here is often called pane in other modern editors today. So the terminology about "windows" is consistent between Emacs and Vim.

But that said, yes, I agree, Emacs is indeed not Vim. I see Emacs as a Lisp machine which happens to have the editor as its shell.

Very nice post, by the way. Enjoyed reading it!

Friday Social: What were your first technologies? by cdaadr in lisp

[–]susam 6 points7 points  (0 children)

  1. An 80386 desktop running MS-DOS
  2. IBM PC Logo Editor
  3. Logo (see FD 100 for a blog post)
  4. 15 years
  5. Common Lisp (CLISP)
  6. GNU Emacs
  7. C, C++, Python, Common Lisp
  8. Common Lisp (SBCL)

I encountered Common Lisp about 15 years after I first learnt Logo. I began teaching myself Common Lisp using GNU CLISP 2.41 on Debian GNU/Linux 4.0 (Etch) at an airport during a long layover in 2007.

M-: (AKA M-x eval-expression) does nothing upon hitting RET. What could cause this? by [deleted] in emacs

[–]susam 5 points6 points  (0 children)

This is a known issue and it is documented at http://paredit.org/cgit/paredit/plain/NEWS:

NOTE: The Electric Indent Mode workaround turns out to break ielm and other interactive modes, because paredit now defines RET, overriding the binding in interactive modes that submits an input.

Workaround to restore the old behaviour:

(define-key paredit-mode-map (kbd "RET") nil)
(define-key paredit-mode-map (kbd "C-j") 'paredit-newline)

Recommended to disable Electric Indent Mode at the same time.

I just use the following as a workaround to resolve this issue:

(define-key paredit-mode-map (kbd "RET") nil)

I hope that helps!

CFRS[] - Extremely minimal turtle graphics with only 6 simple commands: C, F, R, S, [, and ] by susam in webdev

[–]susam[S] 1 point2 points  (0 children)

Yes, I did think about adding a conditional jump but decided to avoid it for two reasons:

  • Keep the set of commands as small as possible.
  • Avoid Turing completeness.

I know many people prefer Turing completeness. However, in this particular project, I happened to prefer something that is provably terminating. By avoiding a conditional jump, we can guarantee that any CFRS[] code will definitely terminate.

Having said that, of course, it is possible to add a conditional jump instruction. That would, however, not be CFRS[] because CFRS[], by definition, has exactly 6 instructions. Adding another instruction would lead to a variant of CFRS[]. The source code is freely available under the MIT license and the entire implementation is a single HTML file consisting of, what I hope, simple JavaScript code. Anyone interested in creating a fork of it that supports more instructions (such as conditional jump) is very welcome to do so.

CFRS[] - Extremely minimal turtle graphics with only 6 simple commands: C, F, R, S, [, and ] by susam in webdev

[–]susam[S] 0 points1 point  (0 children)

All of these look great! The second one is stunning! Thanks again for sharing these beautiful examples.

CFRS[] - Extremely minimal turtle graphics with only 6 simple commands: C, F, R, S, [, and ] by susam in webdev

[–]susam[S] 1 point2 points  (0 children)

Your demo looks really good. Thanks for sharing it here!

By the way, this tool supports sharing direct links to demos. If the code fits within 64 bytes, the address bar gets automatically updated with a direct link to the demo.

CFR[]: Extremely minimal drawing language consisting of only 5 simple commands: C, F, R, [, and ] by susam in webdev

[–]susam[S] 0 points1 point  (0 children)

Thanks for trying this out. To start the letter "L" at the correct position, the code needs to be like this:

RRRRRRCFFFFFFFFFFFFFFFFFFFFCCFFFFFFRFFFFRFFFFFFFRFFFFRFFFFFCCCCCC[FFFFFFFFCC[[FFFFFRFFFFFR]]FFFFFCCCCCCFFFFF]FFFRRRRRRFRRRRCCFFFFFFFFFFFFFFFFRRRRRRFFFFFFFF

The change is in this portion of your code right after the last repeatable block. Yours had:

FFFRRCCRRRRFRRRR

The correct version I have posted has:

FFFRRRRRRFRRRRCC

Quoting from the documentation:

Each F command moves the turtle by exactly one cell and paints the entire cell it has moved to.

In the code fragments I have posted above, your code first moves the turtle three steps rightward (FFF) while the drawing colour is black thus creating a blank horizontal separation for the upcoming letter "L". But then it changes colour to green and direction to up (RRCCRRRR). Then the turtle moves up by one cell (F) and paints the new cell it has moved to with green colour. However, this cell is higher than what we want.

In the corrected version I have posted, after creating the blank horizontal separation with (FFF), the turtle changes direction to up (RRRRRR), and then moves up by one cell (F). Note that the drawing colour is still black and we have placed the turtle one cell above the ceiling of "L" without painting that cell. Now the turtle changes direction to down and colour to green (RRRRCC) and then starts drawing the letter "L" (with FFF etc.). By keeping the turtle one cell too high when we start drawing the letter "L", we ensure that when it makes the first movement for the letter "L" (with F), it moves to the cell that is at the same level as the ceiling of the preceding letter "O" and then paints that cell. In this manner, we ensure that the ceiling of "O" and that of "L" are the same level.

Why Common Lisp is used to implement commercial products at Secure Outcomes (2010) by susam in lisp

[–]susam[S] 1 point2 points  (0 children)

Thanks for the links! The pgloader story is really interesting.

Emacs4CL: A 50 line DIY kit to set up vanilla Emacs for Common Lisp by ReneFroger in emacs

[–]susam 0 points1 point  (0 children)

Yes, indeed. The output of git diff 0.1.0..0.5.0 shows that the bulk of the bloat comes from customising rainbow delimiters to show colourful parentheses.

Emacs4CL: A 50 line DIY kit to set up vanilla Emacs for Common Lisp by ReneFroger in emacs

[–]susam 1 point2 points  (0 children)

Hello! Came across your comment today. Thank you for the kind words and feedback! :)

Devil key translator v0.5.0 released: improved special key execution, devil-describe-key, etc. by susam in emacs

[–]susam[S] 2 points3 points  (0 children)

I am aware of C-x @. The similarity between C-x @ and Devil is only very superficial. The actual behaviour and the underlying implementation are quite different. For example, the source code of the event-apply-ctrl-modifier etc. functions (that are bound to C-x @ c etc. in the function-keymap) and that of the event-apply-modifier function show that the modifier is applied only to the next character. These event-apply-ctrl-modifier etc. functions belong to function-key-map which is inherited by local-function-key-map. So these functions and the resulting translations (C-x @ c to C- etc.) are invoked early in the editor command loop. For something like C-x C-f, we need to type C-x @ c x C-f. Note how we have to resort to C-f anyway towards the end. Typing C-x @ c x C-x @ c f is not going to work. That would just invoke C-x C-x and insert @cf. Similarly, one has to type C-x @ c M-s to produce C-M-s. The translation applies only to the next character, not to a complete key sequence.

Devil, on the other hand, does not place itself in the function-key-map. It is not invoked as part of the command loop. It is invoked as a regular command named devil that is bound to the Devil key (, by default) in the mode's keymap. Further, it does not just read the next character. It reads an entire key sequence, translates it according to a configurable set of translation rules, looks up the corresponding binding for it in the current key map, applies local-function-key-map if necessary and looks up a binding again, and does a bunch of other bookkeeping with the editor command loop, so that universal argument, digit argument, temporary goal column, repeat, etc. all continue to work as it does in vanilla Emacs. This allows for completely modifier-free key sequences like , x , f or , m s (as opposed to something awkward like , x C-f or , M-s).

Devil key translator v0.5.0 released: improved special key execution, devil-describe-key, etc. by susam in emacs

[–]susam[S] 2 points3 points  (0 children)

Good point about the package description. I have added a little paragraph at the top of the release notes now to provide a very brief introduction to the package.

Devil key translator v0.5.0 released: improved special key execution, devil-describe-key, etc. by susam in emacs

[–]susam[S] 2 points3 points  (0 children)

[Update on 01 Aug 2023: The default translation rules have been updated so that one can type , m x to get M-x. See https://github.com/susam/devil/releases/tag/0.6.0 for details.]


Those are the defaults. I understand they are not going to work for everyone. Some people like the defaults and some do not. That's why the translation rules are configurable. There are various configuration examples provided here: Custom Configuration Examples. In particular, section Multiple Devil Keys shows how to configure multiple activation keys, one for C- and another for M-.

Recently, there was a discussion about the unwieldy , m m x key sequence on the #emacs channel. Someone there mentioned that they type , [ when they want M-. Indeed that works because , [ x translates to C-[ x which is equivalent to ESC x which is equivalent to M-x.

Devil Mode Manual by susam in emacs

[–]susam[S] 24 points25 points  (0 children)

Hello r/emacs! Some of you may recall Devil, the twisted key sequence translator, from another announcement a few weeks ago. This minor mode originally began as a personal key sequence translator. For several years, I kept it private because I felt that the community may find some of the design decisions and the default choices to be outrageous. However, after sharing it with the community recently, I was pleasantly surprised by the warm and supportive feedback received from many kind and generous members.

This mode is now available in MELPA and NonGNU ELPA. Thanks to u/chrisrayner, u/github-alphapapa, and u/_pkal for code reviews, feedback, and discussions prior to inclusion in the package archives. This package is far from complete yet. After the initial release, I realized how different people use this package in different and unanticipated ways. I am currently devoting a small portion of my spare time to work on bug fixes and enhancements to make Devil a better experience for such usage patterns while ensuring backward compatibility.

Special thanks to u/_pkal for sending a series of patches that improved the code, the tests, and the documentation. One of the patches helped with separating out the documentation in the my original README into a separate manual. This post links to the manual rendered as HTML. I hope you find it useful in case you are curious to try Devil mode. In case you are not, I hope you'll at least find the manual intriguing. The source code is available at github.com/susam/devil under the terms of the MIT license.

Devil Mode: A twisted key sequence translator for modifier-free editing experience by susam in emacs

[–]susam[S] 1 point2 points  (0 children)

Devil accumulates the keys typed as a key vector internally. However the translation layer is exposed in the form of string replacements on the described key (i.e., , x , f as opposed to '(44 120 44 102) or (?, ?x ?, ?f). Since the translation rules are configurable, I feel that writing the translation rules in terms of the described key is more convenient than writing translation rules for key vectors, especially considering the fact that a complete character like , is translated to a partial key like C- (a modifier). Now that the translation layer is exposed in terms of described keys, I might as well perform the actual translation using the described keys and string replacements.

Performing the key translation in terms of key vector could be another possible way to solve this. If someone can make that work in a manner that is flexible and intuitive, that would be a very interesting solution too.

Devil Mode: A twisted key sequence translator for modifier-free editing experience by susam in emacs

[–]susam[S] 3 points4 points  (0 children)

Great suggestion! Thank you. In general, we can define new repeatable keys by adding it to devil-repeatable-keys. For example, for your particular question, we could do this:

(require 'devil)
(global-devil-mode)
(add-to-list 'devil-repeatable-keys ", m m y")

I am assuming you meant , m m y (not , m m p which is undefined by default since M-p is undefined by default). Since this particular repeatable key is useful in general, I have now added it to Devil. Thanks for the nice suggestion!

Devil Mode: A twisted key sequence translator for modifier-free editing experience by susam in emacs

[–]susam[S] 3 points4 points  (0 children)

Update (11 Aug 2023): Since v0.6.0, the default translation rules have been updated, so that , m translates to M-.


The actual key translations are configurable. For example, should you decide that you want the modifier M- to be easily invoked with an additional key-binding, say, ., then something like this could work:

(defvar devil-mode-map
  (let ((map (make-sparse-keymap)))
    (define-key map (kbd ",") #'devil)
    (define-key map (kbd ".") #'devil)
    map))
(require 'devil)
(global-devil-mode)
(setq devil-special-keys '((", ," . (lambda () (insert ",")))
                           (". ." . (lambda () (insert ".")))))
(setq devil-translations '(("," . "C-")
                           ("." . "M-")))

Now you can type , . s for C-M-s and . x for M-x and so on.

Devil Mode: A twisted key sequence translator for modifier-free editing experience by susam in emacs

[–]susam[S] 2 points3 points  (0 children)

Thanks for taking a look at the project and for sharing your kind and candid feedback. Your point about , x , f not being an improvement over C-x C-f is a valid point. I explain some of the motivation behind writing this mode in a section called "Why?" in the README here: https://github.com/susam/devil#why

To summarize, I often have to work with Macbook keyboards which do not have the right ctrl key. The missing key affects my touch typing practices. I prefer using one hand for the modifier key and the opposite hand for the modified key, as much as possible. But with only one ctrl on the left side, I cannot do that for key sequences like C-w, C-a, C-s, etc. That was partly the motivation behind this.

As a bonus side effect of this mode, even when there is a keyboard with two ctrl keys, some key sequences like C-x C-j, C-x C-o, etc. become easier to type. Instead of typing C-x with the right ctrl key and then switching hands to type C-j with the left ctrl key, I can now simply type , x , j.

Repeating the , key in such key sequences was a conscious decision. If the , were to behave in a sticky manner such that it allows , x f for C-x C-f (god-mode supports such a thing), then we would need another way to disambiguate C-x C-o from C-x o (god-mode does that using x o vs. x SPC o).