Plugin Configuration
ULLD documentation for developers
ULLD supports several types of plugins. Developers can build plugins that integrate with ULLD seemlessly and provide one more more of the following features:
- Embeddable Components: Components that can be embedded in the user's mdx content.
- Slot Components: Components that override specific component slots in the user's compiled application.
- Parsing Functions: Functions that partially parse mdx content before it is rendered to React, adding the ability to create new syntaxes unique to your specific plugin.
Developer Config Schema
ULLD exports a zod object from @ulld/configschema/developer
as a named export, developerConfigSchema
as well as a DeveloperConfigInput
type. ULLD also exports a simple utility function, writePluginConfig
from @ulld/developer/writePluginConfig
. This approach was taken to allow you, the developer, to ensure that your configuration can be successfully parsed before publishing your plugin to npm. With these exports, a file can be created at the root of your package (this already exists if you clone the developer template) which can then be executed with ts-node
or tsx
generate a completely type safe json file named pluginConfig.ulld.json
, also at the root of your project.
Prop | Type | Default |
---|---|---|
pluginName | string | - |
label | string | - |
slot | string | number | symbol | - |
components | ComponentConfig<T>[] | - |
parsers | { mdx?: ParserConfig | undefined; } | - |
additionalImports | AdditionalImportsConfig | - |
trpc | TrpcConfig | - |
settings | PluginSettingsConfig | - |
pages | PluginPageConfig<T>[] | - |
events | PluginEventsConfig | - |
navigationLinks | NavigationLinkType[] | - |
Slot
OptionalThe slot field in the DeveloperConfigInput
type refers to an existing slot in the user's entire application. These slots, mostly, don't affect the user's embedded components but rather their compiled application as a whole. If you do override a slot in the user's compiled application, you do not need to override all subSlots in that slot.
If your pluginConfig.ulld.json
, which ideally is defined directly through the developerConfigSchema
zod object specifies a slot of type keyof SlotMap
, then 1 or more of your components can specify a slot which is a key of the internal object, or a second level key of SlotMap
.
For example, if your pluginConfig.ulld.json
specifies a slot property of navigation
, then your components can optionally override navbar
or secondary
by setting the developerConfigSchema.components[number].slot
path to navbar
, secondary
, or any key of SlotMap["navigation"]
. These components will be passed the same props as the internally built component and be rendered in the same location in the DOM.
This SlotMap
type is exported from @ulld/configschema/developerTypes
, and a matching Typescript object is exported from @ulld/utilities/slotMap
.
Components
Important
It is important to remember that all components, along with most ULLD plugin exports, must be the default export from the file they are contained in. The only real exception to this rule is the PageProps
type discussed here, which is optionally exported as a type alongside a default exported component.
The DeveloperConfigInput["components"]
path is an array of objects which maps items from the exports field in your package.json
file to locations within the user's compiled application, with some properties that you can set as a developer. These components can take one of two broad types:
- An embeddable component, which the user can include directly in their mdx content.
- A slot component, which can override slots that are by default occupied by internally developed ULLD components.
Prop | Type | Default |
---|---|---|
componentName | string | - |
slot | T extends keyof SlotMap ? keyof SlotMap[T] : string | - |
export | string | - |
embeddable | EmbeddableConfig | EmbeddableConfig[] | - |
docsExport | string | - |
fullDocsExport | string | - |
exportedPropsName | string | - |
Of the above properties, the following are universal, regardless if the component is an embeddable component or a slot component.
componentName
The name of the component. This is used for generating files, and more importantly, for creating default mdx embeds. Like all React components, make sure it starts with a capital letter. This will be handled internally as well, but being too reliant on parsing this after the fact may lead to unforeseen bugs.
export
Your files will be ran through a bundler as your app is compiled. There's no need to bundle your app as javascript before hand, and in fact, it might actually complicate the build process. Unless you built your components in Javascript originally, export the .tsx
file directly.
This must match an export field in your package.json
file. For example, take the following truncated package.json
file:
You can now include a component in your developerConfigSchema object as:
Make sure the component is the default export from the <root>/src/path/to/actual/file.tsx
file, or whichever path you add to your package.json
file.
Embeddable Components
Embeddable components are the most simple plugin feature to develop for ULLD. Like all components exported by your plugin, they must be a default export and the both DeveloperConfigInput["components"][number]["export"]
and DeveloperConfigInput["components"][number]["componentName"]
properties are required.
The slot
property does not apply to embeddable components, as they will be made available directly to the user and not applied elsewhere. What is required however, is the embeddable
field. The embeddable field is either an object, or an array of objects of the following type:
An array can be used to create aliases for the same component. This is the case with internal components like the Highlight
component, which can also be used as Hl
. The component will not be imported twice, even if it is used with multiple aliases.
Prop | Type | Default |
---|---|---|
regexToInclude | string | - |
label | string | - |
The regexToInclude
property will be passed to a RegExp
class and used to test a note's raw content for whether or not your component should be imported. 99% of the time, this field should take the form of:
Where MyComponentNameOrAlias
is the name of the component as it will be usable to the user. This does not necessarily have to match the name of the component within your code base, and the leading <
makes sure that that value is being used to create a jsx component.
The label field is optional, but if provided should match the regexToInclude
property apart from any special characters. If none is provided, this will be generated automatically. The only time this really should be overridden is during debugging, or if the output of the developerConfigSchema
produces a value at this field that doesn't seem to make sense.
Documenting your component
The component configuration type also supports two documentation fields, docsExport
and fullDocsExport
. These should be either markdown or mdx files, but make sure not to include components other than your own individual component and default ULLD internal components. Even other components within your own plugin are not guaranteed to be available for all users, as users can choose to override components that serve a similar purpose.
While neither documentation field is strictly required for your component, it is heavily preferred that you include at least a docsExport
field to summarize the component's props if they are not immediately obvious. Keep in mind that in most cases, the docsExport
field will be displayed within a container that is no more than 500-600px wide. The difference between these two fields is that the docsExport
field will be displayed in the users command palette for quick reference, and the fullDocsExport
will optionally link to an independent documentation page for more verbose documentation with a full screen layout to include demonstrations and examples.
Slot Components
Slot components are only slightly more complicated than embeddable components. To occupy a slot within the user's compiled application, your entire plugin must occupy that parent slot. This means that for any given plugin, you are only able to produce components for one subset of the compiled application. For example, if you want to build a plugin that replaces the navbar for users, your plugin config must satisfy the following values:
Styling your components
Provided Utilities
ULLD provides dozens of useful utilities for more quickly building your components, from pre-styled containers guaranteed to fit with the user's application to drop-in form components. Make sure to check out the UI utilities documentation here and the functional utilities like hooks and state helpers here
Since a user will likely include other plugins along side yours, it is important to maintain as strict of a policy around using css and tailwind variables to style your components as absolutely possible. This will ensure a uniform look and feel throughout the user's notes and application, regardless if the component is an embedded component or a slot component. Along side standard tailwind properties and colors, ULLD uses Shadcn based themes by default, and even if a user chooses to move away from a Shadcn based theme, the Shadcn color variables will remain available. You can review that documentation here.
Responsiveness
All mdx content will always be wrapped in a @container/mdx
class applied through the @tailwindcss/container-queries
tailwind plugin, and it is heavily suggested that you rely upon this class for most responsiveness issues and not the viewport or parent directly, unless your component triggers something absolutely positioned like modal.
Pages
Documentation in progress