# Layout

AsukaDesigner uses a zone-based layout. The public configuration is intentionally simple: place items inside `layout.left.items`, `layout.top.items`, `layout.right.items`, or `layout.bottom.items`.

There is no `zone` property on layout items anymore. The zone is inferred from the parent key.

## Basic structure

```ts
const designer = createAsukaDesigner('#app', {
  layout: {
    left: {
      items: [
        { kind: 'views', label: 'Views', icon: 'views', type: 'floating', supportedEngines: ['fabric', '3d'] },
        { kind: 'designs', label: 'Designs', icon: 'brush', type: 'floating', supportedEngines: ['fabric'] },
      ],
    },
    top: {
      collapsed: false,
      items: [
        { kind: 'zoom', label: 'Zoom', icon: 'zoom', type: 'floating' },
        { kind: 'download', label: 'Download', icon: 'download', type: 'floating' },
      ],
    },
    right: { items: [] },
    bottom: { items: [] },
  },
});
```

Do not write this anymore:

```ts
// Not supported in the modern layout API
{ kind: 'download', label: 'Download', zone: 'left' }
```

The old flat `layout.blocks` format has been removed. Use only the zone-based structure.

## Engine filters on menu items

Every layout item can optionally declare `supportedEngines`. When omitted, the menu item follows the module registry compatibility. When provided, it acts as an additional visibility filter for that specific menu entry.

```ts
{ kind: 'designs', label: 'Designs', icon: 'brush', type: 'floating', supportedEngines: ['fabric', '3d'] }
```

For safety, this does not bypass module compatibility. A module must still be registered with support for the active engine before it can be opened.

## Supported zones

The supported layout zones are:

- `left`
- `right`
- `top`
- `bottom`

Each zone accepts a `LayoutZoneConfig`:

```ts
type LayoutZoneConfig = {
  type?: 'default' | 'panels' | 'inline';
  items?: LayoutItemConfig[];
  collapsible?: boolean;
  collapsed?: boolean;
};
```

## Layout item structure

Each item is a menu/module entry.

```ts
type LayoutItemConfig = {
  id?: string;
  kind: string;
  label?: string;
  icon?: string;
  order?: number;
  type?:
    | 'floating'
    | 'modal'
    | 'drawer'
    | 'panel-left'
    | 'panel-right'
    | 'panel-top'
    | 'panel-bottom'
    | 'popover'
    | 'inline';
  collapsed?: boolean;
  renderMode?: 'inline';
  mountTarget?: string | HTMLElement | null;
};
```

`kind` must match a registered module id. Built-in examples include `views`, `designs`, `text`, `image`, `shapes`, `layers`, `marker`, `drawingAreas`, `elements`, `license`, `zoom`, `download`, `magnifier`, and `ruler`.

## Zone rendering type vs item opening type

There are two different concepts:

```ts
layout.right.type = 'panels';
```

controls how the whole zone renders.

```ts
{ kind: 'layers', type: 'floating' }
```

controls how one item opens.

### Zone rendering types

- `default`: regular menu buttons.
- `panels`: renders module contents as sidebar panels.
- `inline`: renders module contents directly with lighter framing.

### Item opening types

- `floating`: floating overlay.
- `popover`: compact anchored overlay.
- `drawer`: drawer container.
- `modal`: viewport-level modal.
- `panel-left`, `panel-right`, `panel-top`, `panel-bottom`: side panel containers.
- `inline`: render inside the layout area.

## Overlay mount targets

Items can choose where their overlay is mounted with `mountTarget`.

```ts
const designer = createAsukaDesigner('#app', {
  layout: {
    left: {
      items: [
        {
          kind: 'download',
          label: 'Download',
          icon: 'download',
          type: 'drawer',
          mountTarget: '#download-drawer-host',
        },
      ],
    },
  },
});
```

Runtime override:

```ts
designer.util.modules.setMountTarget('download', '#download-drawer-host');
designer.util.modules.clearMountTarget('download');
```

Defaults are resolved by overlay type, not by one global target:

- `drawer`: contained inside the central designer content by default.
- `modal`: mounted at viewport/body level by default.
- `tooltip`: mounted at viewport/body level by default.
- `floating`, `popover`, and panels: keep their module/host behavior unless an explicit target is provided.

This keeps drawers inside the product designer while keeping modals full-screen by default.

## Recommended product-designer layout

