ProofWidgets for Lean 4

repository·main·Indexed 42 days ago

https://github.com/leanprover-community/proofwidgets4

A high-level user interface component library for Lean 4 designed for symbolic visualizations, data plotting, and interactive tactic interfaces. It extends Lean's native user widgets with abstractions for HTML-like syntax via a Jsx module, as well as specialized components such as ForceGraphDisplay for D3 force-directed simulations, GraphvizDisplay for DOT diagrams, and PenroseCanvas for interactive diagrams.

Tokens
4.8K
Snippets
16
Records
20
Agent score
53%

What's inside ProofWidgets

  1. How ProofWidgets relates to Lean user widgets

    main
    ProofWidgets is a high-level component library built on top of the native user widgets mechanism in Lean 4. While Lean's core user widgets provide the minimal functionality required for custom interfaces, ProofWidgets provides higher-level abstractions, syntax sugar, and a library of reusable UI components for symbolic visualizations, data visualization, and tactic interfaces.
  2. Build ProofWidgets from source

    main

    If you are developing ProofWidgets or need to build it from source, you must have NPM installed. The build process compiles TypeScript code via NPM and then builds the Lean modules. The lakefile.lean manages this orchestration.

    • Full build: lake build (compiles TypeScript then Lean)
    • TypeScript only: lake build widgetJsAll
    • Development mode (TypeScript): lake build widgetJsAllDev (makes widgets easier to inspect in developer tools)

    Note: Due to a known Lake issue, modifying any single TypeScript source will trigger a rebuild of all widget sources.

    # Build the entire package
    lake build
    
    # Build only the TypeScript components
    lake build widgetJsAll
    
    # Build TypeScript in development mode
    lake build widgetJsAllDev
  3. View ProofWidgets demos

    main

    To view the built-in demos, clone the repository and build it using lake. The demos are located in ProofWidgets/Demos/.

    Tip: Use the pushpin icon in your editor to keep a widget in view while you live-code your own widgets.

    git clone https://github.com/leanprover-community/ProofWidgets4 --depth 1
    cd ProofWidgets4/
    lake build
  4. Add ProofWidgets as a dependency in Lake

    main

    To use ProofWidgets in your own Lean project, add it to your lakefile.lean. It is recommended to reference a specific release tag rather than the main branch to ensure compatibility with your Lean toolchain.

    By default, the repository includes pre-built JavaScript files in widget/js/, so you and your users typically do not need to install NPM. If you want to prevent the build from attempting to compile TypeScript and instead force a failure with a custom error message, you can use the with clause in your requirement.

    -- Standard usage: Replace v0.0.3 with a version published under Releases
    require "leanprover-community" / "proofwidgets" @ git "v0.0.3"
    
    -- Force failure with a custom error message instead of a full build
    require "leanprover-community" / "proofwidgets" @ git "v0.0.3"
      with NameMap.empty.insert `errorOnBuild "<my message>"
  5. Configure Graphviz interactivity and attributes

    main

    You can extend the interactivity and visual styling of the rendered Graphviz diagram using the attributer and event handler props. These are intended to be set by JavaScript callers rather than through the Lean interface.

    Customizing Attributes

    Use the attributer prop to modify how Graphviz attributes are applied to elements. This function is called by d3-graphviz during the rendering process.

    Node Interactivity

    The component supports three primary pointer events on nodes, edges, and clusters:

    • onClickNode: Triggered on click.
    • onPointerEnterNode: Triggered on pointerenter.
    • onPointerLeaveNode: Triggered on pointerleave.

    Warning: All three of these props must be memoized (e.g., using useCallback) to avoid infinite re-render loops.

  6. Use JSX-like syntax for HTML in Lean

    main

    ProofWidgets provides a Jsx scoped module that allows you to write HTML-like syntax directly within Lean code using the #html macro.

    import ProofWidgets.Component.HtmlDisplay
    open scoped ProofWidgets.Jsx
    
    -- click on the line below to see it in your infoview!
    #html <b>You can use HTML in Lean {.text s!"{1 + 3}"}!</b>
  7. Use the PenroseCanvas component to render diagrams

    main

    The PenroseCanvas component renders interactive Penrose diagrams. It supports embedding React nodes (like custom UI or math expressions) directly into the diagram at specific locations defined in the Penrose substance program.

    Requirements for Embedding Nodes

    To successfully embed a React node using the embedNodes prop:

    1. Substance Program: The object must exist in your Penrose substance program.
    2. Style Program: The object must be assigned a name.textBox : Rectangle field in your Penrose style program. The component uses this field to determine the dimensions and position for the embedded React node.
    3. Positioning: The component automatically calculates the offset of the named element and positions the React node absolutely over the textBox area.

    Props

    • trio: A PenroseTrio object containing the dsl, sty, and sub strings.
    • maxOptSteps: A number representing the maximum number of optimization steps allowed. This prevents the optimizer from running indefinitely if a diagram fails to converge.
    • embedNodes: A Map<string, React.ReactNode> where the key is the name of the object in the Penrose program and the value is the React element to render at that location.
    import { PenroseCanvas } from './path/to/penroseCanvas';
    
    // Example usage
    <PenroseCanvas
      trio={{
        dsl: 'domain code...',
        sty: 'style code with name.textBox : Rectangle...',
        sub: 'substance code...'
      }}
      maxOptSteps={100}
      embedNodes={new Map([
        ['myObjectName', <div className="custom-widget">Hello World</div>]
      ])}
    />
  8. Use the ForceGraphDisplay component

    main

    The ForceGraphDisplay component is a React component used to visualize a graph using a force-directed simulation (powered by D3). It supports interactive elements like dragging vertices, zooming, panning, and clicking elements to show details.

    To use it, provide a Graph object containing vertices and edges, along with configuration for the simulation forces.

    import ForceGraphDisplay from './forceGraphDisplay';
    
    // Define your graph data
    const vertices = [
      { id: 'v1', label: { text: 'Node 1' }, boundingShape: { circle: { radius: 10 } } },
      { id: 'v2', label: { text: 'Node 2' }, boundingShape: { circle: { radius: 10 } } }
    ];
    
    const edges = [
      { source: 'v1', target: 'v2', attrs: [['stroke', 'black']] }
    ];
    
    // Define simulation forces
    const forces = [
      { center: { x: 0, y: 0, strength: 0.1 } },
      { manyBody: { strength: -30 } },
      { link: { distance: 50 } }
    ];
    
    // Render the component
    <ForceGraphDisplay 
      vertices={vertices} 
      edges={edges} 
      defaultEdgeAttrs={[['stroke', 'gray']]} 
      forces={forces} 
      showDetails={true} 
    />
  9. Use the GraphvizDisplay component

    main

    The GraphvizDisplay component is a React component used to render Graphviz diagrams using d3-graphviz. It accepts DOT syntax strings and provides options for layout, sizing, and interactivity.

    Important Performance Note: To prevent unnecessary re-renders and layout thrashing, you must memoize the options, attributer, and event handler props (onClickNode, onPointerEnterNode, onPointerLeaveNode). If these references change on every render, the component will re-render the entire graph.

    Props Reference

    PropTypeDescription
    dotstringThe DOT source string for the graph.
    optionsGraphvizOptionsConfiguration for d3-graphviz (e.g., width, height).
    renderDebounceMsnumberTime in milliseconds to debounce the rendering process.
    centerOnVertexstring (optional)The ID/key of a vertex to center the view on after rendering.
    attributerd3.ValueFn (optional)A function to customize graph attributes. Must be memoized.
    onClickNode(ev: PointerEvent, d: unknown) => void (optional)Callback triggered when a node, edge, or cluster is clicked.
    onPointerEnterNode(ev: PointerEvent, d: unknown) => void (optional)Callback triggered on pointer enter.
    onPointerLeaveNode(ev: PointerEvent, d: unknown) => void (optional)Callback triggered on pointer leave.
    styleReact.CSSPropertiesStandard React style props.
    import GraphvizDisplay from './GraphvizDisplay';
    
    // Example usage with memoized props
    const options = React.useMemo(() => ({ width: 800, height: 600 }), []);
    const onClick = React.useCallback((ev, d) => console.log('Clicked:', d), []);
    
    function MyGraph() {
      return (
        <GraphvizDisplay
          dot="digraph { A -> B }"
          options={options}
          renderDebounceMs={100}
          onClickNode={onClick}
        />
      );
    }
  10. Render HTML content with renderHtml

    main

    Use renderHtml to process Html objects into a renderable format. The library also exports HtmlDisplay as a default way to handle HTML-based displays.

    import { renderHtml, HtmlDisplay } from '@leanprover-community/proofwidgets4';
    
    // Usage depends on the Html object structure provided by the library
    const rendered = renderHtml(htmlObject);
  11. Define Vertex and Edge data structures

    main

    When providing data to ForceGraphDisplay, follow these interface structures:

    Vertex

    interface Vertex {
      id: string
      label: Html // Uses the project's Html type for rendering
      boundingShape: 
        | { circle: { radius: number } }
        | { rect: { width: number, height: number } }
      details?: Html // Content shown when clicked if showDetails is true
    }

    Edge

    interface Edge {
      source: string // Vertex ID
      target: string // Vertex ID
      attrs: [string, any][] // Array of [attributeName, value] pairs (e.g., [['stroke', 'red']])
      label?: Html
      details?: Html
    }
  12. Reference ForceParams for simulation configuration

    main

    The forces prop accepts an array of objects, where each object defines a specific D3 force type. Supported force types include:

    • center: { x?: number, y?: number, strength?: number } - Positions the center of the simulation.
    • collide: { radius?: number, strength?: number, iterations?: number } - Prevents nodes from overlapping.
    • link: { distance?: number, strength?: number, iterations?: number } - Constrains edges to a specific distance.
    • manyBody: { strength?: number, theta?: number, distanceMin?: number, distanceMax?: number } - Simulates electrostatic forces (repulsion/attraction).
    • x: { x: number, strength?: number } - Forces nodes toward a specific X coordinate.
    • y: { y: number, strength?: number } - Forces nodes toward a specific Y coordinate.
    • radial: { radius: number, x?: number, y?: number, strength?: number } - Forces nodes into a radial pattern.
    // Example of multiple forces
    const forces = [
      { center: { x: 0, y: 0 } },
      { manyBody: { strength: -100 } },
      { collide: { radius: 20 } },
      { link: { distance: 100 } }
    ];