CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-milkdown--ctx

Core context management module for Milkdown editor providing dependency injection and state management

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

plugin-orchestration.mddocs/

Plugin Orchestration

Central orchestration system providing unified access to containers, clocks, and debugging capabilities. The Ctx class serves as the main interface for plugin development, state coordination, and timer management within the Milkdown ecosystem.

Capabilities

Ctx Class

Main orchestration class that coordinates access to containers, clocks, and provides debugging capabilities. Essential for plugin development and managing the complete context lifecycle.

/**
 * Ctx provides unified access to containers, clocks, and debugging tools
 */
class Ctx {
  /** Metadata associated with this context (readonly) */
  readonly meta: Meta | undefined;
  /** Inspector instance for debugging and telemetry (readonly) */
  readonly inspector: Inspector | undefined;
  
  /** Create a ctx object with container, clock, and optional metadata */
  constructor(container: Container, clock: Clock, meta?: Meta);
  
  /** Create a new ctx with additional metadata, returns same instance if empty meta */
  readonly produce: (meta?: Meta) => Ctx;
  
  /** Add a slice into the ctx with optional initial value */
  readonly inject: <T>(sliceType: SliceType<T>, value?: T) => Ctx;
  /** Remove a slice from the ctx by type or name */
  readonly remove: <T, N extends string = string>(sliceType: SliceType<T, N> | N) => Ctx;
  /** Check if the ctx has a slice by type or name */
  readonly isInjected: <T, N extends string = string>(sliceType: SliceType<T, N> | N) => boolean;
  
  /** Get a slice instance from the ctx */
  readonly use: <T, N extends string = string>(sliceType: SliceType<T, N> | N) => Slice<T, N>;
  /** Get a slice value from the ctx */
  readonly get: <T, N extends string>(sliceType: SliceType<T, N> | N) => T;
  /** Set a slice value in the ctx */
  readonly set: <T, N extends string>(sliceType: SliceType<T, N> | N, value: T) => void;
  /** Update a slice value using a callback function */
  readonly update: <T, N extends string>(sliceType: SliceType<T, N> | N, updater: (prev: T) => T) => void;
  
  /** Add a timer into the ctx */
  readonly record: (timerType: TimerType) => Ctx;
  /** Remove a timer from the ctx */
  readonly clearTimer: (timerType: TimerType) => Ctx;
  /** Check if the ctx has a timer */
  readonly isRecorded: (timerType: TimerType) => boolean;
  /** Get a timer instance from the ctx */
  readonly timer: (timer: TimerType) => Timer;
  /** Resolve a timer */
  readonly done: (timer: TimerType) => void;
  /** Start a timer and return its promise */
  readonly wait: (timer: TimerType) => Promise<void>;
  /** Wait for an array of timers stored in a slice */
  readonly waitTimers: (slice: SliceType<TimerType[]>) => Promise<void>;
}

Usage Examples:

import { Container, Clock, Ctx, createSlice, createTimer } from "@milkdown/ctx";

// Setup context
const container = new Container();
const clock = new Clock();
const ctx = new Ctx(container, clock);

// Slice management
const counterSlice = createSlice(0, "counter");
ctx.inject(counterSlice, 10);

console.log(ctx.get(counterSlice)); // 10
ctx.update(counterSlice, (prev) => prev + 5);
console.log(ctx.get(counterSlice)); // 15

// Timer management
const loadTimer = createTimer("load-data", 5000);
ctx.record(loadTimer);

// Simulate async operation
setTimeout(() => ctx.done(loadTimer), 2000);

// Wait for timer
ctx.wait(loadTimer).then(() => {
  console.log("Data loaded!");
});

Advanced Context Operations

Metadata and Inspector Integration:

import { Ctx, Container, Clock, type Meta } from "@milkdown/ctx";

const meta: Meta = {
  displayName: "Data Plugin",
  description: "Handles data processing",
  package: "@example/data-plugin",
  group: "Core"
};

const ctx = new Ctx(new Container(), new Clock(), meta);

// Create contexts with different metadata
const apiCtx = ctx.produce({ 
  displayName: "API Handler",
  package: "@example/api",
  additional: { endpoint: "/api/v1" }
});

// Access inspector for debugging
if (ctx.inspector) {
  const telemetry = ctx.inspector.read();
  console.log("Plugin metadata:", telemetry.metadata);
  console.log("Injected slices:", telemetry.injectedSlices);
}

Complex Timer Coordination:

import { Ctx, Container, Clock, createSlice, createTimer, TimerType } from "@milkdown/ctx";

