Markwhen and markwhen.com are built to be extensible, primarily in that the app does not assume what kind of visualization will be used from the parsed text.
Name | Repo | Link | By | Notes |
---|---|---|---|---|
Timeline/gantt | mark-when/timeline | timeline.markwhen.com | markwhen | Timeline and gantt view in one |
Calendar | mark-when/calendar | calendar.markwhen.com | markwhen | Calendar |
Map | map.markwhen.com | markwhen | Map | |
Resume | mark-when/resume | resume.markwhen.com | markwhen | Specific syntax, see here for an example |
Views communicate with the renderer/editor via the view client library (github).
The simplest implementation of a markwhen view would look something like the following:
<html>
<body>
<div id="container"></div>
<script type="module">
import { useLpc } from "https://unpkg.com/@markwhen/view-client/dist/index.js";
const { postRequest } = useLpc({
state: (newState) => {
document.getElementById("container").innerHTML =
JSON.stringify(newState);
},
});
postRequest("state");
</script>
</body>
</html>
This is less of a "visualization" more than it is "spitting out the entire app state into html." We request a state update, and, when we get a state update, we set the innerHTML of our only div
to be the whole state. Useful to get started so you can actually see what data is coming through for you to work with.
Let's break it down: we import useLpc
from the view-client
library (LPC
like "remote procedure call" except it's local instead of remote). useLpc
takes an object of listeners, all of which are optional:
interface MessageTypes {
state: State;
setHoveringPath: EventPath;
setDetailPath: EventPath;
setText: {
text: string;
at?: {
from: number;
to: number;
};
};
showInEditor: EventPath;
newEvent: {
dateRangeIso: DateRangeIso;
granularity?: DateTimeGranularity;
immediate: boolean;
};
editEventDateRange: {
path: EventPath;
range: DateRangeIso;
scale: DisplayScale;
preferredInterpolationFormat: DateFormat | undefined;
};
jumpToPath: {
path: EventPath;
};
jumpToRange: {
dateRangeIso: DateRangeIso;
};
}
type MessageListeners = {
[Property in keyof MessageTypes]?: (
event: MessageTypes[Property]
) => any;
};
export function useLpc(listeners?: MessageListeners) { ... }
The type of each entry in the MessageTypes
interface is the type of the parameter that can come through as a message, either to the hosting app or, more likely, from the hosting app (hosting app meaning the view container; this).
In our simple example, we only have a listener for state
updates. state
updates contain information both on the state of the app (is dark mode on, if one of the events is currently selected, which page we're on, etc.) as well as the parsed markwhen document. These are contained in state.app
and state.markwhen
respectively.