### KeystrokeOverride Examples Source: https://github.com/mark-when/view-client/blob/main/_autodocs/types.md Examples demonstrating how to configure keystroke overrides for local handling or proxying to the parent window. ```typescript { combo: "meta+k", action: "skip" } // Cmd+K handled locally { combo: "ctrl+p", action: "proxy" } // Ctrl+P forwarded to parent ``` -------------------------------- ### Basic useLpc Usage Example Source: https://github.com/mark-when/view-client/blob/main/_autodocs/api-reference/useLpc.md Demonstrates how to import and use the useLpc hook with message listeners and options. Includes sending a request and cleaning up the connection. ```typescript import { useLpc } from "@markwhen/view-client" const { postRequest, close } = useLpc({ markwhenState(state) { console.log("Editor state changed:", state.parsed) }, appState(state) { console.log("App theme:", state.isDark ? "dark" : "light") } }, { proxyKeystrokes: true, preventDefaultOnProxy: true }) // Send a request and wait for response const result = await postRequest("newEvent", { dateRangeIso: { startDate: "2024-01-01" }, immediate: false }) // Clean up on unmount close() ``` -------------------------------- ### LPC Setup with Initial State Handling Source: https://github.com/mark-when/view-client/blob/main/_autodocs/configuration.md Configures LPC to handle initial application state and disables keystroke proxying. Dark mode is derived from the timeline content. ```typescript const { postRequest } = useLpc({ markwhenState(state) { updateTimeline(state.parsed) }, appState(state) { setDarkMode(state.isDark) } }, { initialAppStateFromMarkwhen(state) { return { isDark: state.parsed.events?.length === 0 } }, proxyKeystrokes: false }) ``` -------------------------------- ### equivalentPaths Usage Examples Source: https://github.com/mark-when/view-client/blob/main/_autodocs/api-reference/paths.md Demonstrates how to use the equivalentPaths function to compare EventPath arrays for equality. Handles defined, undefined, and empty paths. ```typescript import { equivalentPaths } from "@markwhen/view-client" equivalentPaths([0], [0]) // true equivalentPaths([0, 1], [0, 1]) // true equivalentPaths([0, 1], [0, 2]) // false equivalentPaths([0], undefined) // false equivalentPaths(undefined, undefined) // false equivalentPaths([], []) // true ``` -------------------------------- ### Listener Pattern Example Source: https://github.com/mark-when/view-client/blob/main/_autodocs/messages.md Demonstrates how to register listeners to handle incoming messages from the host. The `useLpc` hook provides the `postRequest` function and allows defining callbacks for specific message types. ```javascript const listeners = { appState(state) { // Handle app state change }, markwhenState(state) { // Handle timeline update }, keystroke(payload) { // Handle keystroke (if listener is registered) } } const { postRequest } = useLpc(listeners) ``` -------------------------------- ### Minimal LPC Setup Source: https://github.com/mark-when/view-client/blob/main/_autodocs/configuration.md Sets up the Local PostMessage Communication (LPC) with default settings. The library automatically detects the communication channel and proxies keystrokes. ```typescript const { postRequest } = useLpc() ``` -------------------------------- ### Initialize View Host Communication Source: https://github.com/mark-when/view-client/blob/main/_autodocs/00-START-HERE.txt Initialize communication with the view host using the `useLpc` hook. This setup includes defining callbacks for handling timeline updates (`markwhenState`) and application state changes (`appState`). ```typescript import { useLpc } from "@markwhen/view-client" const { postRequest, close } = useLpc({ markwhenState(state) { /* handle timeline update */ }, appState(state) { /* handle app state change */ } }) await postRequest("newEvent", { dateRangeIso: {...}, immediate: false }) close() // Cleanup on unmount ``` -------------------------------- ### LPC Setup with Keystroke Customization Source: https://github.com/mark-when/view-client/blob/main/_autodocs/configuration.md Configures LPC to customize keystroke handling. Specific key combinations are handled locally, while others are proxied with default event prevention. ```typescript const { postRequest } = useLpc(listeners, { keystrokeOverrides: [ { combo: "meta+k", action: "skip" }, { combo: "meta+Enter", action: "skip" } ], preventDefaultOnProxy: true }) ``` -------------------------------- ### Usage Example for getNonce Source: https://github.com/mark-when/view-client/blob/main/_autodocs/api-reference/getNonce.md Import and use getNonce to create a unique request ID. This is useful for internal message identification. ```typescript import { getNonce } from "@markwhen/view-client" const requestId = `request_${getNonce()}` // Generates: "request_aBcDeFgHiJkLmNoPqRsTuVwXyZ12345" ``` -------------------------------- ### setText Request with Range Source: https://github.com/mark-when/view-client/blob/main/_autodocs/messages.md Example of using the 'setText' message to replace a specific range of text in the editor. The 'at' parameter specifies the start and end positions. ```typescript await postRequest("setText", { text: "new text", at: { from: 50, to: 100 } }) ``` -------------------------------- ### Request-Response Pattern Example Source: https://github.com/mark-when/view-client/blob/main/_autodocs/messages.md Illustrates the request-response pattern where a request message with a unique ID is sent, and a response with the same ID is expected back. The `postRequest` function facilitates this by returning a Promise. ```typescript // Request { type: "newEvent", request: true, id: "markwhen_abc123xyz...", params: { ... } } // Response (from host) { type: "newEvent", response: true, id: "markwhen_abc123xyz...", // Same ID params: { ... } // Response data } ``` ```javascript const response = await postRequest("newEvent", params) ``` -------------------------------- ### Jump to Range Message Source: https://github.com/mark-when/view-client/blob/main/_autodocs/messages.md Navigates the view to a specified date range. The view will center on the provided start and end dates. ```typescript { type: "jumpToRange", request: true, id: "markwhen_...", params: { dateRangeIso: { startDate: "2024-03-01", endDate: "2024-03-31" } } } ``` -------------------------------- ### Bulk Text Update in Editor Source: https://github.com/mark-when/view-client/blob/main/_autodocs/examples.md Replace a large section of text in the editor by specifying the start and end positions and the new content. Ensure you have the `useLpc` hook and `getPositionOfLine` function available. ```typescript const { postRequest } = useLpc(listeners) async function refactorTimelineSection( fromPosition: number, toPosition: number, newContent: string ) { await postRequest("setText", { text: newContent, at: { from: fromPosition, to: toPosition } }) } // Usage: replace lines 100-200 with updated content const lineStart = getPositionOfLine(100) const lineEnd = getPositionOfLine(200) refactorTimelineSection(lineStart, lineEnd, "new event\nmore content") ``` -------------------------------- ### Get Nonce Function Signature Source: https://github.com/mark-when/view-client/blob/main/_autodocs/api-reference/getNonce.md The getNonce function returns a string. ```typescript export const getNonce = () => string ``` -------------------------------- ### editEventDateRange Source: https://github.com/mark-when/view-client/blob/main/_autodocs/messages.md Modifies an event's date range. This allows for updating the start and end dates of an existing event. ```APIDOC ## editEventDateRange ### Description Modify an event's date range. ### Method POST (implied by postRequest) ### Endpoint N/A (Client-side method) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **path** (EventPath) - Required - Path to the event to edit. - **range** (DateRangeIso) - Required - New date range. - **scale** (DisplayScale) - Required - Time scale (second, minute, hour, day, month, year, decade). - **preferredInterpolationFormat** (DateFormat?) - Optional - Suggested format for interpolation. ### Request Example ```json { "type": "editEventDateRange", "request": true, "id": "markwhen_...", "params": { "path": [0, 1], "range": { "startDate": "2024-02-01", "endDate": "2024-02-15" }, "scale": "day", "preferredInterpolationFormat": "YYYY-MM-DD" } } ``` ### Response #### Success Response (200) Response data will be returned in the `params` field of a response message with a matching ID. #### Response Example ```json { "type": "editEventDateRange", "response": true, "id": "markwhen_...", "params": { ... } } ``` ``` -------------------------------- ### Initialize Markwhen View Client with Default Settings Source: https://github.com/mark-when/view-client/blob/main/_autodocs/examples.md Initializes the library with default settings and sets up message listeners for timeline and application state changes. Call the close function when the component unmounts. ```typescript import { useLpc } from "@markwhen/view-client" const { postRequest, close } = useLpc({ markwhenState(state) { console.log("Timeline loaded:", state.parsed) }, appState(state) { console.log("App state:", state) } }) // On component unmount close() ``` -------------------------------- ### Initialize useLpc Hook Source: https://github.com/mark-when/view-client/blob/main/README.md Use the `useLpc` hook to establish communication with the parent window. Configure callbacks for state changes and handle keystroke overrides. ```javascript import { useLpc } from "@markwhen/view-client" const { postRequest } = useLpc({ markwhenState(ms) { // When there is some state change from the editor console.log(ms) } appState(newState) { // When the app state changes (dark mode, hovering event, selected event, etc) console.log(newState) } }, { // Proxy all keystrokes by default; override specific combos when you want // to handle them inside the view instead of the parent window keystrokeOverrides: [ { combo: "meta+k", action: "skip" }, { combo: "ctrl+p", action: "proxy" } ], // Prevent default in the iframe when proxying (default: true) preventDefaultOnProxy: true }) ``` -------------------------------- ### Generate Message ID with getNonce Source: https://github.com/mark-when/view-client/blob/main/_autodocs/messages.md Message IDs are generated using the getNonce() function. IDs starting with 'markwhen_' are recognized as belonging to this library. ```typescript const id = `markwhen_${getNonce()}` // e.g., "markwhen_aBcDeFgHiJkLmNoPqRsT" ``` -------------------------------- ### UseLpcOptions Interface Source: https://github.com/mark-when/view-client/blob/main/_autodocs/QUICK-REFERENCE.md Defines the configuration options available when initializing the useLpc hook. This includes settings for keystroke handling and an optional function to map initial app state. ```typescript interface UseLpcOptions { proxyKeystrokes?: boolean // default: true keystrokeOverrides?: KeystrokeOverride[] preventDefaultOnProxy?: boolean // default: true initialAppStateFromMarkwhen?: (state: MarkwhenState) => Partial } ``` -------------------------------- ### Initial State Rendering with Markwhen View Client Source: https://github.com/mark-when/view-client/blob/main/_autodocs/examples.md Render the timeline with pre-loaded data before the WebSocket connection is established. The library calls the markwhenState listener immediately if initial state exists in window scope. ```typescript import type { MarkwhenState } from "@markwhen/view-client" const { postRequest } = useLpc({ markwhenState(state) { renderTimeline(state) } }, { // If initial state exists in window scope, derive app state from it initialAppStateFromMarkwhen(state: MarkwhenState) { return { isDark: state.parsed.events?.length === 0, path: "/" } } }) // The library calls markwhenState listener immediately if // window.__markwhen_initial_state is defined, before waiting // for WebSocket connection ``` -------------------------------- ### Initialize LPC Client Source: https://github.com/mark-when/view-client/blob/main/_autodocs/QUICK-REFERENCE.md Initialize the Local Post Communication (LPC) client by providing listener functions for 'markwhenState' and 'appState' updates. Configure options like keystroke proxying and default prevention. Remember to call the 'close' function on cleanup. ```typescript const { postRequest, close } = useLpc( { markwhenState(state) { /* handle timeline */ }, appState(state) { /* handle app state */ } }, { proxyKeystrokes: true, preventDefaultOnProxy: true, keystrokeOverrides: [ { combo: "meta+k", action: "skip" } ] } ) // On cleanup close() ``` -------------------------------- ### Global Variables Provided by Host Source: https://github.com/mark-when/view-client/blob/main/_autodocs/QUICK-REFERENCE.md Information about global variables that are set by the host environment. `__markwhen_wss_url` provides the WebSocket URL, and `__markwhen_initial_state` offers pre-loaded Markwhen state. `acquireVsCodeApi` is provided by VS Code for communication. ```typescript // Set by host window.__markwhen_wss_url // string? — WebSocket URL window.__markwhen_initial_state // MarkwhenState? — Pre-loaded state // Provided by VS Code window.acquireVsCodeApi() // () => { postMessage: ... } ``` -------------------------------- ### Initialize LPC Communication and Send Messages Source: https://github.com/mark-when/view-client/blob/main/_autodocs/README.md Initializes LPC communication with optional listeners and configuration. Use postRequest to send messages to the host and close to clean up resources. ```typescript import { useLpc } from "@markwhen/view-client" // Initialize communication const { postRequest, close } = useLpc( { // Listeners for messages from the host markwhenState(state) { console.log("Timeline updated:", state.parsed) }, appState(state) { console.log("App state changed:", state) } }, { // Configuration proxyKeystrokes: true, preventDefaultOnProxy: true, keystrokeOverrides: [ { combo: "meta+k", action: "skip" } // Handle Cmd+K locally ] } ) // Send messages to the host await postRequest("newEvent", { dateRangeIso: { startDate: "2024-01-01" }, immediate: false }) // Clean up on unmount close() ``` -------------------------------- ### Keystroke Proxying Configuration Source: https://github.com/mark-when/view-client/blob/main/_autodocs/api-reference/useLpc.md Shows how to customize keystroke proxying behavior using the `keystrokeOverrides` option within useLpc. ```typescript const { postRequest } = useLpc(listeners, { keystrokeOverrides: [ { combo: "meta+k", action: "skip" }, // Don't proxy Cmd+K { combo: "ctrl+p", action: "proxy" } // Do proxy Ctrl+P ] }) ``` -------------------------------- ### Import Core Functions and Types Source: https://github.com/mark-when/view-client/blob/main/_autodocs/QUICK-REFERENCE.md Import the necessary hooks and utility functions from the @markwhen/view-client library. This includes functions for handling communication and comparing paths, as well as types for application state. ```typescript import { useLpc, getNonce, equivalentPaths } from "@markwhen/view-client" import type { AppState, MarkwhenState, EventPath, DisplayScale } from "@markwhen/view-client" ``` -------------------------------- ### Initial State Structure Source: https://github.com/mark-when/view-client/blob/main/_autodocs/configuration.md Defines the structure for `window.__markwhen_initial_state`, which allows for synchronous rendering of timelines before communication is established. ```typescript window.__markwhen_initial_state = { rawText: "title: My Timeline\n...", parsed: { events: [...], tags: {} }, transformed: { name: "root", events: [...] } } ``` -------------------------------- ### Listen for Markwhen and App State Updates Source: https://github.com/mark-when/view-client/blob/main/_autodocs/QUICK-REFERENCE.md Configure the 'useLpc' hook to listen for state updates. The 'markwhenState' listener receives raw text, parsed results, and transformed event data. The 'appState' listener provides information like UI state, hover/detail paths, and color mappings. ```typescript useLpc({ markwhenState(state) { state.rawText // string? state.parsed // ParseResult from @markwhen/parser state.transformed // Sourced? }, appState(state) { state.key // string? state.isDark // boolean? state.hoveringPath // EventPath? state.detailPath // EventPath? state.path // string? state.colorMap // Record> } }) ``` -------------------------------- ### useLpc Hook Source: https://github.com/mark-when/view-client/blob/main/_autodocs/api-reference/useLpc.md Initializes LPC communication and returns methods for sending requests and closing the connection. ```APIDOC ## useLpc ### Description Initializes local-procedure-call (LPC) communication between a Markwhen view and its hosting application. It allows for sending messages to the host and receiving messages from it. ### Function Signature ```typescript export const useLpc = ( listeners?: MessageListeners, options?: UseLpcOptions ) => { postRequest: Function; close: Function } ``` ### Parameters #### listeners - **Type**: `MessageListeners` (Object) - **Required**: No - **Description**: An object where keys are message types (e.g., `appState`, `markwhenState`, `setHoveringPath`) and values are callback functions that handle incoming messages and their parameters. #### options - **Type**: `UseLpcOptions` (Object) - **Required**: No - **Description**: A configuration object for customizing LPC behavior. Refer to `configuration.md` for detailed options. ### Return Type An object containing two methods: - **postRequest** - **Type**: `(type: T, params?: MessageParam) => Promise` - **Description**: Sends a request message of a specific `type` with optional `params` to the parent application. It returns a Promise that resolves with the response from the host. A unique ID is assigned to each request. - **close** - **Type**: `() => void` - **Description**: Removes all event listeners and terminates any active WebSocket connections. This is typically called automatically when the view unmounts or when communication needs to be explicitly stopped. ### Message Types These message types can be sent via `postRequest` or received in the `listeners` object: | Message Type | Direction | Params Type | Description | |-------------------|-------------|--------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------| | `appState` | Receive | `AppState` | Indicates changes in the hosting app's state (e.g., dark mode, selection). | `markwhenState` | Receive | `MarkwhenState` | Reflects changes in the Markwhen editor's state or parsed content. | `setHoveringPath` | Send/Receive| `EventPath` | Sets or receives the currently hovered event path. | `setDetailPath` | Send/Receive| `EventPath` | Sets or receives the event path for which detailed view is shown. | `setText` | Send/Receive| `{ text: string; at?: { from: number; to: number } }` | Updates the editor text, optionally at a specified range. | `showInEditor` | Send/Receive| `EventPath` | Scrolls the editor to display a specific event path. | `newEvent` | Send/Receive| `{ dateRangeIso: DateRangeIso; granularity?: DateTimeGranularity; immediate: boolean }` | Creates a new event with a specified date range. | `editEventDateRange`| Send/Receive| `{ path: EventPath; range: DateRangeIso; scale: DisplayScale; preferredInterpolationFormat: DateFormat | undefined }` | Edits the date range of an event. | `jumpToPath` | Send/Receive| `{ path: EventPath }` | Navigates the view to a specific event path. | `jumpToRange` | Send/Receive| `{ dateRangeIso: DateRangeIso }` | Navigates the view to a specified date range. | `keystroke` | Send | `KeystrokePayload` | Proxies keyboard events from the view to the host. ### Communication Channels The function automatically prioritizes and uses the following communication channels: 1. **WebSocket**: Used if `window.__markwhen_wss_url` is defined (for server-side hosting). 2. **VS Code API**: Used if `acquireVsCodeApi` is available (for VS Code extensions). 3. **Parent Window postMessage**: Used if the view is embedded in an iframe (for web-based hosting). 4. **None**: Logs an error if no communication channels are detected. ### Basic Usage Example ```typescript import { useLpc } from "@markwhen/view-client" const { postRequest, close } = useLpc({ markwhenState(state) { console.log("Editor state changed:", state.parsed) }, appState(state) { console.log("App theme:", state.isDark ? "dark" : "light") } }, { proxyKeystrokes: true, preventDefaultOnProxy: true }) // Send a request and wait for response const result = await postRequest("newEvent", { dateRangeIso: { startDate: "2024-01-01" }, immediate: false }) // Clean up on unmount close() ``` ### Keystroke Proxying Keystrokes are proxied to the parent window by default. This behavior can be customized using `keystrokeOverrides` in the `options` object: ```typescript const { postRequest } = useLpc(listeners, { keystrokeOverrides: [ { combo: "meta+k", action: "skip" }, // Don't proxy Cmd+K { combo: "ctrl+p", action: "proxy" } // Do proxy Ctrl+P ] }) ``` - **Key combo format**: Modifiers (`meta`, `ctrl`, `alt`, `shift`) separated by `+`, followed by the key (e.g., `"ctrl+shift+a"`). - **`preventDefaultOnProxy`**: When `true` (default), the browser's default action for proxied keystrokes is prevented. ### Initial State Handling If `window.__markwhen_initial_state` is defined, it's passed to the `markwhenState` listener immediately upon page load, enabling the view to render with initial content before a connection is fully established. The `initialAppStateFromMarkwhen` option can be used to derive app state from this initial Markwhen state: ```typescript const { postRequest } = useLpc(listeners, { initialAppStateFromMarkwhen(state) { return { isDark: false } // Example: Set initial theme to light } }) ``` ``` -------------------------------- ### Create New Event Source: https://github.com/mark-when/view-client/blob/main/_autodocs/examples.md Handles user actions to create a new event. The `immediate: false` option lets the host prompt the user for confirmation. Logs the response or any errors. ```typescript const { postRequest } = useLpc(listeners) async function createEventFromDialog(startDate: string) { try { const response = await postRequest("newEvent", { dateRangeIso: { startDate }, granularity: "day", immediate: false // Let host prompt user to confirm }) console.log("Event created:", response) } catch (error) { console.error("Failed to create event:", error) } } // Usage in button click handler button.addEventListener("click", () => { createEventFromDialog("2024-06-01") }) ``` -------------------------------- ### Navigation to Events and Dates Source: https://github.com/mark-when/view-client/blob/main/_autodocs/examples.md Jump to specific events or date ranges within the Markwhen view. This requires importing `EventPath` and using the `useLpc` hook. ```typescript import type { EventPath } from "@markwhen/view-client" const { postRequest } = useLpc(listeners) async function navigateToEvent(path: EventPath) { // Highlight and scroll to event await postRequest("jumpToPath", { path }) } async function navigateToDate(date: string) { // Scroll to show date range await postRequest("jumpToRange", { dateRangeIso: { startDate: date } }) } // Usage navigateToEvent([2, 1]) // Jump to specific event navigateToDate("2024-12-25") // Jump to Christmas ``` -------------------------------- ### useLpc Source: https://github.com/mark-when/view-client/blob/main/_autodocs/README.md Initializes Local-Procedure-Call (LPC) communication. It sets up listeners for incoming messages from the host and returns functions to send requests and close the connection. ```APIDOC ## useLpc(listeners?, options?) ### Description Initializes LPC communication with message listeners and configuration options. This is the main function to establish communication between a markwhen timeline view and its hosting application. ### Method Function call ### Parameters #### Arguments - **listeners** (object) - Optional - An object where keys are message types and values are callback functions to handle incoming messages from the host. - **options** (object) - Optional - Configuration options for the LPC communication, such as keystroke proxying and overrides. ### Returns An object containing: - **postRequest**: A function to send requests to the host. - **close**: A function to close the communication channel. ### Example ```typescript import { useLpc } from "@markwhen/view-client" const { postRequest, close } = useLpc( { markwhenState(state) { console.log("Timeline updated:", state.parsed) }, appState(state) { console.log("App state changed:", state) } }, { proxyKeystrokes: true } ) await postRequest("newEvent", { dateRangeIso: { startDate: "2024-01-01" }, immediate: false }) close() ``` ``` -------------------------------- ### Derive AppState from MarkwhenState Source: https://github.com/mark-when/view-client/blob/main/_autodocs/configuration.md Use `initialAppStateFromMarkwhen` to create an initial application state based on the loaded Markwhen state. This function is called with the initial Markwhen state and its return value is used to set the initial AppState. ```typescript const { postRequest } = useLpc({ markwhenState(state) { /* ... */ }, appState(state) { /* ... */ } }, { initialAppStateFromMarkwhen(state) { // Derive app state from markwhen state return { isDark: state.parsed.tags?.includes("dark") ?? false, path: "/timeline" } } }) ``` -------------------------------- ### React Integration with Markwhen View Client Source: https://github.com/mark-when/view-client/blob/main/_autodocs/examples.md Integrate the Markwhen View Client with React hooks using `useEffect` to manage the LPC instance and state. Handles loading states and applies CSS classes based on app state. ```typescript import { useEffect, useState } from "react" import { useLpc } from "@markwhen/view-client" import type { MarkwhenState, AppState } from "@markwhen/view-client" export function TimelineView() { const [markwhenState, setMarkwhenState] = useState(null) const [appState, setAppState] = useState(null) useEffect(() => { const { postRequest, close } = useLpc({ markwhenState: setMarkwhenState, appState: setAppState }) // Store for use in handlers window.timelinePostRequest = postRequest return close }, []) if (!markwhenState) return
Loading...
return (
{/* Render timeline */}
) } ``` -------------------------------- ### Path Comparison Utility in Markwhen View Client Source: https://github.com/mark-when/view-client/blob/main/_autodocs/examples.md Use the `equivalentPaths` utility to track event selection and toggle deselection. This function compares two event paths for equivalence. ```typescript import { equivalentPaths } from "@markwhen/view-client" import type { EventPath } from "@markwhen/view-client" let selectedPath: EventPath | undefined function selectEvent(path: EventPath) { if (equivalentPaths(path, selectedPath)) { // Already selected, deselect selectedPath = undefined } else { selectedPath = path } updateSelection(selectedPath) } // Usage in event handlers eventElement.addEventListener("click", () => { selectEvent(getPathFromElement(eventElement)) }) ``` -------------------------------- ### Select Event Detail via LPC Source: https://github.com/mark-when/view-client/blob/main/_autodocs/INDEX.md Sends a request to set the currently detailed event. Requires the event's path. ```typescript postRequest("setDetailPath", [0]) ``` -------------------------------- ### Configure Keystroke Handling Source: https://github.com/mark-when/view-client/blob/main/_autodocs/examples.md Customizes which keyboard shortcuts are handled locally ('skip') versus forwarded to the parent ('proxy'). `preventDefaultOnProxy` and `proxyKeystrokes` control the proxy behavior. ```typescript const { postRequest } = useLpc(listeners, { keystrokeOverrides: [ // Search: always forward to parent (Cmd+K / Ctrl+K opens search) { combo: "meta+k", action: "proxy" }, { combo: "ctrl+k", action: "proxy" }, // Copy/Paste: handle locally (default would proxy them) { combo: "meta+c", action: "skip" }, { combo: "meta+v", action: "skip" }, { combo: "ctrl+c", action: "skip" }, { combo: "ctrl+v", action: "skip" }, // Enter: always local in timeline editing { combo: "enter", action: "skip" } ], preventDefaultOnProxy: true, proxyKeystrokes: true }) ``` -------------------------------- ### AppState Interface Definition Source: https://github.com/mark-when/view-client/blob/main/_autodocs/types.md Represents the current state of the hosting application, including UI state and navigation information. Used for communication between the host app and the view. ```typescript export interface AppState { key?: string; isDark?: boolean; hoveringPath?: EventPath; detailPath?: EventPath; path?: string; colorMap: Record>; } ``` -------------------------------- ### jumpToPath Source: https://github.com/mark-when/view-client/blob/main/_autodocs/messages.md Navigates to a specific event, centering the view on it. This is useful for focusing the user's attention on a particular event. ```APIDOC ## jumpToPath ### Description Navigate to a specific event, centering view on it. ### Method POST (implied by postRequest) ### Endpoint N/A (Client-side method) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **path** (EventPath) - Required - Path to the event to navigate to. ### Request Example ```json { "type": "jumpToPath", "request": true, "id": "markwhen_...", "params": { "path": [1, 2, 0] } } ``` ### Response #### Success Response (200) Response data will be returned in the `params` field of a response message with a matching ID. #### Response Example ```json { "type": "jumpToPath", "response": true, "id": "markwhen_...", "params": { ... } } ``` ``` -------------------------------- ### keystroke Source: https://github.com/mark-when/view-client/blob/main/_autodocs/messages.md Proxies a keyboard event from the view to the host. This allows the host application to capture and process keyboard input from the view. ```APIDOC ## keystroke ### Description Proxied keyboard event from the view to the host. ### Method POST (implied by postRequest) ### Endpoint N/A (Client-side method) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **combo** (string) - Required - Normalized key combination (e.g., "ctrl+shift+a"). - **key** (string) - Required - Key value from KeyboardEvent.key (lowercase). - **code** (string) - Required - Physical key code (e.g., "KeyS", "Space"). - **altKey** (boolean) - Required - Alt modifier active. - **ctrlKey** (boolean) - Required - Ctrl modifier active. - **metaKey** (boolean) - Required - Meta/Cmd modifier active. - **shiftKey** (boolean) - Required - Shift modifier active. - **repeat** (boolean) - Required - Whether this is a repeated keypress. - **location** (number) - Required - Key location code (0=standard, 1=left, 2=right, 3=numpad). - **type** ("keydown" | "keyup") - Required - Event type. ### Request Example ```json { "type": "keystroke", "request": true, "id": "markwhen_...", "params": { "combo": "ctrl+s", "key": "s", "code": "KeyS", "altKey": false, "ctrlKey": true, "metaKey": false, "shiftKey": false, "repeat": false, "location": 0, "type": "keydown" } } ``` ### Response #### Success Response (200) Response data will be returned in the `params` field of a response message with a matching ID. #### Response Example ```json { "type": "keystroke", "response": true, "id": "markwhen_...", "params": { ... } } ``` **Note:** Keystroke handling is controlled by `proxyKeystrokes` and `keystrokeOverrides` options. Not sent if disabled. ``` -------------------------------- ### Error Handling for Markwhen View Client Communication Source: https://github.com/mark-when/view-client/blob/main/_autodocs/examples.md Handle communication failures gracefully by implementing try-catch blocks for rendering and request sending. Includes a timeout for requests. ```typescript const { postRequest, close } = useLpc({ markwhenState(state) { try { renderTimeline(state.parsed) } catch (error) { console.error("Failed to render timeline:", error) // Show fallback UI } } }) // Handle request errors async function safePostRequest(type: string, params: any) { try { const response = await Promise.race([ postRequest(type as any, params), new Promise((_, reject) => setTimeout(() => reject(new Error("Request timeout")), 5000) ) ]) return response } catch (error) { console.error(`Failed to send ${type}:`, error) // Fallback behavior return null } } ``` -------------------------------- ### LPC Keystroke Handling - Local Source: https://github.com/mark-when/view-client/blob/main/_autodocs/INDEX.md Configures how a specific keystroke combination is handled. Setting action to 'skip' means the event will be handled locally and not forwarded to the host. ```typescript { combo: "ctrl+k", action: "skip" } // Handle locally ``` -------------------------------- ### showInEditor Source: https://github.com/mark-when/view-client/blob/main/_autodocs/messages.md Scroll the editor to display a specific event. This is a request that expects a response. ```APIDOC ## showInEditor ### Description Scroll the editor to display a specific event. ### Method POST (or equivalent for LPC) ### Endpoint postRequest("showInEditor", params) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **(params)** (EventPath) - Path to the event to show ### Request Example ```json { "type": "showInEditor", "request": true, "id": "markwhen_...", "params": [0, 1] } ``` ### Response #### Success Response (200) - **type** (string) - "showInEditor" - **response** (boolean) - true - **id** (string) - Unique identifier matching the request #### Response Example ```json { "type": "showInEditor", "response": true, "id": "markwhen_..." } ``` ``` -------------------------------- ### React Integration with useState and useEffect Source: https://github.com/mark-when/view-client/blob/main/_autodocs/QUICK-REFERENCE.md Integrate the Markwhen view client into a React application using 'useState' to manage the timeline state and 'useEffect' to initialize the LPC client and handle its cleanup. The 'markwhenState' listener is used to update the React state. ```typescript const [state, setState] = useState(null) useEffect(() => { const { postRequest, close } = useLpc({ markwhenState: setState }) return close }, []) ``` -------------------------------- ### Configure Keystroke Overrides Source: https://github.com/mark-when/view-client/blob/main/_autodocs/configuration.md Use `keystrokeOverrides` to define specific actions for key combinations, overriding the global `proxyKeystrokes` setting. Each override specifies a key combination and whether to 'proxy' or 'skip' the event. ```typescript const { postRequest } = useLpc(listeners, { keystrokeOverrides: [ { combo: "meta+k", action: "skip" }, // Handle Cmd+K locally { combo: "ctrl+p", action: "proxy" }, // Forward Ctrl+P to parent { combo: "alt+shift+d", action: "skip" }, // Handle Alt+Shift+D locally ] }) ``` -------------------------------- ### Create New Event via LPC Source: https://github.com/mark-when/view-client/blob/main/_autodocs/INDEX.md Sends a request to create a new event in the timeline. Requires a date range in ISO format and an optional immediate flag. ```typescript postRequest("newEvent", { dateRangeIso: {...}, immediate: false }) ``` -------------------------------- ### markwhenState Source: https://github.com/mark-when/view-client/blob/main/_autodocs/messages.md Sent by the host to provide the parsed timeline content. This is a notification-only message. ```APIDOC ## markwhenState ### Description Sent by the host to provide the parsed timeline content. ### Method Listener ### Endpoint listeners.markwhenState(params) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **rawText** (string?) - Raw editor text - **parsed** (ParseResult) - Parsed timeline data structure - **transformed** (Sourced?) - Post-processed event tree ### Request Example ```json { "type": "markwhenState", "id": "markwhen_...", "params": { "rawText": "# Event\n- Date: Today\n- Desc: Test event", "parsed": { ... }, "transformed": { ... } } } ``` ### Response #### Success Response (200) None (notification only) #### Response Example None ``` -------------------------------- ### setDetailPath Source: https://github.com/mark-when/view-client/blob/main/_autodocs/messages.md Inform the host which event is showing detailed view. This is a request that expects a response. ```APIDOC ## setDetailPath ### Description Inform the host which event is showing detailed view. ### Method POST (or equivalent for LPC) ### Endpoint postRequest("setDetailPath", params) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **(params)** (EventPath) - Array of indices into event tree ### Request Example ```json { "type": "setDetailPath", "request": true, "id": "markwhen_...", "params": [0, 2] } ``` ### Response #### Success Response (200) - **type** (string) - "setDetailPath" - **response** (boolean) - true - **id** (string) - Unique identifier matching the request #### Response Example ```json { "type": "setDetailPath", "response": true, "id": "markwhen_..." } ``` ``` -------------------------------- ### Initialize LPC Connection Source: https://github.com/mark-when/view-client/blob/main/_autodocs/INDEX.md Initializes the Local-Procedure-Call (LPC) connection. It returns functions for sending requests and closing the connection. Ensure listeners and options are correctly configured before calling. ```typescript import { useLpc } from "@markwhen/view-client" const { postRequest, close } = useLpc(listeners, options) ``` -------------------------------- ### setHoveringPath Source: https://github.com/mark-when/view-client/blob/main/_autodocs/messages.md Inform the host which event is currently hovered in the view. This is a request that expects a response. ```APIDOC ## setHoveringPath ### Description Inform the host which event is currently hovered in the view. ### Method POST (or equivalent for LPC) ### Endpoint postRequest("setHoveringPath", params) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **(params)** (EventPath) - Array of indices into event tree ### Request Example ```json { "type": "setHoveringPath", "request": true, "id": "markwhen_...", "params": [0, 1] } ``` ### Response #### Success Response (200) - **type** (string) - "setHoveringPath" - **response** (boolean) - true - **id** (string) - Unique identifier matching the request #### Response Example ```json { "type": "setHoveringPath", "response": true, "id": "markwhen_..." } ``` ``` -------------------------------- ### Vue Integration with Markwhen State Source: https://github.com/mark-when/view-client/blob/main/_autodocs/QUICK-REFERENCE.md Integrates Markwhen state management into a Vue component using `ref` and lifecycle hooks. Ensure `useLpc` is imported. ```typescript const state = ref(null) onMounted(() => { lpc = useLpc({ markwhenState: (s) => state.value = s }) }) onUnmounted(() => { lpc.close() }) ``` -------------------------------- ### Vue 3 Integration with Markwhen View Client Source: https://github.com/mark-when/view-client/blob/main/_autodocs/examples.md Integrate the Markwhen View Client with Vue 3 Composition API using `ref`, `onMounted`, and `onUnmounted`. Manages reactive state for Markwhen and app state, and provides a postRequest function. ```typescript import { ref, onMounted, onUnmounted } from "vue" import { useLpc } from "@markwhen/view-client" import type { MarkwhenState, AppState } from "@markwhen/view-client" export function useTimelineClient() { const markwhenState = ref(null) const appState = ref(null) let lpc: ReturnType onMounted(() => { lpc = useLpc({ markwhenState: (state) => markwhenState.value = state, appState: (state) => appState.value = state }) }) onUnmounted(() => { lpc.close() }) return { markwhenState, appState, postRequest: () => lpc.postRequest } } ``` -------------------------------- ### showInEditor Request Source: https://github.com/mark-when/view-client/blob/main/_autodocs/messages.md Defines the 'showInEditor' message for scrolling the editor to display a specific event. Sent as a request from the view. ```typescript { type: "showInEditor", request: true, id: "markwhen_...", params: [0, 1] } ``` -------------------------------- ### LPC Keystroke Handling - Proxy Source: https://github.com/mark-when/view-client/blob/main/_autodocs/INDEX.md Configures how a specific keystroke combination is handled. Setting action to 'proxy' means the event will be forwarded to the host application. ```typescript { combo: "meta+s", action: "proxy" } // Forward to host ``` -------------------------------- ### setHoveringPath Request and Response Source: https://github.com/mark-when/view-client/blob/main/_autodocs/messages.md Defines the 'setHoveringPath' message for informing the host about the currently hovered event. Sent as a request from the view, with an optional response from the host. ```typescript // Message envelope (sent as request) { type: "setHoveringPath", request: true, id: "markwhen_...", params: [0, 1] // EventPath: array of indices } // Response from host { type: "setHoveringPath", response: true, id: "markwhen_..." } ``` -------------------------------- ### appState Source: https://github.com/mark-when/view-client/blob/main/_autodocs/messages.md Sent by the host to notify the view of application state changes. This is a notification-only message and does not expect a response. ```APIDOC ## appState ### Description Sent by the host to notify the view of application state changes. ### Method Listener ### Endpoint listeners.appState(params) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **key** (string?) - Unique identifier for the app instance - **isDark** (boolean?) - Whether dark mode is active - **hoveringPath** (EventPath?) - Path of the currently hovered event - **detailPath** (EventPath?) - Path of the event showing in detail - **path** (string?) - Current navigation path/URL - **colorMap** (Record>) - Nested color mappings ### Request Example ```json { "type": "appState", "id": "markwhen_...", "params": { "key": "app1", "isDark": true, "hoveringPath": [0, 1], "detailPath": [0], "path": "/", "colorMap": {} } } ``` ### Response #### Success Response (200) None (notification only) #### Response Example None ``` -------------------------------- ### MarkwhenState Interface Definition Source: https://github.com/mark-when/view-client/blob/main/_autodocs/types.md Represents the state of the markwhen editor and its parsed timeline data. Includes raw text, parsed results, and transformed event data. ```typescript export interface MarkwhenState { rawText?: string; parsed: ParseResult; transformed?: Sourced; } ``` -------------------------------- ### New Event Message Source: https://github.com/mark-when/view-client/blob/main/_autodocs/messages.md Represents a request to create a new event. Specify the date range, optional granularity, and whether to create immediately. ```typescript { type: "newEvent", request: true, id: "markwhen_...", params: { dateRangeIso: { startDate: "2024-01-15", endDate?: "2024-01-20" }, granularity?: "day", immediate: false } } ``` -------------------------------- ### KeystrokeAction and KeystrokeOverride Types Source: https://github.com/mark-when/view-client/blob/main/_autodocs/QUICK-REFERENCE.md Defines the possible actions for keystroke overrides ('proxy' or 'skip') and the structure for defining these overrides, including the key combination and the action to perform. ```typescript type KeystrokeAction = "proxy" | "skip" interface KeystrokeOverride { combo: string // "ctrl+k", "meta+s", "alt+shift+a" action: KeystrokeAction } ``` -------------------------------- ### Handle State Changes from Host Source: https://github.com/mark-when/view-client/blob/main/_autodocs/examples.md Updates the view based on state changes received from the host, including timeline data, raw text, theme, and event selections. Stores raw text in local storage if available. ```typescript import type { MarkwhenState, AppState } from "@markwhen/view-client" const { postRequest } = useLpc({ markwhenState(state: MarkwhenState) { // Update timeline display renderTimeline(state.parsed) // Store raw text if available if (state.rawText) { localStorage.setItem("timeline_text", state.rawText) } }, appState(state: AppState) { // Update theme if (state.isDark !== undefined) { document.documentElement.classList.toggle("dark", state.isDark) } // Handle selection changes if (state.hoveringPath) { highlightEvent(state.hoveringPath) } if (state.detailPath) { showEventDetails(state.detailPath) } } }) ```