Getting Started

ULLD functions primarily as an ecosystem of note taking and academic focused React components and a build script to generate a complete web application using all of the most modern technologies, customized to your own configuration.

Get the latest

Follow the ULLD blog here, specifically the 'About ULLD' section to stay up to date on the changes coming to ULLD. Every consequential update will include an accompanying blog post.

About the installation

The installation process described here only needs to be undertaken once. Once your app is built, future builds can be configured from this initial build using the existing configuration file. You do not need to reinstall everything from scratch or run the build script every time your notes change. Once your application is compiled, simply creating new files of the file types that are supported by ULLD inside of the directory you've added to your ULLD configuration file will update your notes automatically, and clicking a single sync button available from anywhere within your app will update your database to include your new notes in seconds.

My own workflow related to ULLD went like this:

  1. Create a new file of whichever ULLD supported file type I needed.
  2. Click the sync button initially so ULLD can add that note to the search results.
  3. Navigate to that note, and simply refresh the browser every time I wanted to view an updated version of my note. Because ULLD does not pull any data from remote sources, this refresh should be almost instant. To follow changes to the note on your file system closely, make sure to turn on the Prefer File System option from the command palette from within your app for this rapid updating to take effect. This is off by default for a small boost to performance, but turning this option on will tell ULLD to avoid the database for everything that doesn't absolutely require interaction with the database, and will instead choose to read directly from your file system.

While it's a good practice to sync your notes often, even the sync button really only needs to be used everytime a note is added; not when a note is changed.

Environment

For now, ULLD runs only in the browser with an accompanying Node back end. In time, a companion mobile application will be released for both Android and iOS, and if user's collectively determine that a native desktop app is preferable, a native desktop application as well.

Installing ULLD

The Build Process

This build process will remain under active development. With time, there will likely be an option for installing ULLD via a single container that will house all of the necessary steps described here. That however, would not be without it's own drawbacks, so the build cli will be updated regularly. Make sure to update the @ulld/build package regularly, ideally before every update of ULLD itself.

Review

If you are unfamiliar with JSX syntax, review the Intro to jsx article. JSX is an incredibly simple syntax that allows you to insert complete React components directly in your markdown notes, and is at the core of ULLD functionality..

Configure

Configure your application using either the configuration form here, or by writing your own appConfig.ulld.json

Important

The online configuration form will handle all of the core options of your appConfig.ulld.json file, but it does not cover everything. There are many more options that can be set for a more refined application using the types exported from the @ulld/configschema package, but there is one value that you absolutely must set on your own: fsRoot.

The fsRoot field is a single field of type string at the base of your appConfig.ulld.json file that points to a directory anywhere on your computer that houses all of your notes. This must be set before the build takes place to properly gather information about your file system. This directory is really the only directory you'll have to interact with once your app is compiled until you choose to update or rebuild your application. All of your mdx, notebook, bib and other content files should go in this directory.

The appConfig.ulld.json file also accepts bibPath and cslPath fields, which can be used to point to a .bib and .csl file with a path that is relative to the fsRoot path. The bibPath field defaults to /citations.bib and the cslPath field is optional.

If you have a citations.bib file inside of the directory pointed to by your ULLD_ADDITIONAL_SOURCES variable, a file and path will be automatically generated for you.

There is a repository of every csl format you could ever want available here. Any of those files be placed in the root of your ULLD_ADDITIONAL_SOURCES directory with any file name, as long as it ends with a .csl extension. If that file is present, you can ignore this field as a path and file will be automatically generated for you.

There are some useful cli commands that can help with this process documented here.

Setup your computer

If you do not have Node.js installed, install the specific stable build for your operating system by clicking here1 ULLD works as an entire full-stack application, all on your own computer. This means that a locally running server is required, and for that, ULLD uses Node.js. Because of the safety limitations within browsers and their intentional lack of access to the file system, any application of this type would not be able to function without a locally running server.

Package Managers
TLDR: Use pnpm for now.

Package managers are pieces of software that make it much easier to install, locate and update packages for a given environment. For Node, there are three popular package managers.

When you install Node, you'll automatically install a package manager called npm as well. There are two other popular alternatives to npm that do exactly the same thing in a usually more efficient manner, pnpm and yarn. Eventually, ULLD will support all three package managers. The code is in place, but as of now, the only package manager that has been thoroughly tested is pnpm.

If you are starting from an environment without Node or a Node package manager installed, the easiest way to accomplish this is to download Node, and use npm to install pnpm by running the following command in your terminal.

npm install --global pnpm

You will now have access to the pnpm command directly, which the ULLD cli will use to install dependencies needed to complete the build of your application based on your configuration file.

Tell ULLD where to find the configuration file.

This can be done in 1 of 2 ways, but please make things easy on yourself and use the first method unless you have good reason to use the second.

First, you can create a directory anywhere on your computer that follows the file structure described here, and point to that directory with an env variable ULLD_ADDITIONAL_SOURCES.

On a mac, that would look like this:

~/.zshrc
export ULLD_ADDITIONAL_SOURCES="/path/to/my/directory"

And on windows, you'll have to follow the docs here. This approach will allow the core app files to exist separately from the compiled app, ensuring that they remain available during rebuilds and updates.

Alternatively, you can run the build cli and wait for it to pause when it can't find the appConfig.ulld.json file. If the cli cannot find the configuration file, it will pause and give you the opportunity to drop the appConfig.ulld.json file into the directory that the cli creates.

The downside to this approach however, is that this configuration file will be overwritten if it is not saved elsewhere when the app is updated or rebuilt.