// Setup context and timers
const container = new Container();
const clock = new Clock();
const ctx = new Ctx(container, clock);
const initTimer = createTimer("init", 3000);
const loadTimer = createTimer("load", 5000);
const renderTimer = createTimer("render", 2000);

// Create slice to hold timer array
const pipelineTimers = createSlice<TimerType[]>([], "pipeline");
ctx.inject(pipelineTimers, [initTimer, loadTimer, renderTimer]);

// Record all timers
[initTimer, loadTimer, renderTimer].forEach(timer => ctx.record(timer));

// Simulate pipeline completion
setTimeout(() => ctx.done(initTimer), 1000);
setTimeout(() => ctx.done(loadTimer), 2000);
setTimeout(() => ctx.done(renderTimer), 3000);

// Wait for entire pipeline
ctx.waitTimers(pipelineTimers).then(() => {
  console.log("Pipeline completed!");
});

MilkdownPlugin Type

Type definition for Milkdown plugins with optional metadata and lifecycle support.

/**
 * Plugin function type with optional metadata and lifecycle management
 */
type MilkdownPlugin = { meta?: Meta } & ((ctx: Ctx) => CtxRunner);

type CtxRunner = () => RunnerReturnType;
type RunnerReturnType = void | Promise<void> | Cleanup | Promise<Cleanup>;
type Cleanup = () => void | Promise<void>;

Usage Examples:

import { type MilkdownPlugin, type Meta } from "@milkdown/ctx";

// Simple plugin
const simplePlugin: MilkdownPlugin = (ctx) => {
  // Setup phase
  const dataSlice = createSlice([], "plugin-data");
  ctx.inject(dataSlice);
  
  return () => {
    // Run phase
    console.log("Plugin running");
  };
};

// Full lifecycle plugin with metadata
const fullPlugin: MilkdownPlugin = Object.assign(
  (ctx) => {
    // Setup phase
    const configSlice = createSlice({ enabled: true }, "config");
    ctx.inject(configSlice);
    
    return async () => {
      // Run phase
      console.log("Async plugin running");
      
      return async () => {
        // Cleanup phase
        console.log("Plugin cleaning up");
        ctx.remove(configSlice);
      };
    };
  },
  {
    meta: {
      displayName: "Full Lifecycle Plugin",
      description: "Demonstrates complete plugin lifecycle",
      package: "@example/full-plugin",
      group: "Examples"
    } as Meta
  }
);

// Plugin with error handling
const robustPlugin: MilkdownPlugin = (ctx) => {
  const errorSlice = createSlice<Error | null>(null, "errors");
  ctx.inject(errorSlice);
  
  return () => {
    try {
      // Plugin logic
      console.log("Plugin executing");
    } catch (error) {
      ctx.set(errorSlice, error as Error);
      console.error("Plugin error:", error);
    }
    
    return () => {
      // Cleanup
      if (ctx.isInjected(errorSlice)) {
        ctx.remove(errorSlice);
      }
    };
  };
};

Integration Patterns

Plugin Composition

import { Ctx, Container, Clock, type MilkdownPlugin } from "@milkdown/ctx";

// Create shared context
const sharedCtx = new Ctx(new Container(), new Clock());

// Plugin that provides services
const servicePlugin: MilkdownPlugin = (ctx) => {
  const apiService = createSlice({ baseUrl: "/api" }, "api-service");
  ctx.inject(apiService);
  
  return () => {
    console.log("Service plugin initialized");
  };
};

// Plugin that consumes services
const consumerPlugin: MilkdownPlugin = (ctx) => {
  return () => {
    if (ctx.isInjected("api-service")) {
      const service = ctx.get("api-service");
      console.log("Using API service:", service.baseUrl);
    }
  };
};

// Initialize plugins in order
const serviceRunner = servicePlugin(sharedCtx);
const consumerRunner = consumerPlugin(sharedCtx);

serviceRunner();
consumerRunner();

State Synchronization

// Plugin that manages shared state
const stateManagerPlugin: MilkdownPlugin = (ctx) => {
  const appState = createSlice({ 
    theme: "light", 
    language: "en" 
  }, "app-state");
  
  ctx.inject(appState);
  
  // Watch for state changes
  const stateSlice = ctx.use(appState);
  stateSlice.on((newState) => {
    console.log("App state changed:", newState);
    // Persist to localStorage, sync with server, etc.
  });
  
  return () => {
    console.log("State manager ready");
  };
};

docs

context-management.md

index.md

plugin-orchestration.md

runtime-inspection.md

timer-coordination.md

tile.json