### Install and Run Markwhen Timeline (Bash)
Source: https://github.com/mark-when/timeline/blob/main/README.md
Instructions to clone the Markwhen timeline repository, install dependencies, and run the development server. This process requires Node.js version 18 or higher and npm. The timeline will be available on port 6180 and is automatically detected by Meridiem.
```bash
git clone https://github.com/mark-when/timeline.git
cd timeline
npm i
npm run dev
```
--------------------------------
### Configure Timeline Plugin Metadata (JSON)
Source: https://context7.com/mark-when/timeline/llms.txt
Defines the metadata for the OG Timeline plugin used in the Markwhen ecosystem. This JSON object specifies the plugin's name, a description of its features (timeline and Gantt chart views), the repository URL, an SVG path for its icon, example screenshots, and attribution.
```json
{
"name": "OG Timeline",
"description": "A graph-like representation of events in a cascading timeline. Optionally view as a gantt chart as well.",
"repo": "https://github.com/mark-when/timeline",
"iconSvgPath": "M 8 12 h 11 L 19 13 L 8 13 Z Z Z M 12 18 h 8 L 20 19 L 12 19 Z Z M 5 6 L 8 6 L 8 7 L 5 7 Z M 6 16 Q 7 16 7 17 L 7 20 Q 7 21 6 21 L 5 21 Q 4 21 4 20 L 4 17 Q 4 16 5 16 Z",
"screenshots": [
"https://timeline.markwhen.com/timeline1.png",
"https://timeline.markwhen.com/timeline2.png"
],
"by": "markwhen"
}
```
--------------------------------
### Build Markwhen Timeline (Bash)
Source: https://github.com/mark-when/timeline/blob/main/README.md
Command to build the Markwhen timeline project for production. This command bundles all HTML, CSS, and JavaScript assets into a single `index.html` file located in the `dist/` directory.
```bash
npm run build
```
--------------------------------
### TypeScript: Main Application Entry Point for Vue App
Source: https://context7.com/mark-when/timeline/llms.txt
This script serves as the main entry point for the Vue application, initializing the Vue instance with Pinia for state management and Vue Router for navigation. It also conditionally injects Vue DevTools for development environments. Dependencies include Vue, Pinia, and Vue Router.
```typescript
// main.ts setup
import { createApp } from "vue";
import { createPinia } from "pinia";
import App from "./App.vue";
import router from "@/router/router";
import "./assets/main.css";
const app = createApp(App);
const pinia = createPinia();
app.use(router);
app.use(pinia);
app.mount("#app");
// Vue DevTools in development
if (import.meta.env.DEV) {
const script = document.createElement("script");
script.src = "http://localhost:8098";
document.head.append(script);
}
```
--------------------------------
### Manage Timeline State with Markwhen Store (TypeScript)
Source: https://context7.com/mark-when/timeline/llms.txt
Demonstrates how to use the Pinia store for managing parsed timeline data, application configuration, and bidirectional communication with parent applications using the LPC protocol. It covers accessing data, updating hover states, creating and editing events, loading data from URL hashes, and generating shareable links.
```typescript
import { useMarkwhenStore } from "./Markwhen/markwhenStore";
import { parse } from "@markwhen/parser";
// Initialize store in component
const markwhenStore = useMarkwhenStore();
// Access parsed timeline data
const timeline = markwhenStore.markwhen?.parsed;
const events = markwhenStore.markwhen?.transformed;
// Set hovering state for event highlighting
markwhenStore.setHoveringPath({ type: "pageFilePath", path: "page/0/1/2" });
// Create new event from date range
markwhenStore.createEventFromRange(
{
fromDateTimeIso: "2024-01-15T10:00:00.000Z",
toDateTimeIso: "2024-01-15T12:00:00.000Z"
},
"hour",
true
);
// Edit existing event's date range
markwhenStore.editEventDateRange(
{ type: "pageFilePath", path: "page/0/1" },
{
fromDateTimeIso: "2024-01-20T09:00:00.000Z",
toDateTimeIso: "2024-01-20T17:00:00.000Z"
},
"hour",
undefined
);
// Load timeline from URL hash
// URL format: #mw=base64EncodedMarkwhenText
const encoded = btoa("2024-01-01: Project started\n2024-02-01: First milestone");
window.location.hash = `#mw=${encoded}`;
// Generate shareable links
console.log(markwhenStore.editorLink); // https://meridiem.markwhen.com/...
console.log(markwhenStore.timelineLink); // https://timeline.markwhen.com/...
console.log(markwhenStore.embedLink); //
```
--------------------------------
### Control Timeline Viewport and Scaling with Timeline Store (TypeScript)
Source: https://context7.com/mark-when/timeline/llms.txt
Explains how to use the timeline store for managing rendering states, including viewport positioning, zoom scale, date calculations, and adaptive time marker granularity. It covers adjusting the scale, converting between dates and pixel positions, setting the viewport, and navigating the timeline.
```typescript
import { useTimelineStore } from "./Timeline/timelineStore";
import { DateTime } from "luxon";
const timelineStore = useTimelineStore();
// Get current page scale (pixels per hour)
const scale = timelineStore.pageScale; // e.g., 100 means 100px = 1 hour
// Set new scale (clamped between MIN_SCALE and MAX_SCALE)
timelineStore.setPageScale(200); // Zoom in
// Convert between dates and pixel positions
const pixelOffset = timelineStore.distanceFromBaselineLeftmostDate(
DateTime.fromISO("2024-06-15T14:30:00")
);
// Get date from pixel position
const date = timelineStore.dateFromClientLeft(500);
console.log(date.toISO()); // DateTime at pixel 500
// Calculate viewport date interval
const viewport = {
left: 1000,
width: 1920,
top: 0,
height: 1080,
offsetLeft: 200
};
timelineStore.setViewport(viewport);
console.log(timelineStore.pageSettings.viewportDateInterval);
// { fromDateTime: DateTime, toDateTime: DateTime }
// Get current time scale for markers
console.log(timelineStore.scaleOfViewportDateInterval);
// "hour" | "day" | "month" | "year" | "decade" | "cent"
// Access weight values for marker density
const weights = timelineStore.weights; // [0.8, 0.6, 0.4, ...]
// Higher weight = more visible time markers at that scale
// Toggle between timeline and gantt view
timelineStore.setMode("gantt"); // or "timeline"
// Navigate to current date/time
timelineStore.goToNow();
// Jump to specific date range
timelineStore.setRange({
fromDateTime: DateTime.fromISO("2024-01-01"),
toDateTime: DateTime.fromISO("2024-12-31")
});
```
--------------------------------
### TypeScript: Vite Build Configuration for Single-File Output
Source: https://context7.com/mark-when/timeline/llms.txt
This Vite configuration is optimized for bundling the entire Vue application into a single HTML file with all assets (JavaScript, CSS, images) inlined. This simplifies distribution and embedding. It includes plugins for Vue and `vite-plugin-singlefile`, customizes the Vue compiler to allow `svg:style` tags, and sets up development server options.
```typescript
import { fileURLToPath, URL } from "node:url";
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import { viteSingleFile } from "vite-plugin-singlefile";
export default defineConfig({
plugins: [
vue({
template: {
compilerOptions: {
// Allow svg:style tags
isCustomElement: (tag) => tag.startsWith("svg:style"),
},
},
}),
viteSingleFile(), // Bundle into single index.html
],
resolve: {
alias: {
"@": fileURLToPath(new URL("./src", import.meta.url)),
},
},
server: {
port: 6180, // Meridiem scans ports 6180-6280 for mw.json
host: "0.0.0.0",
},
});
// Build command: npm run build
// Output: dist/index.html (single file with all assets inlined)
// File size typically: ~200-300KB
// Development server: npm run dev
// Runs on http://localhost:6180
// Auto-detected by Meridiem desktop/web app
```
--------------------------------
### TypeScript: Vue Router Configuration for Dynamic Timelines
Source: https://context7.com/mark-when/timeline/llms.txt
This configuration sets up Vue Router to handle dynamic routing for user-specific and timeline-specific URLs. It utilizes `createWebHistory` and supports optional user and timeline parameters, enabling shared timeline loading from the Meridiem platform. The root path is designed to handle direct access or hash-based data.
```typescript
import { createRouter, createWebHistory } from "vue-router";
import App from "../App.vue";
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: "/:user?/:timeline?",
name: "home",
component: App,
},
],
});
export default router;
// Route patterns:
// / - Root, expects LPC communication or hash
// /:user - Load user's default timeline (https://meridiem.markwhen.com/{user}.mw)
// /:user/:timeline - Load specific timeline (https://meridiem.markwhen.com/{user}/{timeline}.mw)
// /#mw=base64... - Load timeline from base64-encoded hash
// Example URLs:
// https://timeline.markwhen.com/
// https://timeline.markwhen.com/johndoe
// https://timeline.markwhen.com/johndoe/project-alpha
// https://timeline.markwhen.com/#mw=MjAyNC0wMS0wMSogUHJvamVjdCBzdGFydGVk
```
--------------------------------
### LPC Communication System for Timeline Integration
Source: https://context7.com/mark-when/timeline/llms.txt
Implements a bidirectional message-passing protocol using `useLpc` for timeline components to communicate with parent applications like Meridiem or VS Code. It supports `postMessage`, WebSocket, or VS Code API for message exchange. The system handles state updates, command execution, and data requests.
```typescript
import { useLpc } from "./Markwhen/useLpc";
import type { AppState, MarkwhenState } from "./Markwhen/useLpc";
// Initialize LPC with message listeners
const { postRequest } = useLpc({
// Receive app state from parent
appState(state: AppState) {
console.log("Dark mode:", state.isDark);
console.log("Hovering path:", state.hoveringPath);
console.log("Detail path:", state.detailPath);
console.log("Color map:", state.colorMap);
},
// Receive parsed markwhen content
markwhenState(state: MarkwhenState) {
console.log("Raw text:", state.rawText);
console.log("Parsed timeline:", state.parsed);
console.log("Events:", state.transformed);
},
// Receive jump-to-path command
jumpToPath({ path }) {
console.log("Jump to event:", path);
// Scroll timeline to show this event
},
// Receive jump-to-range command
jumpToRange({ dateRangeIso }) {
console.log("Jump to date range:", dateRangeIso);
// Zoom timeline to show this range
},
// Handle SVG export request
getSvg(params: any) {
// Generate and return SVG of current timeline view
return "";
}
});
// Send requests to parent application
await postRequest("setHoveringPath", {
type: "pageFilePath",
path: "page/0/1/2"
});
await postRequest("setDetailPath", {
type: "pageFilePath",
path: "page/0/3"
});
await postRequest("setText", {
text: "2024-06-15: New event added",
at: { from: 150, to: 150 } // Insert at character position 150
});
await postRequest("newEvent", {
dateRangeIso: {
fromDateTimeIso: "2024-07-01T09:00:00.000Z",
toDateTimeIso: "2024-07-01T17:00:00.000Z"
},
granularity: "hour",
immediate: true
});
await postRequest("editEventDateRange", {
path: { type: "pageFilePath", path: "page/0/2" },
range: {
fromDateTimeIso: "2024-08-15T10:00:00.000Z",
toDateTimeIso: "2024-08-15T18:00:00.000Z"
},
scale: "hour",
preferredInterpolationFormat: undefined
});
// Request state updates from parent
await postRequest("appState");
await postRequest("markwhenState");
// WebSocket connection (if __markwhen_wss_url is set)
// window.__markwhen_wss_url = "ws://localhost:3000";
// LPC will automatically use WebSocket instead of postMessage
// VS Code extension integration
// window.acquireVsCodeApi is automatically detected and used
```
--------------------------------
### Gesture Handling Composable for Timeline Interaction
Source: https://context7.com/mark-when/timeline/llms.txt
Manages pinch-to-zoom and pan gestures for touch devices and trackpads in a timeline component. It utilizes a composable to handle gesture detection and state management, providing smooth interaction. Dependencies include Vue's reactivity system and potentially HammerJS or custom wheel handlers.
```typescript
import { useGestures } from "./Timeline/composables/useGestures";
import { ref, onMounted } from "vue";
const timelineElement = ref();
const { isZooming, isPanning } = useGestures(timelineElement, () => {
// Optional callback when scale changes
console.log("Scale updated, trigger re-render");
});
// Check gesture states
console.log(isZooming.value); // true when pinch-zooming
console.log(isPanning.value); // true when panning
// The composable automatically handles:
// - Pinch-to-zoom on touch devices (2+ fingers)
// - Trackpad pinch gestures
// - Mouse wheel zoom (via zoomer utility)
// - Two-finger pan on touch devices
// - Maintains zoom center at gesture point
// - Respects MAX_SCALE limits
onMounted(() => {
// Gestures are automatically set up when element is mounted
// HammerJS and custom wheel handler are initialized
});
// To temporarily disable panning:
import { useCanPanStore } from "./Timeline/composables/canPan";
const canPan = useCanPanStore();
canPan.canPan = false; // Disable panning
canPan.canPan = true; // Re-enable panning
```
--------------------------------
### Date/Time Manipulation and Formatting Utilities (TypeScript)
Source: https://context7.com/mark-when/timeline/llms.txt
Utility functions for manipulating DateTime objects, including flooring to specific scales (hour, day, decade), ceiling to the next boundary, and rounding to the nearest scale. It also formats date ranges into human-readable strings and determines the appropriate scale for a given duration. Dependencies include luxon and custom format constants.
```typescript
import {
floorDateTime,
ceilDateTime,
roundDateTime,
dateRangeToString,
humanDuration,
scaleForDuration
} from "./Timeline/utilities/dateTimeUtilities";
import { DateTime } from "luxon";
import { AMERICAN_DATE_FORMAT, EUROPEAN_DATE_FORMAT } from "@markwhen/parser";
// Floor datetime to specific scale
const dt = DateTime.fromISO("2024-06-15T14:37:42");
const floored = floorDateTime(dt, "hour");
console.log(floored.toISO()); // 2024-06-15T14:00:00.000Z
const flooredDay = floorDateTime(dt, "day");
console.log(flooredDay.toISO()); // 2024-06-15T00:00:00.000Z
const flooredDecade = floorDateTime(dt, "decade");
console.log(flooredDecade.toISO()); // 2020-01-01T00:00:00.000Z
// Ceiling datetime to next boundary
const ceiled = ceilDateTime(dt, "month");
console.log(ceiled.toISO()); // 2024-07-01T00:00:00.000Z
// Round to nearest scale boundary
const rounded = roundDateTime(dt, "quarterhour");
console.log(rounded.toISO()); // 2024-06-15T14:30:00.000Z or 14:45:00
// Format date range as string
const range = {
fromDateTime: DateTime.fromISO("2024-01-01T00:00:00"),
toDateTime: DateTime.fromISO("2024-01-31T23:59:59")
};
const formatted = dateRangeToString(range, "day", AMERICAN_DATE_FORMAT);
console.log(formatted); // "1/1/2024 - 1/31/2024"
const formattedEU = dateRangeToString(range, "day", EUROPEAN_DATE_FORMAT);
console.log(formattedEU); // "1/1/2024 - 31/1/2024"
// Human-readable duration
const duration = humanDuration({
fromDateTime: DateTime.fromISO("2024-01-01"),
toDateTime: DateTime.fromISO("2024-01-15")
});
console.log(duration); // "2 weeks"
const shortDuration = humanDuration({
fromDateTime: DateTime.now(),
toDateTime: DateTime.now().plus({ minutes: 45 })
});
console.log(shortDuration); // "45 minutes"
// Determine appropriate scale for duration
const scale1 = scaleForDuration({
fromDateTime: DateTime.now(),
toDateTime: DateTime.now().plus({ hours: 3 })
});
console.log(scale1); // "hour"
const scale2 = scaleForDuration({
fromDateTime: DateTime.now(),
toDateTime: DateTime.now().plus({ days: 45 })
});
console.log(scale2); // "month"
```
--------------------------------
### Interactive Event Creation Composable (Vue.js/TypeScript)
Source: https://context7.com/mark-when/timeline/llms.txt
A Vue.js composable (`useCreateEvent`) that enables interactive event creation on a timeline component using mouse or touch gestures. It handles drag-to-create functionality, tracks the event's position, and manages the creation state. It requires Vue's reactivity system and DOM event listeners.
```typescript
import { useCreateEvent } from "./Timeline/Events/NewEvent/composables/useCreateEvent";
import { ref, onMounted } from "vue";
// In a Vue component
const { mouseDownTouchStartListener, newEventPosition, creating } = useCreateEvent();
// Attach to timeline element
const timelineRef = ref();
onMounted(() => {
timelineRef.value?.addEventListener("mousedown", mouseDownTouchStartListener);
timelineRef.value?.addEventListener("touchstart", mouseDownTouchStartListener);
});
// Check if currently creating an event
console.log(creating.value); // boolean
// Get position of event being created
if (creating.value && newEventPosition.value) {
const [start, end] = newEventPosition.value;
console.log(`Creating event from ${start.dateTime.toISO()} to ${end.dateTime.toISO()}`);
console.log(`Visual position: ${start.left}px to ${end.left}px`);
}
// The composable automatically:
// 1. Captures mousedown/touchstart to begin event creation
// 2. Tracks mousemove/touchmove to extend the event range
// 3. Creates the event on mouseup/touchend
// 4. Cancels creation on Escape key
// 5. Disables panning while creating
// Example integration in template:
//
//
//
```
--------------------------------
### TypeScript: Calculate Viewport Dimensions and Scroll Position
Source: https://context7.com/mark-when/timeline/llms.txt
This utility hook extracts current viewport dimensions and scroll position from a timeline container element. It's crucial for rendering visible events and time markers accurately. It uses Vue's reactivity system and ResizeObserver for efficient updates.
```typescript
import { useViewport } from "./Timeline/Events/composables/useViewport";
import { ref } from "vue";
const timelineElement = ref(null);
// Get current viewport information
const viewport = useViewport(timelineElement);
console.log(viewport.left); // Scroll position from left edge
console.log(viewport.width); // Visible width of timeline
console.log(viewport.top); // Vertical scroll position
// Example usage in scroll handler
const onScroll = () => {
const vp = useViewport(timelineElement);
// Update timeline store with new viewport
timelineStore.setViewport({
left: vp.left,
width: vp.width,
top: vp.top,
height: timelineElement.value?.clientHeight || 0,
offsetLeft: timelineElement.value?.offsetLeft || 0
});
// Viewport automatically updates viewportDateInterval
// which triggers re-rendering of visible time markers and events
};
// Typical integration in component
import { watchEffect } from "vue";
watchEffect(() => {
if (!timelineElement.value) return;
const observer = new ResizeObserver(() => {
const vp = useViewport(timelineElement);
timelineStore.setViewport({
...vp,
height: timelineElement.value!.clientHeight,
offsetLeft: timelineElement.value!.offsetLeft
});
});
observer.observe(timelineElement.value);
return () => observer.disconnect();
});
```
=== COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.