Configure Postgres

ULLD will eventually support two different databases; SQLite and Postgres. Postgres is strongly preferred for performance reasons, but both Postgres and SQLite offer their own unique advantages. For now however, only Postgres is officially supported. Unfortunately, Postgres requires some additional setup by you, the user. If you intend to store your data on your own machine, as most users will, you'll need to install the postgres server on your machine as well.

To install postgres, visit their installer page here.

After installing postgres, you'll need to create a database specifically for ULLD. This can be done through the command line using the createdb command as documented here, or through a graphical interface like the free and open source pgAdmin tool available for download here.

You will need to provide ULLD with two environment variables used to provide a connection url for your database, ULLD_POSTGRES_URL and ULLD_POSTGRES_URL_NON_POOLING. While they both are required, when using a local postgres instance these will most likely have the same value.

Unlike the ULLD_ADDITIONAL_SOURCES environment variable, these variables will most likely be specific to your application. To create env variables that are applied specifically to your generated application, you can create a file called .env (notice the leading period) in the root of the directory pointed to by your ULLD_ADDITIONAL_SOURCES variable.

If you use the pgAdmin tool, you'll be able to find the connection url directly through their interface. If you create your database through the cli however, you'll need to format your own connection url using the format documented here.

When this is all put together, you'll have something like this:

~/.zshrc
export ULLD_ADDITIONAL_SOURCES="/path/to/my/directory"
/path/to/my/directory/appConfig.ulld.json
{
    "fsRoot": "/path/to/my/notes/directory",
    ...
}
/path/to/my/directory/.env
ULLD_POSTGRES_URL="postgresql://myUserName:somePassword@localhost:5432/myUlldDatabaseName"
ULLD_POSTGRES_URL_NON_POOLING="postgresql://myUserName:somePassword@localhost:5432/myUlldDatabaseName"

Whew... I know that's a lot, but you're now all set to generate your app.

The last step of this process will be handled almost entirely by the ULLD cli. All you need to do is install it, using npm, pnpm, or yarn.

Once you've installed the build cli using the command above you'll have access to the ulld command. For right now the @ulld/build package exposes only two commands, build and run, which can be executed as ulld build and ulld run.

For more information and to see the available options, use the ulld command without any other arguments. You can also see more detailed help for any command with the ulld help <cmd>, or in practice, ulld help build or ulld help run.

Note: If you are executing this command against a new database, you'll want to include the ulldBuild --genDatabase flag. This will format your database to match the ULLD data structure. Be careful however to not include this flag against future builds using the same database, as it may modify the database in a manner that is not recoverable.

If you are unsure of how to find the folder you would like to place the project in from within the terminal, do a quick Google search for 'change directory' on your specific operating system. On a mac, you can also right clock on the folder in Finder and select Services > New Terminal Tab at Folder. Remember to select the folder that the project should be inside of. Do not create a folder specifically for ULLD, as the build cli will take care of that for you. For example, if you run the ulldBuild command from within the /Users/myUserName/myRandomStuff directory, the generated app will exist in a newly created /Users/myUserName/myRandomStuff/ulldApp directory.

That's It!

I know that's a lot more complicated than installing a regular application, but building the app in this format will allow ULLD to grow endlessly, as the learning curve for developers to contribute is near zero.

You can now run your project with ulld run.

Note:

For those with development experience, note that the ulld run command is just a utility to execute the start script in the compiled app's package.json file from any directory.

alias myConvenientAlias="pnpm run start --dir /path/to/my/app/ulldApp"

Future Builds

This build process will become significantly improved over the coming weeks and months. Under normal circumstances, I wouldn't even release this application until a better build process was in place, but because of my current living situation, the heat, and my sweet, sweet puppy, I'm unable to work for more than a few hours each day until my battery dies. Once that is taken care of, or even in small increments in the mean time, a significantly improved build process is my main focus.

Optional: Writing your own configuration file

If you choose to write your own configuration file, follow the AppConfigSchemaOutput type exported from @ulld/configschema/types. That same module also exports a zod object, appConfigSchema from the @ulld/configschema/zod/main path that can be used to generate a more complete AppConfigSchemaOutput type with sensible defaults, however you must at least fulfill the AppConfigSchemaInput type and then write the output to an appConfig.ulld.json file. That would look something like this:

import fs from 'fs'
import path from 'path'
import {appConfigSchema, AppConfigSchemaInput} from "@ulld/configschema/zod/main"
 
// TODO: Change this to wherever you are storing the application or to 
// where your ULLD_ADDITIONAL_SOURCES variable points to.
const target = "/Users/bigsexy/ulld/appConfig.schema.json" 
 
const myConfig: AppConfigSchemaInput = {
    fsRoot: "/Users/bigsexy/Desktop/notes", // Yes... my real home directory. Self confidence is important.
    ...
}
 
fs.writeFileSync(target, JSON.stringify(myConfig, null, 4), { encoding: "utf-8" })
 
console.log(`Your app config was written to ${target}!`)
Hint:

Make sure to set the fsRoot value to the root of your notes; not the root of your compiled application. This allows the you to have a folder that is dedicated only to your notes, not one that is complicated by an additional application.

Footnotes

  1. Node.js is a popular run time that allows Javascript, a language that was intended to run inside of browser environments, to be executed on the server. ULLD, along with many other applications, relies on a locally running server instance for interacting with it's internal database. ULLD does not contact any outside service or remote database if your application does not specifically set a database connection url to a remote instance, and if your application does not provide credentials for a third party service like Google Calendar.

On this page