```ts
const designer = createAsukaDesigner('#app', {
  layout: {
    left: {
      items: [
        { kind: 'views', label: 'Views', icon: 'views', type: 'floating' },
        { kind: 'designs', label: 'Designs', icon: 'brush', type: 'floating' },
        { kind: 'text', label: 'Text', icon: 'text', type: 'floating' },
        { kind: 'image', label: 'Images', icon: 'images', type: 'floating' },
        { kind: 'shapes', label: 'Shapes', icon: 'customize', type: 'floating' },
        { kind: 'layers', label: 'Layers', icon: 'layers', type: 'floating' },
        { kind: 'marker', label: 'Marker', icon: 'marker', type: 'floating' },
        { kind: 'drawingAreas', label: 'Drawing Areas', icon: 'pencil', type: 'floating' },
        { kind: 'license', label: 'License', icon: 'license', type: 'floating' },
        { kind: 'elements', label: 'Elements', icon: 'puzzle', type: 'floating' },
      ],
    },
    top: {
      collapsed: false,
      items: [
        { kind: 'magnifier', label: 'Magnifier', icon: 'magnifying-glass', type: 'floating' },
        { kind: 'ruler', label: 'Ruler', icon: 'ruler', type: 'floating' },
        { kind: 'zoom', label: 'Zoom', icon: 'zoom', type: 'floating' },
        { kind: 'download', label: 'Download', icon: 'download', type: 'floating' },
      ],
    },
    right: { items: [] },
    bottom: { items: [] },
  },
});
```

## View pagination

The view pagination bar is the navigation strip rendered below the canvas. It is configured separately from layout zones because it is not a regular `layout.left/right/top/bottom` item.

Default behavior is automatic:

```ts
createAsukaDesigner('#app', {
  ui: {
    viewsPagination: {
      enabled: true,
      mode: 'auto', // thumbnails when possible, dots otherwise
    },
  },
});
```

To disable only the bottom pagination bar while keeping the Views module and multi-view document state available:

```ts
createAsukaDesigner('#app', {
  ui: {
    viewsPagination: false,
  },
});
```

To force a specific display style:

```ts
createAsukaDesigner('#app', {
  ui: {
    viewsPagination: {
      mode: 'numbers', // 'auto' | 'thumbnails' | 'dots' | 'numbers'
    },
  },
});
```

Optional flags:

```ts
createAsukaDesigner('#app', {
  ui: {
    viewsPagination: {
      showLabels: true,
      enableGrabNavigation: false,
    },
  },
});
```

Runtime updates are available through `designer.util.uiConfig`:

```ts
designer.util.uiConfig.setViewsPagination({ mode: 'dots' });
designer.util.uiConfig.setViewsPaginationEnabled(false);
```

## Runtime layout control

```ts
designer.layout.openModule('text');
designer.layout.addItem('left', {
  kind: 'my-module',
  label: 'My module',
  icon: 'puzzle',
  type: 'floating',
});
designer.layout.moveBlock('my-module', 'right', 0);
designer.layout.removeItem('my-module');
```

Zone state:

```ts
designer.layout.getZoneType('left');
designer.layout.setZoneType('right', 'panels');
designer.layout.getZoneCollapsed('top');
designer.layout.setZoneCollapsed('top', true);
designer.layout.toggleZoneCollapsed('top');
```

Block state:

```ts
designer.layout.getBlockCollapsed('layers');
designer.layout.setBlockCollapsed('layers', false);
designer.layout.toggleBlockCollapsed('layers');
```

## Contextual selection menu

The selection menu is configured separately through `layout.selectionMenu`.

```ts
const designer = createAsukaDesigner('#app', {
  layout: {
    selectionMenu: {
      type: 'popover',
      items: ['font-family', 'text-formatting', 'text-curve', 'colors'],
      open: true,
    },
  },
});
```

Useful runtime helpers:

```ts
designer.layout.selectionMenu.setItems(['font-family', 'text-formatting', 'colors']);
designer.layout.selectionMenu.setType('popover');
designer.layout.selectionMenu.open();
designer.layout.selectionMenu.goTo('colors');
designer.layout.selectionMenu.back();
designer.layout.selectionMenu.close();
```

## Menu translations

Menu labels resolve in this order:

1. `menu.<id>`
2. `menu.<kind>`
3. the raw `label` value

See [Internationalization](./i18n.md) and [Translation keys reference](./translation-keys.md).
