Dashboard
Lem ships with a customizable dashboard, lem-dashboard, out of the box:

Besides a more friendly greeting than a *tmp* buffer, it ships with useful shortcuts like starting a lisp scratch or seeing recent files/projects at a glance.
The dashboard is launched when you open lem without passing any file arguments. If you wish to open/reopen it, you can do so with M-x open-dashboard.
To disable, simply set *dashboard-enable* to nil in your init.lisp:
(setf lem-dashboard:*dashboard-enable* nil)
If you want to make changes to the dashboard without defining a custom layout, there is a convenience function set-default-dashboard that you can use to make minor modifications:
(lem-dashboard:set-default-dashboard :project-count 1
:file-count 6
:hide-links t
:splash '("hello" "hello again"))
Which would look like:

Here are the currently available keywords:
hide-links: Whether to hide the doc/GitHub links.project-count: How many projects to list.file-count: How many recent files to list.splash: List of splash messages, will be randomly chosen.footer-messages: List of footer messages, will be randomly chosen.
lem-dashboard is designed to be customizable. If you wish to make more changes than set-default-dashboard allows, you can make your own dashboard layout.
A dashboard is simply a list of dashboard-item instances. Here’s a list of available items:
dashboard-splash: Randomly displays one ofSPLASH-TEXTS.dashboard-url: Creates link/button withDISPLAY-TEXTthat opensURLexternally.dashboard-working-dir: Prints current working directory.dashboard-footer-message: Randomly displays one of the passed-inMESSAGES.dashboard-command: Creates a link/button withDISPLAY-TEXTthat executesACTION-COMMANDwhen clicked/activated.dashboard-recent-projects: Displays a list of recent projects, limited to the lastPROJECT-COUNT.dashboard-recent-files: Displays a list of recent files, limited to the lastFILE-COUNT.
All you need to do is call set-dashboard and pass it a list of dashboard-item instances:
(in-package :lem-dashboard)
(set-dashboard (list (make-instance 'dashboard-splash
:item-attribute 'document-metadata-attribute
:splash-texts '("Welcome!" "Second splash message!")
:top-margin 4
:bottom-margin 2)
(make-instance 'dashboard-working-dir)
(make-instance 'dashboard-recent-files
:file-count 5
:bottom-margin 1)
(make-instance 'dashboard-command
:display-text " New Lisp Scratch Buffer (l)"
:action-command 'lem-lisp-mode/internal:lisp-scratch
:item-attribute 'document-header2-attribute
:bottom-margin 2)))
The base class, dashboard-item, has the following slots:
item-attribute: The attribute used when drawing the itemtop-margin: The amount of vertical space (lines) to apply before the item.bottom-margin: The amount of vertical space (lines) to apply after the item.action: Function to execute whenRETis pressed over this item. Most of the existingdashboard-itemimplementations have good default behavior here.
Creating a custom dashboard-item is straightforward; just derive the class and implement the draw-dashboard-item method.
You are responsible for the actual insertion of text inside of draw-dashboard-item. There is a convenience function, create-centered-string, which you can use when inserting text.
As an example, let’s make a dashboard-item that fetches a random fact from an HTTP API and prints it:
(in-package :lem-dashboard)
(defclass dashboard-random-fact (dashboard-item)
((fact :initform nil :accessor fact)
(fetching :initform nil :accessor fetching))
(:documentation "Creates a dashboard item that displays a random fact."))
(defmethod draw-dashboard-item ((item dashboard-random-fact) point)
;; Display fact if exists, or 'loading fact..' while fetching
(insert-string point
(create-centered-string (or (fact item) "Loading fact..."))
:attribute (item-attribute item))
;; If we don't currently have a fact and are not fetching, start fetching fact
(unless (or (fact item) (fetching item))
(setf (fetching item) t)
;; Start a background thread so we don't block
(bt2:make-thread
(lambda ()
(handler-case
(let* ((response (dexador:get "https://uselessfacts.jsph.pl/api/v2/facts/random"))
(json-data (json:decode-json-from-string response))
(fact-text (cdr (assoc :text json-data))))
;; Once we receive the fact, we'll set it and redraw the dashboard
(send-event (lambda ()
(setf (fact item) fact-text)
(redraw-dashboard))))
(error (e)
(send-event (lambda () (message (format nil "Error fetching fact: ~A" e))))))
:name "random-fact-fetcher"))))
Multithreading is overkill here, but it lets us fetch the fact without delaying the startup of the dashboard. Adding dashboard-random-fact to the last above, we get:

- Only vertical stacking is currently supported. If you want to stack multiple elements horizontally, you’ll have to make a dashboard item that does so.
- No image support.