Skip to content

Building UI Features ​

UI is the extension surface we most want you building on. DocuShark exposes a PanelExtensions registry so you can add to the interface — PropertyPanel sections, custom property-type renderers, and panel-header actions — without modifying core components.

Extension Points ​

ExtensionDescriptionRegistration Method
Property SectionsAdd custom sections to PropertyPanelregisterPropertySection()
Property RenderersCustom renderers for new property typesregisterPropertyRenderer()
Panel ActionsAction buttons in panel headersregisterPanelAction()

Quick Start ​

typescript
import { panelExtensions } from './plugins/PanelExtensions';

// Register a custom property section
panelExtensions.registerPropertySection({
  id: 'my-plugin-settings',
  title: 'Plugin Settings',
  priority: 100,
  shouldShow: (shape, allShapes) => shape.type === 'my-custom-shape',
  render: (shape, allShapes, updateShape) => (
    <MyPluginSettings shape={shape} onChange={updateShape} />
  ),
});

Creating a Custom Shape Library ​

  1. Define shape definitions implementing LibraryShapeDefinition:
typescript
import type { LibraryShapeDefinition } from '../shapes/library/ShapeLibraryTypes';
import { createStandardProperties } from '../shapes/ShapeMetadata';

export const myShapes: LibraryShapeDefinition[] = [
  {
    type: 'hexagon',
    metadata: {
      type: 'hexagon',
      name: 'Hexagon',
      category: 'custom',
      icon: '⬡',
      properties: createStandardProperties({ includeLabel: true, includeIcon: true }),
      supportsLabel: true,
      supportsIcon: true,
      defaultWidth: 100,
      defaultHeight: 86,
      description: 'Regular hexagon shape',
    },
    // PathBuilder: receives width & height, returns a Path2D in local space (centered at origin)
    pathBuilder: (width, height) => {
      const path = new Path2D();
      const hw = width / 2;
      const hh = height / 2;
      const qw = width / 4;
      path.moveTo(-qw, -hh);
      path.lineTo(qw, -hh);
      path.lineTo(hw, 0);
      path.lineTo(qw, hh);
      path.lineTo(-qw, hh);
      path.lineTo(-hw, 0);
      path.closePath();
      return path;
    },
    // Anchor points for connector attachment (functions receive width, height)
    anchors: [
      { position: 'center', x: () => 0, y: () => 0 },
      { position: 'top', x: () => 0, y: (_w, h) => -h / 2 },
      { position: 'right', x: (w) => w / 2, y: () => 0 },
      { position: 'bottom', x: () => 0, y: (_w, h) => h / 2 },
      { position: 'left', x: (w) => -w / 2, y: () => 0 },
    ],
  },
];
  1. Register with the shape library store:
typescript
import { useShapeLibraryStore } from '../store/shapeLibraryStore';

useShapeLibraryStore.getState().registerShapes(myShapes);
  1. For lazy loading (recommended), add a loader entry in shapeLibraryStore.ts:
typescript
{
  category: 'custom',
  load: () => import('../shapes/library/myShapes').then((m) => m.myShapes),
}

TIP

See the Creating Shapes guide for a complete end-to-end walkthrough, including the full LibraryShapeDefinition API, metadata configuration, and testing.

Adding a Property Renderer ​

Custom property renderers handle specialized property types in the PropertyPanel:

typescript
import { panelExtensions } from './plugins/PanelExtensions';

// Register a gradient picker for 'gradient' type properties
panelExtensions.registerPropertyRenderer('gradient', ({ value, onChange }) => (
  <GradientPicker value={value} onChange={onChange} />
));

State Management Guidelines ​

  • Read-only access: Plugins should use useDocumentStore, useSessionStore for reading state
  • Mutations: Use updateShape() from useDocumentStore for shape modifications
  • Persistence: Plugin settings can use localStorage directly or create a Zustand store with persist middleware
  • Lifecycle: Register extensions during module initialization (top-level calls)

API Reference ​

See src/plugins/PanelExtensions.ts for the full TypeScript interface definitions including:

  • PropertySectionExtension — Custom PropertyPanel sections
  • PropertyRendererExtension — Custom property type renderers
  • PanelActionExtension — Panel header action buttons

Released under the AGPL-3.0 License.