diff --git a/lab/dms/client/component/x-file-item-upload.ts b/lab/dms/client/component/x-file-item-upload.ts
index 3ec73dcc7573ccee8c755235f809c1f60f66c975..b79d63d14b6eec546d6da14b0ba316b07e49b0a2 100644
--- a/lab/dms/client/component/x-file-item-upload.ts
+++ b/lab/dms/client/component/x-file-item-upload.ts
@@ -46,8 +46,9 @@ export class XFileItemUpload extends ChemistryLitElement {
           const resolvedFile = (await this._uploadClass?.getByID(file)(
             this._uploadClass.allFields,
           )) as Maybe<AdornisFile>;
-          if (resolvedFile && resolvedFile.meta && this.file?.name.split('.')[0]) {
-            const name = this.file.name.split('.')[0]!;
+
+          const name = this.file?.name.substring(0, this.file?.name.lastIndexOf('.'));
+          if (resolvedFile && resolvedFile.meta && name) {
             resolvedFile.meta.fileName = name;
             await resolvedFile.save();
           }
diff --git a/lab/dms/client/component/x-file-list.ts b/lab/dms/client/component/x-file-list.ts
index 97abc5bef4c935899d9f7a3eca1c361af0d992cf..37374b8c5a57e775484d29e386af399fdcfde9d0 100644
--- a/lab/dms/client/component/x-file-list.ts
+++ b/lab/dms/client/component/x-file-list.ts
@@ -147,7 +147,7 @@ export class XFileList extends ChemistryLitElement {
                           fontSize: '16px',
                         })}
                       >
-                        ${file.meta?.fileName}.${file.meta?.extension}
+                        ${file.meta?.fileName}
                       </x-text>
 
                       <x-text ${css({ fontSize: '12px' })}>
diff --git a/lab/html-based-buildify/client/BuildifyLitElement.ts b/lab/html-based-buildify/client/BuildifyLitElement.ts
index a7e27293f267250494d5c90d718db36ade77e053..78a5ef475d046445c31190f2473437a09fc97689 100644
--- a/lab/html-based-buildify/client/BuildifyLitElement.ts
+++ b/lab/html-based-buildify/client/BuildifyLitElement.ts
@@ -48,7 +48,7 @@ export class BuildifyLitElement<T extends HTMLBase> extends ChemistryLitElement
   @property({ attribute: 'data', type: String }) data = new RXController(this, undefined);
   @property({ attribute: 'no-content', type: Boolean }) hasNoContent = false;
 
-  @state() protected readonly _constructedData = new RXController(
+  @state() readonly _constructedData = new RXController(
     this,
     this.data.observable.pipe(
       filter(Boolean),
diff --git a/lab/html-based-buildify/client/marks/font-size.ts b/lab/html-based-buildify/client/marks/font-size.ts
index b5bd1ad42aa8f23673c893f6844af34bbe45d07a..5f284c73b6467bc32d03b02529da1f656cb75a94 100644
--- a/lab/html-based-buildify/client/marks/font-size.ts
+++ b/lab/html-based-buildify/client/marks/font-size.ts
@@ -22,7 +22,7 @@ export const fontSize: IMarkConfig = {
 
 export const FONT_SIZE_MENU_ITEM = (fontSize: string) => createFontSizeMenuItem(fontSize);
 
-function createFontSizeMenuItem(fontSize: string): IMenuItem {
+export function createFontSizeMenuItem(fontSize: string): IMenuItem {
   return {
     group: MenuItemGroup.TEXT_STYLE,
     label: fontSize,
@@ -37,27 +37,3 @@ function createFontSizeMenuItem(fontSize: string): IMenuItem {
     },
   };
 }
-
-// export function fontSizeMenu() {
-//   const fontSizes = [
-//     '8px',
-//     '10px',
-//     '12px',
-//     '14px',
-//     '16px',
-//     '18px',
-//     '20px',
-//     '24px',
-//     '30px',
-//     '36px',
-//     '48px',
-//     '60px',
-//     '72px',
-//   ];
-
-//   const items = fontSizes.map(size => createFontSizeMenuItem(size));
-
-//   return new Dropdown(items, {
-//     label: '',
-//   });
-// }
diff --git a/lab/html-based-buildify/client/menu-items/CollapsibleMenuItem.ts b/lab/html-based-buildify/client/menu-items/CollapsibleMenuItem.ts
new file mode 100644
index 0000000000000000000000000000000000000000..453b17b88835ed4a2bca7f16610cce1e67b35640
--- /dev/null
+++ b/lab/html-based-buildify/client/menu-items/CollapsibleMenuItem.ts
@@ -0,0 +1,104 @@
+import type { Styles } from '@adornis/ass/style.js';
+import { ChemistryLitElement } from '@adornis/chemistry/chemistry-lit-element.js';
+import { css } from '@adornis/chemistry/directives/css.js';
+import { html, nothing } from 'lit';
+import { customElement, state } from 'lit/decorators.js';
+import type { EditorView } from 'prosemirror-view';
+import type { IMenuItem } from '../types.js';
+import { renderMenuItem, renderMenuItemIcon } from '../x-prosemirror-toolbar.js';
+
+// Note:
+// The collapsible uses its own class to represent a state
+// Do not recreate this MenuItem on render cycles! State will be lost. (isOpen state)
+
+export class CollapsibleMenuItem implements IMenuItem {
+  isOpen: boolean = false;
+  menuItems: IMenuItem[];
+  label: string;
+  icon: string | undefined;
+  group?: string | undefined;
+
+  isActive?: ((editorView: EditorView) => boolean) | undefined;
+  shouldVisualize?: ((editorView: EditorView) => boolean) | undefined;
+
+  private _menu: CollapsibleMenu;
+  constructor({
+    label,
+    group,
+    menuItems,
+    icon,
+  }: {
+    label: string;
+    group?: string;
+    menuItems: IMenuItem[];
+    icon: string;
+  }) {
+    this.label = label;
+    this.group = group;
+    this.menuItems = menuItems;
+    this.icon = icon;
+
+    this._menu = document.createElement('collapsible-menu') as CollapsibleMenu;
+    this._menu.addEventListener('mousedown', e => {
+      e.preventDefault();
+    });
+    this._menu.label = label;
+    this._menu.icon = icon;
+    this._menu.menuItems = menuItems;
+    this._menu.isOpen = this.isOpen;
+  }
+
+  render(view: EditorView) {
+    this._menu.view = view;
+    this._menu.isOpen = this.isOpen;
+    return html` ${this._menu} `;
+  }
+
+  run(editorView: EditorView) {
+    this.isOpen = !this._menu.isOpen;
+    this._menu.isOpen = this.isOpen;
+  }
+}
+
+@customElement('collapsible-menu')
+export class CollapsibleMenu extends ChemistryLitElement {
+  label!: string;
+  icon!: string;
+  menuItems!: IMenuItem[];
+  view!: EditorView;
+  @state() isOpen: boolean = false;
+
+  override render() {
+    return html`
+      <x-flex horizontal crossaxis-center space="sm" wrap ${css({ borderBottom: this.isOpen ? '2px solid #333' : '' })}>
+        ${renderMenuItemIcon({ view: this.view, isActive: this.isOpen, icon: this.icon, run: () => {} })}
+        ${this.isOpen
+          ? html`
+              <x-flex
+                horizontal
+                crossaxis-center
+                space="sm"
+                wrap
+                @click=${e => {
+                  e.preventDefault();
+                  e.stopPropagation();
+                  this.requestUpdate();
+                }}
+              >
+                ${this.menuItems.map(item => renderMenuItem(item, this.view))}
+              </x-flex>
+            `
+          : nothing}
+      </x-flex>
+    `;
+  }
+
+  override styles() {
+    return [
+      ...super.styles(),
+      {
+        ':host': {},
+      },
+    ] as Styles[];
+  }
+}
diff --git a/lab/html-based-buildify/client/menu-items/CopySelectionToClipboard.ts b/lab/html-based-buildify/client/menu-items/CopySelectionToClipboard.ts
index 2571b976b48dfd4a13f1af43cf1cab46ad23c039..c9e9bfea65a907b6d47c6cfa9cea98f214d73ee4 100644
--- a/lab/html-based-buildify/client/menu-items/CopySelectionToClipboard.ts
+++ b/lab/html-based-buildify/client/menu-items/CopySelectionToClipboard.ts
@@ -1,5 +1,7 @@
 import { XDialog } from '@adornis/dialog/x-dialog.js';
 import { baseKeymap } from 'prosemirror-commands';
+import { DOMParser } from 'prosemirror-model';
+import {} from 'prosemirror-state';
 import type { IMenuItem } from '../types.js';
 
 export const CopySelectionMenuItem: IMenuItem = {
@@ -27,49 +29,55 @@ export const CopySelectionMenuItem: IMenuItem = {
   },
 };
 
-// export const PasteFromClipboardMenuItem: IMenuItem = {
-//   label: 'Einfügen',
-//   icon: 'paste',
-//   run: async view => {
-//     try {
-//       // Lese den HTML-Inhalt aus der Zwischenablage
-//       const clipboardItems = await navigator.clipboard.read();
-//       for (const item of clipboardItems) {
-//         if (item.types.includes('text/html')) {
-//           const htmlBlob = await item.getType('text/html');
-//           const htmlText = await htmlBlob.text();
-//           const { state, dispatch } = view;
-//           const parser = DOMParser.fromSchema(state.schema);
-//           const doc = parser.parseSlice(htmlText, 'text/html');
-//           const body = doc.body;
+export const PasteFromClipboardMenuItem: IMenuItem = {
+  label: 'Einfügen',
+  icon: 'arrow_upward',
+  run: async view => {
+    const { state, dispatch } = view;
+    const { tr, schema } = state;
+
+    try {
+      // Überprüfen, ob die Zwischenablage-API verfügbar ist
+      if (!navigator.clipboard || !navigator.clipboard.read) {
+        console.error('Die Zwischenablage-API wird in diesem Browser nicht unterstützt.');
+        return;
+      }
 
-//           // Verwende den ProseMirror-Parser, um den HTML-Inhalt in Nodes umzuwandeln
-//           const slice = Slice.fromJSON(state.schema, doc.toJSON());
+      // Lesen der Daten aus der Zwischenablage
+      const clipboardItems = await navigator.clipboard.read();
+      let htmlContent: string | null = null;
+
+      // Durchsuchen der ClipboardItems nach HTML-Inhalten
+      for (const item of clipboardItems) {
+        if (item.types.includes('text/html')) {
+          const blob = await item.getType('text/html');
+          htmlContent = await blob.text();
+          break;
+        }
+      }
+
+      // Wenn kein HTML-Inhalt gefunden wurde, eine Warnung ausgeben
+      if (!htmlContent) {
+        console.warn('Die Zwischenablage enthält keinen HTML-Inhalt.');
+        return;
+      }
 
-//           if (slice) {
-//             const { tr } = state;
-//             tr.replaceSelection(slice);
-//             dispatch(tr);
-//             await XDialog.alert('Der Inhalt aus der Zwischenablage wurde eingefügt.');
-//           } else {
-//             await XDialog.alert('Fehler beim Einfügen des Inhalts: Kein gültiger Slice.');
-//           }
-//         } else if (item.types.includes('text/plain')) {
-//           const textBlob = await item.getType('text/plain');
-//           const text = await textBlob.text();
-//           const { state, dispatch } = view;
-//           const { tr } = state;
-//           tr.insertText(text, state.selection.from, state.selection.to);
-//           dispatch(tr);
-//           await XDialog.alert('Der Inhalt aus der Zwischenablage wurde eingefügt.');
-//         }
-//       }
-//     } catch (err) {
-//       await XDialog.alert('Fehler beim Einfügen aus der Zwischenablage: ' + err.message);
-//     }
-//   },
-//   shouldVisualize: view => {
-//     // Die Visualisierung basiert darauf, ob die Zwischenablage-Daten verfügbar sind
-//     return navigator.clipboard && navigator.clipboard.read !== undefined;
-//   },
-// };
+      // Erstellen eines temporären DOM-Elements zum Parsen des HTML-Inhalts
+      const tempElement = document.createElement('div');
+      tempElement.innerHTML = htmlContent;
+
+      // Konvertieren des HTML-Inhalts in ein ProseMirror-Dokument
+      const fragment = DOMParser.fromSchema(schema).parse(tempElement);
+
+      // Einfügen des Inhalts in das Dokument
+      tr.replaceSelectionWith(fragment);
+      dispatch(tr);
+    } catch (error) {
+      console.error('Fehler beim Einfügen aus der Zwischenablage:', error);
+    }
+  },
+  shouldVisualize: view => {
+    // Die Visualisierung basiert darauf, ob die Zwischenablage-API verfügbar ist
+    return navigator.clipboard && typeof navigator.clipboard.read === 'function';
+  },
+};
diff --git a/lab/html-based-buildify/client/menu-items/EditGlobalSettings.ts b/lab/html-based-buildify/client/menu-items/EditGlobalSettings.ts
index 1e4692f608c2c5862660b110e104066e7a3bd32f..77d55adef15879b4da8d704220c2dc651281a6bb 100644
--- a/lab/html-based-buildify/client/menu-items/EditGlobalSettings.ts
+++ b/lab/html-based-buildify/client/menu-items/EditGlobalSettings.ts
@@ -3,7 +3,6 @@ import { GlobalSettingsDrawerPanel } from '../global-settings-drawer-panel.js';
 import type { IMenuItem } from '../types.js';
 
 export const EditGlobalSettingsMenuItem = (globalSettingsClassName: string): IMenuItem => {
-  console.log('menu item für global settings: ', globalSettingsClassName);
   return {
     label: 'Globale Einstellungen',
     icon: 'settings',
diff --git a/lab/html-based-buildify/client/nodes/grid.ts b/lab/html-based-buildify/client/nodes/grid.ts
deleted file mode 100644
index 603a7ffc2d9557e0d654a63fa3466530871f2bf5..0000000000000000000000000000000000000000
--- a/lab/html-based-buildify/client/nodes/grid.ts
+++ /dev/null
@@ -1,319 +0,0 @@
-/* eslint-disable complexity */
-/* eslint-disable @typescript-eslint/no-non-null-assertion */
-import type { Styles } from '@adornis/ass/style.js';
-import '@adornis/buildify/client/components/x-buildify-spacing-picker.js';
-import '@adornis/buildify/client/components/x-icon-button.js';
-import { ModeConsumer } from '@adornis/buildify/client/globals/consumer.js';
-import { ChemistryLitElement } from '@adornis/chemistry/chemistry-lit-element.js';
-import { css } from '@adornis/chemistry/directives/css.js';
-import '@adornis/chemistry/elements/components/x-flex';
-import '@adornis/chemistry/elements/components/x-grid.js';
-import '@adornis/chemistry/elements/components/x-icon.js';
-import '@adornis/forms/x-checkbox.js';
-import '@adornis/forms/x-input';
-import { html, nothing } from 'lit';
-import { customElement, property } from 'lit/decorators.js';
-import type { EditorView } from 'prosemirror-view';
-import { Size } from '../../db/enums.js';
-import { HTMLBaseContainerGridTableView, type HTMLBaseContainerGrid } from '../../db/HTMLBaseContainerGrid.js';
-import { GRID_CELL_KEY, GRID_CELL_SCHEMA, GRID_KEY, GRID_SCHEMA } from '../../schema/nodes/grid.js';
-import { PARAGRAPH_KEY } from '../../schema/nodes/paragraph.js';
-import { BuildifyLitElement } from '../BuildifyLitElement.js';
-import { ContainerEditor } from '../editors/Container.js';
-import { runInsertNode } from '../helper/runInsertNode.js';
-import { MenuItemGroup, type EditorFunc, type INodeConfig } from '../types.js';
-import { isNodeSelection } from '../util.js';
-
-export const editor: EditorFunc<HTMLBaseContainerGrid> = ({
-  content,
-  contentController,
-  controllerBaseKeyPath,
-  host,
-}) => {
-  const globalColumns = content.value([...controllerBaseKeyPath, 'columns'], undefined) ?? 3;
-  const desktopColumns = content.value([...controllerBaseKeyPath, 'columns'], Size.DESKTOP);
-  const tabletColumns = content.value([...controllerBaseKeyPath, 'columns'], Size.TABLET);
-  const mobileColumns = content.value([...controllerBaseKeyPath, 'columns'], Size.MOBILE);
-
-  const highestColumnsCount = [globalColumns, desktopColumns, tabletColumns, mobileColumns]
-    .sort((a, b) => b - a)
-    .at(0) as number;
-
-  if (!content.columnWidths) content.columnWidths = [];
-  // delete too many entries, if not needed
-  if (content.columnWidths.length > highestColumnsCount)
-    content.columnWidths = content.columnWidths.slice(0, highestColumnsCount);
-  // ensure entries length === columns count
-  for (let i = 0; i < highestColumnsCount; i++) {
-    const columnValue = content.columnWidths[i];
-    if (columnValue !== undefined) continue;
-    content.columnWidths[i] = '';
-  }
-
-  const countColumns =
-    content.value([...controllerBaseKeyPath, 'columns'], contentController.size) ??
-    content.value([...controllerBaseKeyPath, 'columns'], undefined) ??
-    3;
-
-  return html`
-    <!-- column definitions -->
-    <x-flex space="md">
-      <x-input
-        clearable
-        placeholder="Anzahl Spalten"
-        type="number"
-        ${contentController.field(...controllerBaseKeyPath, 'columns')}
-        @value-picked=${() => {
-          host.requestUpdate();
-        }}
-      ></x-input>
-
-      <x-infobox> Die Summe der Prozentzahl aller Spaltenbreiten sollte max. 100% betragen </x-infobox>
-      ${new Array(countColumns).fill(null).map((width, index) => {
-        return html`
-          <x-input
-            clearable
-            placeholder=${`Breite der ${index + 1}. Spalte`}
-            ${contentController.field(...controllerBaseKeyPath, 'columnWidths', index)}
-          ></x-input>
-        `;
-      })}
-      <!-- END: column definitions -->
-
-      <x-infobox>
-        Um den Abstand der Kacheln innerhalb des Grids zu verändern, kannst du den Wert für den vertikalen oder den
-        horizontalen Abstand vergrößern und verkleinern.
-      </x-infobox>
-
-      <x-buildify-spacing-picker
-        clearable
-        select
-        placeholder="Abstand zwischen den Spalten"
-        ${contentController.field(...controllerBaseKeyPath, 'gridColumnGap')}
-        select
-      ></x-buildify-spacing-picker>
-      <x-buildify-spacing-picker
-        clearable
-        select
-        placeholder="Abstand zwischen den Reihen"
-        ${contentController.field(...controllerBaseKeyPath, 'gridRowGap')}
-        select
-      ></x-buildify-spacing-picker>
-
-      <x-checkbox
-        .label=${'Anordnung der Items im Grid umdrehen'}
-        ${contentController.field(...controllerBaseKeyPath, 'revertDirection')}
-      ></x-checkbox>
-
-      ${content.tableView
-        ? html`
-            <x-infobox> Tabellenoptik </x-infobox>
-            <x-checkbox
-              .label=${'Horizontale Linien hinzufügen'}
-              ${contentController.field(...controllerBaseKeyPath, 'tableView', 'hasHorizontalSeperator')}
-            ></x-checkbox>
-            <x-checkbox
-              .label=${'Vertikale Linien hinzufügen'}
-              ${contentController.field(...controllerBaseKeyPath, 'tableView', 'hasVerticalSeperator')}
-            ></x-checkbox>
-            <x-buildify-spacing-picker
-              placeholder="Breite der Linien"
-              ${contentController.field(...controllerBaseKeyPath, 'tableView', 'thickness')}
-            ></x-buildify-spacing-picker>
-            <x-buildify-color-picker
-              placeholder="Farbe der Linien"
-              ${contentController.field(...controllerBaseKeyPath, 'tableView', 'colorSeperators')}
-            ></x-buildify-color-picker>
-            <x-icon-button
-              icon="delete"
-              text="Tabellenoptik entfernen"
-              @click=${() => {
-                content.tableView = null;
-                host.requestUpdate();
-              }}
-            ></x-icon-button>
-          `
-        : html`
-            <x-icon-button
-              icon="add"
-              text="Tabellenoptik hinzufügen"
-              @click=${() => {
-                content.tableView = new HTMLBaseContainerGridTableView({});
-                host.requestUpdate();
-              }}
-            ></x-icon-button>
-          `}
-      ${ContainerEditor({
-        content,
-        // @ts-expect-error its the same class
-        contentController,
-        controllerBaseKeyPath,
-        host,
-      })}
-    </x-flex>
-  `;
-};
-
-export const grid: INodeConfig = {
-  node: {
-    name: GRID_KEY,
-    schema: GRID_SCHEMA,
-  },
-  menuItems: [
-    {
-      group: MenuItemGroup.LAYOUT,
-      label: 'Grid',
-      icon: 'grid_on',
-      run: (view: EditorView) =>
-        runInsertNode(GRID_KEY, state => {
-          return {
-            content: [
-              state.schema.nodes[GRID_CELL_KEY]!.create(null, [
-                state.schema.nodes[PARAGRAPH_KEY]!.create(null, [state.schema.text('Hier tippen...')]),
-              ]),
-            ],
-          };
-        })(view.state, view),
-      shouldVisualize: (view: EditorView) => {
-        return !isNodeSelection(view.state.selection);
-      },
-    },
-  ],
-  editor,
-};
-
-export const gridCell: INodeConfig = {
-  node: {
-    name: GRID_CELL_KEY,
-    schema: GRID_CELL_SCHEMA,
-  },
-};
-
-@customElement('node-grid')
-export class NodeGrid extends BuildifyLitElement<HTMLBaseContainerGrid> {
-  override content({ content, mode }: { content?: HTMLBaseContainerGrid; mode }) {
-    const columns = content?.columns ? content.columns : 3;
-    const templateColumns: string[] = [];
-    for (let i = 0; i < columns; i++) {
-      const templateValue = content?.getColumnWidthByIndex(i) ?? '1fr';
-      templateColumns.push(templateValue);
-    }
-
-    return html`
-      <x-grid
-        columns=${templateColumns.join(' ')}
-        ${css({
-          gridColumnGap: content?.gridColumnGap ?? '',
-          gridRowGap: content?.gridRowGap ?? '',
-          padding: content?.padding ?? '',
-          background: content?.backgroundColor ?? '',
-        })}
-      >
-        <slot></slot>
-        ${mode === 'edit'
-          ? html`
-              <x-flex
-                ${css({ border: '1px lightgrey dashed', userSelect: 'none', cursor: 'pointer' })}
-                center
-                crossaxis-center
-                @click=${e => {
-                  e.preventDefault();
-                  e.stopPropagation();
-                  this.append((document.createElement('div').innerText = 'Hier tippen...'));
-                }}
-              >
-                Zelle hinzufügen
-              </x-flex>
-            `
-          : nothing}
-      </x-grid>
-    `;
-  }
-
-  override styles() {
-    return [
-      ...super.styles(),
-      {
-        ':host': {
-          borderRadius: this._data.value?.borderRadius ?? '',
-        },
-        'x-grid>*::slotted(*)': {
-          boxSizing: 'border-box',
-        },
-      },
-    ] as Styles[];
-  }
-}
-
-@customElement('node-grid-cell')
-export class NodeGridCell extends ChemistryLitElement {
-  private readonly _consumedMode = ModeConsumer(this);
-
-  @property({ attribute: false }) thickness!: string;
-  @property({ attribute: false }) color!: string;
-  @property({ attribute: false }) rowGap!: number;
-  @property({ attribute: false }) columnGap!: number;
-
-  override render() {
-    return html` <slot></slot> `;
-  }
-
-  isEditMode() {
-    return this._consumedMode.value === 'edit';
-  }
-
-  override styles() {
-    return [
-      ...super.styles(),
-      {
-        ':host': {
-          border: this.isEditMode() ? '1px solid grey' : '',
-          position: 'relative',
-        },
-        ':host(.grid-item)': {
-          position: 'relative',
-          overflow: 'visible',
-        },
-        ':host(.grid-item) > *': {
-          width: '100%',
-        },
-        ':host(.grid-item.child-container-item) > :first-child': {
-          boxSizing: 'border-box',
-          height: '100%',
-        },
-        ':host(.grid-line-left)::before': {
-          content: "''",
-          position: 'absolute',
-          left: this.columnGap ? `-${this.columnGap / 2}px` : '0',
-          top: '0',
-          bottom: '0',
-          width: this.thickness,
-          background: this.color,
-          zIndex: '10',
-        },
-        ':host(.grid-line-below)::after': {
-          content: "''",
-          position: 'absolute',
-          bottom: this.rowGap ? `-${this.rowGap / 2}px` : '0',
-          height: this.thickness,
-          background: this.color,
-          left: '0',
-          right: '0',
-          zIndex: '10',
-        },
-        ':host(.grid-line-left.line-left-max-bottom)::before': {
-          bottom: this.rowGap ? `-${this.rowGap / 2}px` : '0',
-        },
-        ':host(.grid-line-left.line-left-max-top)::before': {
-          top: this.rowGap ? `-${this.rowGap / 2}px` : '0',
-        },
-        ':host(.grid-line-below.line-below-max-left)::after': {
-          left: this.columnGap ? `-${this.columnGap / 2}px` : '0',
-        },
-        ':host(.grid-line-below.line-below-max-right)::after': {
-          right: this.columnGap ? `-${this.columnGap / 2}px` : '0',
-        },
-      },
-    ] as Styles[];
-  }
-}
diff --git a/lab/html-based-buildify/client/nodes/grid/config.ts b/lab/html-based-buildify/client/nodes/grid/config.ts
new file mode 100644
index 0000000000000000000000000000000000000000..abd5d945ffe9cd7bbd37d623db0bb85261c49ad0
--- /dev/null
+++ b/lab/html-based-buildify/client/nodes/grid/config.ts
@@ -0,0 +1,38 @@
+import type { EditorView } from 'prosemirror-view';
+import { GRID_CELL_KEY, GRID_KEY, GRID_SCHEMA } from '../../../schema/nodes/grid.js';
+import { PARAGRAPH_KEY } from '../../../schema/nodes/paragraph.js';
+import { runInsertNode } from '../../helper/runInsertNode.js';
+import { type INodeConfig, MenuItemGroup } from '../../types.js';
+import { isNodeSelection } from '../../util.js';
+import { editor } from './editor.js';
+import './node-grid.js';
+// import { gridViewPlugin } from './node-view.js';
+
+export const grid: INodeConfig = {
+  node: {
+    name: GRID_KEY,
+    schema: GRID_SCHEMA,
+  },
+  menuItems: [
+    {
+      group: MenuItemGroup.LAYOUT,
+      label: 'Grid',
+      icon: 'grid_on',
+      run: (view: EditorView) =>
+        runInsertNode(GRID_KEY, state => {
+          return {
+            content: [
+              state.schema.nodes[GRID_CELL_KEY]!.create(null, [
+                state.schema.nodes[PARAGRAPH_KEY]!.create(null, [state.schema.text('Hier tippen...')]),
+              ]),
+            ],
+          };
+        })(view.state, view),
+      shouldVisualize: (view: EditorView) => {
+        return !isNodeSelection(view.state.selection);
+      },
+    },
+  ],
+  editor,
+  //   plugins: (schema: Schema) => [gridViewPlugin],
+};
diff --git a/lab/html-based-buildify/client/nodes/grid/editor.ts b/lab/html-based-buildify/client/nodes/grid/editor.ts
new file mode 100644
index 0000000000000000000000000000000000000000..717e87431f3b5b29ec1547e2cfe4eb7c50dc7d21
--- /dev/null
+++ b/lab/html-based-buildify/client/nodes/grid/editor.ts
@@ -0,0 +1,135 @@
+import { Size } from '@adornis/buildify/client/globals/enums.js';
+import { html } from 'lit';
+import { HTMLBaseContainerGrid, HTMLBaseContainerGridTableView } from '../../../db/HTMLBaseContainerGrid.js';
+import { ContainerEditor } from '../../editors/Container.js';
+import type { EditorFunc } from '../../types.js';
+
+export const editor: EditorFunc<HTMLBaseContainerGrid> = ({
+  content,
+  contentController,
+  controllerBaseKeyPath,
+  host,
+}) => {
+  const globalColumns = content.value([...controllerBaseKeyPath, 'columns'], undefined) ?? 3;
+  const desktopColumns = content.value([...controllerBaseKeyPath, 'columns'], Size.DESKTOP);
+  const tabletColumns = content.value([...controllerBaseKeyPath, 'columns'], Size.TABLET);
+  const mobileColumns = content.value([...controllerBaseKeyPath, 'columns'], Size.MOBILE);
+
+  const highestColumnsCount = [globalColumns, desktopColumns, tabletColumns, mobileColumns]
+    .sort((a, b) => b - a)
+    .at(0) as number;
+
+  if (!content.columnWidths) content.columnWidths = [];
+  // delete too many entries, if not needed
+  if (content.columnWidths.length > highestColumnsCount)
+    content.columnWidths = content.columnWidths.slice(0, highestColumnsCount);
+  // ensure entries length === columns count
+  for (let i = 0; i < highestColumnsCount; i++) {
+    const columnValue = content.columnWidths[i];
+    if (columnValue !== undefined) continue;
+    content.columnWidths[i] = '';
+  }
+
+  const countColumns =
+    content.value([...controllerBaseKeyPath, 'columns'], contentController.size) ??
+    content.value([...controllerBaseKeyPath, 'columns'], undefined) ??
+    3;
+
+  return html`
+    <!-- column definitions -->
+    <x-flex space="md">
+      <x-input
+        clearable
+        placeholder="Anzahl Spalten"
+        type="number"
+        ${contentController.field(...controllerBaseKeyPath, 'columns')}
+        @value-picked=${() => {
+          host.requestUpdate();
+        }}
+      ></x-input>
+
+      <x-infobox> Die Summe der Prozentzahl aller Spaltenbreiten sollte max. 100% betragen </x-infobox>
+      ${new Array(countColumns).fill(null).map((width, index) => {
+        return html`
+          <x-input
+            clearable
+            placeholder=${`Breite der ${index + 1}. Spalte`}
+            ${contentController.field(...controllerBaseKeyPath, 'columnWidths', index)}
+          ></x-input>
+        `;
+      })}
+      <!-- END: column definitions -->
+
+      <x-infobox>
+        Um den Abstand der Kacheln innerhalb des Grids zu verändern, kannst du den Wert für den vertikalen oder den
+        horizontalen Abstand vergrößern und verkleinern.
+      </x-infobox>
+
+      <x-buildify-spacing-picker
+        clearable
+        select
+        placeholder="Abstand zwischen den Spalten"
+        ${contentController.field(...controllerBaseKeyPath, 'gridColumnGap')}
+        select
+      ></x-buildify-spacing-picker>
+      <x-buildify-spacing-picker
+        clearable
+        select
+        placeholder="Abstand zwischen den Reihen"
+        ${contentController.field(...controllerBaseKeyPath, 'gridRowGap')}
+        select
+      ></x-buildify-spacing-picker>
+
+      <x-checkbox
+        .label=${'Anordnung der Items im Grid umdrehen'}
+        ${contentController.field(...controllerBaseKeyPath, 'revertDirection')}
+      ></x-checkbox>
+
+      ${content.tableView
+        ? html`
+            <x-infobox> Tabellenoptik </x-infobox>
+            <x-checkbox
+              .label=${'Horizontale Linien hinzufügen'}
+              ${contentController.field(...controllerBaseKeyPath, 'tableView', 'hasHorizontalSeperator')}
+            ></x-checkbox>
+            <x-checkbox
+              .label=${'Vertikale Linien hinzufügen'}
+              ${contentController.field(...controllerBaseKeyPath, 'tableView', 'hasVerticalSeperator')}
+            ></x-checkbox>
+            <x-buildify-spacing-picker
+              placeholder="Breite der Linien"
+              ${contentController.field(...controllerBaseKeyPath, 'tableView', 'thickness')}
+            ></x-buildify-spacing-picker>
+            <x-buildify-color-picker
+              placeholder="Farbe der Linien"
+              ${contentController.field(...controllerBaseKeyPath, 'tableView', 'colorSeperators')}
+            ></x-buildify-color-picker>
+            <x-icon-button
+              icon="delete"
+              text="Tabellenoptik entfernen"
+              @click=${() => {
+                content.tableView = null;
+                host.requestUpdate();
+              }}
+            ></x-icon-button>
+          `
+        : html`
+            <x-icon-button
+              icon="add"
+              text="Tabellenoptik hinzufügen"
+              @click=${() => {
+                content.tableView = new HTMLBaseContainerGridTableView({});
+                host.requestUpdate();
+              }}
+            ></x-icon-button>
+          `}
+      ${ContainerEditor({
+        content,
+        // @ts-expect-error its the same class
+        contentController,
+        controllerBaseKeyPath,
+        host,
+      })}
+    </x-flex>
+  `;
+};
diff --git a/lab/html-based-buildify/client/nodes/grid/grid-cell/config.ts b/lab/html-based-buildify/client/nodes/grid/grid-cell/config.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b0f476dedc2b5fc6ba535fb20c03511fba85eaa3
--- /dev/null
+++ b/lab/html-based-buildify/client/nodes/grid/grid-cell/config.ts
@@ -0,0 +1,13 @@
+import type { Schema } from 'prosemirror-model';
+import { GRID_CELL_KEY, GRID_CELL_SCHEMA } from '../../../../schema/nodes/grid.js';
+import type { INodeConfig } from '../../../types.js';
+import './node-grid-cell.js';
+import { gridCellViewPlugin } from './node-view.js';
+
+export const gridCell: INodeConfig = {
+  node: {
+    name: GRID_CELL_KEY,
+    schema: GRID_CELL_SCHEMA,
+  },
+  plugins: (schema: Schema) => [gridCellViewPlugin],
+};
diff --git a/lab/html-based-buildify/client/nodes/grid/grid-cell/node-grid-cell.ts b/lab/html-based-buildify/client/nodes/grid/grid-cell/node-grid-cell.ts
new file mode 100644
index 0000000000000000000000000000000000000000..35e340b7648c0cd8a51a9d564808e732b3098a34
--- /dev/null
+++ b/lab/html-based-buildify/client/nodes/grid/grid-cell/node-grid-cell.ts
@@ -0,0 +1,152 @@
+import type { Styles } from '@adornis/ass/style.js';
+import { ModeConsumer } from '@adornis/buildify/client/globals/consumer.js';
+import { ChemistryLitElement } from '@adornis/chemistry/chemistry-lit-element.js';
+import { html, type PropertyValues } from 'lit';
+import { customElement, property } from 'lit/decorators.js';
+import type { EditorView } from 'prosemirror-view';
+import { updateNodeAttrs } from '../../../util.js';
+import type { NodeGrid } from '../node-grid.js';
+
+@customElement('node-grid-cell')
+export class NodeGridCell extends ChemistryLitElement {
+  private readonly _consumedMode = ModeConsumer(this);
+
+  @property({ attribute: false }) view?: EditorView;
+  @property({ attribute: false }) getPos?: () => number | undefined;
+
+  @property({ attribute: false }) thickness!: string;
+  @property({ attribute: false }) color!: string;
+  @property({ attribute: false }) rowGap!: number;
+  @property({ attribute: false }) columnGap!: number;
+
+  override updated(changedProperties: PropertyValues): void {
+    super.updated(changedProperties);
+
+    const grid = this.parentElement as NodeGrid;
+    if (!grid) return;
+    const gridData = grid._constructedData.value;
+    const tableView = gridData?.tableView;
+    if (!tableView) return;
+    this.thickness = tableView.thickness ?? '1px';
+    this.color = tableView.colorSeperators ?? '#333';
+
+    const gridColumnGap = gridData.gridColumnGap;
+    const parsedGridColumnGap = Number.parseInt(gridColumnGap?.split('px')[0] ?? '');
+    this.columnGap = parsedGridColumnGap;
+
+    const gridRowGap = gridData.gridRowGap;
+    const parsedGridRowGap = Number.parseInt(gridRowGap?.split('px')[0] ?? '');
+    this.rowGap = parsedGridRowGap;
+
+    if (!this.view || !this.getPos) return;
+
+    const classList = ['grid-item'];
+    // 'children' enthält alle direkten Kind-Elemente des Elternteils
+    const childrenArray = Array.from(grid.children);
+
+    // Bestimmen des Index des 'childElement' innerhalb der 'childrenArray'
+    const index = childrenArray.indexOf(this);
+    const columns = gridData.columns ?? 3;
+    const rows = Math.ceil(childrenArray.length / columns);
+
+    if (index < rows * columns - columns && tableView.hasHorizontalSeperator) {
+      const column = index % columns;
+      if (column === 0) classList.push('line-below-max-right');
+      else if (column === columns - 1) classList.push('line-below-max-left');
+      else {
+        classList.push('line-below-max-right');
+        classList.push('line-below-max-left');
+      }
+      classList.push('grid-line-below');
+    }
+    if (index % columns !== 0 && tableView.hasVerticalSeperator) {
+      const row = Math.floor(index / columns);
+      if (rows > 1) {
+        if (row === 0) classList.push('line-left-max-bottom');
+        else if (row === rows - 1) classList.push('line-left-max-top');
+        else {
+          classList.push('line-left-max-bottom');
+          classList.push('line-left-max-top');
+        }
+      }
+      classList.push('grid-line-left');
+    }
+
+    if (this.classList.length === classList.length && classList.every(item => this.classList.contains(item))) return;
+
+    const { state, dispatch } = this.view;
+    const { tr } = state;
+    updateNodeAttrs(
+      state,
+      tr,
+      () => {
+        return this.getPos!() ?? 0;
+      },
+      { class: classList.join(' ') },
+    );
+    dispatch(tr);
+  }
+
+  override render() {
+    return html` <slot></slot> `;
+  }
+
+  isEditMode() {
+    return this._consumedMode.value === 'edit';
+  }
+
+  override styles() {
+    return [
+      ...super.styles(),
+      {
+        ':host': {
+          border: this.isEditMode() ? '1px solid grey' : '',
+          position: 'relative',
+        },
+        ':host(.grid-item)': {
+          position: 'relative',
+          overflow: 'visible',
+        },
+        ':host(.grid-item) > *': {
+          width: '100%',
+        },
+        ':host(.grid-item.child-container-item) > :first-child': {
+          boxSizing: 'border-box',
+          height: '100%',
+        },
+        ':host(.grid-line-left)::before': {
+          content: "''",
+          position: 'absolute',
+          left: this.columnGap ? `-${this.columnGap / 2}px` : '0',
+          top: '0',
+          bottom: '0',
+          width: this.thickness,
+          background: this.color,
+          zIndex: '10',
+        },
+        ':host(.grid-line-below)::after': {
+          content: "''",
+          position: 'absolute',
+          bottom: this.rowGap ? `-${this.rowGap / 2}px` : '0',
+          height: this.thickness,
+          background: this.color,
+          left: '0',
+          right: '0',
+          zIndex: '10',
+        },
+        ':host(.grid-line-left.line-left-max-bottom)::before': {
+          bottom: this.rowGap ? `-${this.rowGap / 2}px` : '0',
+        },
+        ':host(.grid-line-left.line-left-max-top)::before': {
+          top: this.rowGap ? `-${this.rowGap / 2}px` : '0',
+        },
+        ':host(.grid-line-below.line-below-max-left)::after': {
+          left: this.columnGap ? `-${this.columnGap / 2}px` : '0',
+        },
+        ':host(.grid-line-below.line-below-max-right)::after': {
+          right: this.columnGap ? `-${this.columnGap / 2}px` : '0',
+        },
+      },
+    ] as Styles[];
+  }
+}
diff --git a/lab/html-based-buildify/client/nodes/grid/grid-cell/node-view.ts b/lab/html-based-buildify/client/nodes/grid/grid-cell/node-view.ts
new file mode 100644
index 0000000000000000000000000000000000000000..60702f306c5d482d56829222c302dcf7a8f531cd
--- /dev/null
+++ b/lab/html-based-buildify/client/nodes/grid/grid-cell/node-view.ts
@@ -0,0 +1,53 @@
+import { Node as ProsemirrorNode } from 'prosemirror-model';
+import { Plugin, PluginKey } from 'prosemirror-state';
+import type { EditorView, NodeViewConstructor } from 'prosemirror-view';
+import { GRID_CELL_KEY } from '../../../../schema/nodes/grid.js';
+import type { NodeGridCell } from './node-grid-cell.js';
+
+class GridCellNodeView {
+  view: EditorView;
+  dom: HTMLElement;
+  contentDOM: HTMLElement;
+  node: ProsemirrorNode;
+
+  constructor(node: ProsemirrorNode, view: EditorView, getPos: () => number | undefined) {
+    this.node = node;
+    this.view = view;
+
+    const cell = document.createElement(`node-grid-cell`) as NodeGridCell;
+    cell.setAttribute('class', node.attrs.class);
+    cell.view = view;
+    cell.getPos = getPos;
+    this.dom = cell;
+    this.contentDOM = cell;
+  }
+
+  update(node: ProsemirrorNode): boolean {
+    if (node.attrs.data !== this.node.attrs.data) return false;
+    this.node = node;
+    this.dom.setAttribute('class', node.attrs.class);
+    return true;
+  }
+
+  destroy(): void {
+    // @ts-expect-error still works
+    this.dom = null;
+  }
+}
+
+const gridCellNodeView: NodeViewConstructor = (
+  node: ProsemirrorNode,
+  view: EditorView,
+  getPos: () => number | undefined,
+) => {
+  return new GridCellNodeView(node, view, getPos);
+};
+
+export const gridCellViewPlugin = new Plugin({
+  key: new PluginKey('grid-cell-nodeview-plugin'),
+  props: {
+    nodeViews: {
+      [GRID_CELL_KEY]: gridCellNodeView,
+    },
+  },
+});
diff --git a/lab/html-based-buildify/client/nodes/grid/node-grid.ts b/lab/html-based-buildify/client/nodes/grid/node-grid.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6d16385fda3ddca0a638263c7a4758cfa58459b8
--- /dev/null
+++ b/lab/html-based-buildify/client/nodes/grid/node-grid.ts
@@ -0,0 +1,68 @@
+import type { Styles } from '@adornis/ass/style.js';
+import { css } from '@adornis/chemistry/directives/css.js';
+import { html, nothing } from 'lit';
+import { customElement, property } from 'lit/decorators.js';
+import { Node as ProsemirrorNode } from 'prosemirror-model';
+import type { EditorView } from 'prosemirror-view';
+import type { HTMLBaseContainerGrid } from '../../../db/HTMLBaseContainerGrid.js';
+import { BuildifyLitElement } from '../../BuildifyLitElement.js';
+
+@customElement('node-grid')
+export class NodeGrid extends BuildifyLitElement<HTMLBaseContainerGrid> {
+  @property({ attribute: false }) view?: EditorView;
+  @property({ attribute: false }) node?: ProsemirrorNode;
+  @property({ attribute: false }) getPos?: () => number | undefined;
+
+  override content({ content, mode }: { content?: HTMLBaseContainerGrid; mode }) {
+    const columns = content?.columns ? content.columns : 3;
+    const templateColumns: string[] = [];
+    for (let i = 0; i < columns; i++) {
+      const templateValue = content?.getColumnWidthByIndex(i) ?? '1fr';
+      templateColumns.push(templateValue);
+    }
+
+    return html`
+      <x-grid
+        columns=${templateColumns.join(' ')}
+        ${css({
+          gridColumnGap: content?.gridColumnGap ?? '',
+          gridRowGap: content?.gridRowGap ?? '',
+          padding: content?.padding ?? '',
+          background: content?.backgroundColor ?? '',
+        })}
+      >
+        <slot></slot>
+        ${mode === 'edit'
+          ? html`
+              <x-flex
+                ${css({ border: '1px lightgrey dashed', userSelect: 'none', cursor: 'pointer' })}
+                center
+                crossaxis-center
+                @click=${e => {
+                  e.preventDefault();
+                  e.stopPropagation();
+                  this.append((document.createElement('node-grid-cell').innerText = 'Hier tippen...'));
+                }}
+              >
+                Zelle hinzufügen
+              </x-flex>
+            `
+          : nothing}
+      </x-grid>
+    `;
+  }
+
+  override styles() {
+    return [
+      ...super.styles(),
+      {
+        ':host': {
+          borderRadius: this._data.value?.borderRadius ?? '',
+        },
+        'x-grid>*::slotted(*)': {
+          boxSizing: 'border-box',
+        },
+      },
+    ] as Styles[];
+  }
+}
diff --git a/lab/html-based-buildify/client/nodes/grid/node-view.ts b/lab/html-based-buildify/client/nodes/grid/node-view.ts
new file mode 100644
index 0000000000000000000000000000000000000000..75173191c0c5e27c0cebba45e199ef8754cede0b
--- /dev/null
+++ b/lab/html-based-buildify/client/nodes/grid/node-view.ts
@@ -0,0 +1,59 @@
+// import { Node as ProsemirrorNode } from 'prosemirror-model';
+// import { Plugin, PluginKey } from 'prosemirror-state';
+// import type { Decoration, DecorationSource, EditorView, NodeViewConstructor } from 'prosemirror-view';
+// import { GRID_KEY } from '../../../schema/nodes/grid.js';
+// import type { NodeGrid } from './node-grid.js';
+
+// class GridNodeView {
+//   view: EditorView;
+//   dom: HTMLElement;
+//   contentDOM: HTMLElement;
+//   node: ProsemirrorNode;
+
+//   constructor(
+//     node: ProsemirrorNode,
+//     view: EditorView,
+//     getPos: () => number | undefined,
+//     decorations: readonly Decoration[],
+//     innerDecorations: DecorationSource,
+//   ) {
+//     this.node = node;
+//     this.view = view;
+
+//     const nodeGrid = document.createElement(`node-grid`) as NodeGrid;
+//     nodeGrid.setAttribute('data', node.attrs.data);
+//     this.dom = nodeGrid;
+//     this.contentDOM = nodeGrid;
+//   }
+
+//   update(node: ProsemirrorNode): boolean {
+//     if (node.attrs.data !== this.node.attrs.data) return false;
+//     this.node = node;
+//     this.dom.setAttribute('data', node.attrs.data);
+//     return true;
+//   }
+
+//   destroy(): void {
+//     // @ts-expect-error still works
+//     this.dom = null;
+//   }
+// }
+
+// const gridNodeView: NodeViewConstructor = (
+//   node: ProsemirrorNode,
+//   view: EditorView,
+//   getPos: () => number | undefined,
+//   decorations: readonly Decoration[],
+//   innerDecorations: DecorationSource,
+// ) => {
+//   return new GridNodeView(node, view, getPos, decorations, innerDecorations);
+// };
+
+// export const gridViewPlugin = new Plugin({
+//   key: new PluginKey('grid-nodeview-plugin'),
+//   props: {
+//     nodeViews: {
+//       [GRID_KEY]: gridNodeView,
+//     },
+//   },
+// });
diff --git a/lab/html-based-buildify/client/nodes/iframe.ts b/lab/html-based-buildify/client/nodes/iframe.ts
new file mode 100644
index 0000000000000000000000000000000000000000..97542eea32206069cb4261989f05453a4ae5bd60
--- /dev/null
+++ b/lab/html-based-buildify/client/nodes/iframe.ts
@@ -0,0 +1,49 @@
+import '@adornis/buildify/client/components/x-buildify-color-picker';
+import '@adornis/buildify/client/components/x-buildify-spacing-picker';
+import { css } from '@adornis/chemistry/directives/css.js';
+import '@adornis/chemistry/elements/components/x-flex';
+import '@adornis/chemistry/elements/components/x-icon';
+import '@adornis/forms/x-checkbox.js';
+import '@adornis/forms/x-input';
+import { html } from 'lit';
+import { customElement } from 'lit/decorators.js';
+import type { HTMLBaseIframe } from '../../db/HTMLBaseIframe.js';
+import { IFRAME_KEY, IFRAME_SCHEMA } from '../../schema/nodes/iframe.js';
+import { BuildifyLitElement } from '../BuildifyLitElement.js';
+import { runInsertNode } from '../helper/runInsertNode.js';
+import { type EditorFunc, type INodeConfig } from '../types.js';
+import { isNodeSelection } from '../util.js';
+
+export const editor: EditorFunc<HTMLBaseIframe> = ({ content, contentController, controllerBaseKeyPath, host }) => {
+  return html`
+    <x-flex space="md">
+      <x-input placeholder="Url*" ${contentController.field(...controllerBaseKeyPath, 'url')}></x-input>
+      <x-input placeholder="Höhe" ${contentController.field(...controllerBaseKeyPath, 'height')}></x-input>
+    </x-flex>
+  `;
+};
+
+export const iframe: INodeConfig = {
+  node: {
+    name: IFRAME_KEY,
+    schema: IFRAME_SCHEMA,
+  },
+  menuItems: [
+    {
+      label: 'iFrame',
+      icon: 'frame_inspect',
+      run: view => runInsertNode(IFRAME_KEY)(view.state, view),
+      shouldVisualize: view => {
+        return !isNodeSelection(view.state.selection);
+      },
+    },
+  ],
+  editor,
+};
+
+@customElement('node-iframe')
+export class NodeIframe extends BuildifyLitElement<HTMLBaseIframe> {
+  override content({ content, mode }) {
+    return html` <iframe ${css({ width: '100%', height: content.height ?? '500px' })} src=${content.url}></iframe> `;
+  }
+}
diff --git a/lab/html-based-buildify/client/prosemirror-editor.ts b/lab/html-based-buildify/client/prosemirror-editor.ts
index 596801ef4860670528e082ec4b8b8ea0dd267272..c011f8bb32aca954ce198a938a4f16532a156263 100644
--- a/lab/html-based-buildify/client/prosemirror-editor.ts
+++ b/lab/html-based-buildify/client/prosemirror-editor.ts
@@ -27,7 +27,10 @@ import { EditorView } from 'prosemirror-view';
 import { combineLatest, debounceTime, distinctUntilChanged, filter, switchMap, takeUntil } from 'rxjs';
 import { Contexts, Size } from '../db/enums.js';
 import { colorMenuItem } from './marks/color.js';
+import { createFontSizeMenuItem } from './marks/font-size.js';
 import { createTextAlignMenuItem } from './marks/text-align.js';
+import { CollapsibleMenuItem } from './menu-items/CollapsibleMenuItem.js';
+import { CopySelectionMenuItem, PasteFromClipboardMenuItem } from './menu-items/CopySelectionToClipboard.js';
 import { DeleteSelectedNodeMenuItem } from './menu-items/DeleteSelectedNode.js';
 import { EditGlobalSettingsMenuItem } from './menu-items/EditGlobalSettings.js';
 import { EditSelectedNodeMenuItem } from './menu-items/EditSelectedNode.js';
@@ -69,6 +72,16 @@ export class ProsemirrorEditor extends FormField<string> {
     createTextAlignMenuItem('center'),
     createTextAlignMenuItem('right'),
     createTextAlignMenuItem('justify'),
+    new CollapsibleMenuItem({
+      label: 'Schriftgröße',
+      icon: 'text_fields',
+      menuItems: [
+        createFontSizeMenuItem('8px'),
+        createFontSizeMenuItem('12px'),
+        createFontSizeMenuItem('16px'),
+        createFontSizeMenuItem('20px'),
+      ],
+    }),
   ]);
 
   @property({ attribute: false }) globalSettingsClassName = new RXController(this, BuildifyGlobalSettings._class);
@@ -196,6 +209,8 @@ export class ProsemirrorEditor extends FormField<string> {
     menuItems.push(EditSelectedNodeMenuItem(this.getEditors(), this.globalSettingsClassName.value));
     menuItems.push(DeleteSelectedNodeMenuItem);
     menuItems.push(EditGlobalSettingsMenuItem(this.globalSettingsClassName.value));
+    menuItems.push(CopySelectionMenuItem);
+    menuItems.push(PasteFromClipboardMenuItem);
 
     return menuItems;
   }
@@ -219,13 +234,11 @@ export class ProsemirrorEditor extends FormField<string> {
       .pipe(filter(Boolean), distinctUntilChanged())
       .subscribe(globalSettingsClassName => {
         this._globalSettingsClassNameProvider.setValue(globalSettingsClassName, true);
-        console.log('global settings classname changed!');
       });
 
     combineLatest([this.configs.observable, this.additionalMenuItems.observable, this._globalSettings.observable])
       .pipe(debounceTime(300))
       .subscribe(([configs, additionalMenuItems, globalSettings]) => {
-        console.log('call update editor');
         this.updateEditor();
       });
 
diff --git a/lab/html-based-buildify/client/starterPack.ts b/lab/html-based-buildify/client/starterPack.ts
index 8aae97fac45468e84ea9923ebc56577cf333247f..e641282169d6c33794a0b57370c0c7a59ba370c7 100644
--- a/lab/html-based-buildify/client/starterPack.ts
+++ b/lab/html-based-buildify/client/starterPack.ts
@@ -11,11 +11,13 @@ import { doc } from './nodes/doc.js';
 import { excalidraw } from './nodes/excalidraw.js';
 import { file } from './nodes/file.js';
 import { flex } from './nodes/flex.js';
-import { grid, gridCell } from './nodes/grid.js';
+import { grid } from './nodes/grid/config.js';
+import { gridCell } from './nodes/grid/grid-cell/config.js';
 import { hardBreak } from './nodes/hard-break.js';
 import { heading } from './nodes/headings.js';
 import { icon } from './nodes/icon.js';
 import { iconText, iconWrapper, textWrapper } from './nodes/iconText.js';
+import { iframe } from './nodes/iframe.js';
 import { image } from './nodes/image.js';
 import { bulletList, listItem, orderedList } from './nodes/list.js';
 import { paragraph } from './nodes/paragraph.js';
@@ -63,4 +65,5 @@ export const startedPack: Array<INodeConfig | IMarkConfig> = [
   tag,
   vimeo,
   youtube,
+  iframe,
 ];
diff --git a/lab/html-based-buildify/client/types.ts b/lab/html-based-buildify/client/types.ts
index 02e7ed1191f3b6527313b00c6dbc5c36ec4f5ba7..060302bac2170a280ae28f32c9730aa4611274a8 100644
--- a/lab/html-based-buildify/client/types.ts
+++ b/lab/html-based-buildify/client/types.ts
@@ -31,6 +31,7 @@ export interface IMenuItem {
   run: (editorView: EditorView) => void;
   isActive?: (editorView: EditorView) => boolean;
   shouldVisualize?: (editorView: EditorView) => boolean;
+  render?: (editorView: EditorView) => TemplateResult;
 }
 
 export interface IEditorConfigBase {
diff --git a/lab/html-based-buildify/client/x-prosemirror-toolbar.ts b/lab/html-based-buildify/client/x-prosemirror-toolbar.ts
index 67876ddd6c444306a43bdb1eace86dbe5c30391e..77175edae484a6939117b906063630b0b2b948d1 100644
--- a/lab/html-based-buildify/client/x-prosemirror-toolbar.ts
+++ b/lab/html-based-buildify/client/x-prosemirror-toolbar.ts
@@ -1,14 +1,94 @@
 import type { Styles } from '@adornis/ass/style.js';
 import { ChemistryLitElement } from '@adornis/chemistry/chemistry-lit-element.js';
+import { acss } from '@adornis/chemistry/directives/acss.js';
 import { css } from '@adornis/chemistry/directives/css.js';
 import '@adornis/chemistry/elements/components/x-flex';
 import '@adornis/chemistry/elements/components/x-icon';
+import '@adornis/fonts/fonts';
 import '@adornis/popover/x-tooltip';
 import { html, nothing, type TemplateResult } from 'lit';
 import { customElement, property } from 'lit/decorators.js';
 import { EditorView } from 'prosemirror-view';
 import type { IMenuItem } from './types.js';
 
+export function renderMenuItemIcon({
+  view,
+  isActive,
+  icon,
+  run,
+  color,
+}: {
+  view: EditorView;
+  isActive: boolean;
+  icon: string;
+  run: IMenuItem['run'];
+  color?: IMenuItem['color'];
+}) {
+  return html`
+    <x-icon
+      ${css({
+        fontSize: '28px',
+        cursor: 'pointer',
+        padding: '2px',
+        background: isActive ? '#F2F2F1' : 'transparent',
+        borderRadius: '4px',
+        color: color ?? '#333',
+      })}
+      @click=${() => {
+        if (!run) return;
+        run(view);
+      }}
+    >
+      ${icon}
+    </x-icon>
+  `;
+}
+
+export function renderMenuItem(item: IMenuItem, view: EditorView): TemplateResult | typeof nothing {
+  if (item.shouldVisualize && !item.shouldVisualize(view)) return nothing;
+  if (item.render)
+    return html`
+      <div
+        @click=${() => {
+          if (!item.run) return;
+          item.run(view);
+        }}
+      >
+        ${item.render(view)}
+      </div>
+    `;
+
+  const isActive = item.isActive?.(view) ?? false;
+
+  return html`
+    <x-flex>
+      ${item.label ? html` <x-tooltip .text=${item.label}></x-tooltip> ` : nothing}
+      ${item.icon
+        ? renderMenuItemIcon({ view, isActive, icon: item.icon, run: item.run, color: item.color })
+        : html`
+            <x-flex
+              ${acss({
+                cursor: 'pointer',
+                background: isActive ? '#4b6584' : '',
+                color: isActive ? '#fff' : '',
+                padding: '8px',
+                borderRadius: '4px',
+                '&:hover': {
+                  background: isActive ? '' : '#F2F2F1',
+                },
+              })}
+              @click=${() => {
+                if (!item.run) return;
+                item.run(view);
+              }}
+            >
+              <x-text> ${item.label} </x-text>
+            </x-flex>
+          `}
+    </x-flex>
+  `;
+}
+
 @customElement('x-prosemirror-toolbar')
 export class XProsemirrorToolbar extends ChemistryLitElement {
   @property({ attribute: false }) menuItems: IMenuItem[] = [];
@@ -31,61 +111,18 @@ export class XProsemirrorToolbar extends ChemistryLitElement {
     for (const group of groups) {
       const items = this._getAllMenuItemsForGroup(group);
       for (const item of items) {
-        result.push(this._renderMenuItem(item, view));
+        result.push(renderMenuItem(item, view));
       }
     }
 
     // append other items
     const notGroupedItems = this.menuItems.filter(item => !item.group || !groups.includes(item.group));
     for (const item of notGroupedItems) {
-      result.push(this._renderMenuItem(item, view));
+      result.push(renderMenuItem(item, view));
     }
     return result;
   }
 
-  private _renderMenuItem(item: IMenuItem, view: EditorView) {
-    if (item.shouldVisualize && !item.shouldVisualize(view)) return nothing;
-
-    const isActive = item.isActive?.(view);
-    const style = isActive ? 'background: #4b6584; color: #fff;' : '';
-
-    return html`
-      <x-flex>
-        ${item.label ? html` <x-tooltip .text=${item.label}></x-tooltip> ` : nothing}
-        ${item.icon
-          ? html`
-              <x-icon
-                ${css({
-                  fontSize: '28px',
-                  cursor: 'pointer',
-                  padding: '2px',
-                  background: isActive ? '#F2F2F1' : 'transparent',
-                  borderRadius: '4px',
-                  color: item.color ?? '#333',
-                })}
-                @click=${() => {
-                  if (!item.run) return;
-                  item.run(view);
-                }}
-              >
-                ${item.icon}
-              </x-icon>
-            `
-          : html`
-              <button
-                style=${style}
-                @click=${() => {
-                  if (!item.run) return;
-                  item.run(view);
-                }}
-              >
-                ${item.label}
-              </button>
-            `}
-      </x-flex>
-    `;
-  }
-
   private _getAllMenuItemsForGroup(group?: string) {
     return this.menuItems.filter(item => item.group === group);
   }
diff --git a/lab/html-based-buildify/db/HTMLBaseIframe.ts b/lab/html-based-buildify/db/HTMLBaseIframe.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9d6dd10230c437b3a2979407adbd582f1bbd7507
--- /dev/null
+++ b/lab/html-based-buildify/db/HTMLBaseIframe.ts
@@ -0,0 +1,16 @@
+import { Entity, Field } from '@adornis/baseql/decorators.js';
+import { validate } from '@adornis/validation/decorators.js';
+import { nonOptional } from '@adornis/validation/functions/nonOptional.js';
+import { HTMLBase } from './HTMLBase.js';
+
+@Entity()
+export class HTMLBaseIframe extends HTMLBase {
+  static override _class = 'HTMLBaseIframe';
+
+  @validate(nonOptional())
+  @Field(type => String)
+  url!: string;
+
+  @Field(type => String)
+  height!: string;
+}
diff --git a/lab/html-based-buildify/schema/nodes/grid.ts b/lab/html-based-buildify/schema/nodes/grid.ts
index b9474f4c4ddd5d5e979fd8df2736ea838eed0ef7..79aab96b9459d69b6ef0166b750bd58c832a5c6b 100644
--- a/lab/html-based-buildify/schema/nodes/grid.ts
+++ b/lab/html-based-buildify/schema/nodes/grid.ts
@@ -1,9 +1,11 @@
+import { html } from 'lit';
 import type { NodeSpec } from 'prosemirror-model';
+import { toDOMByTemplate } from '../../client/util.js';
 import { HTMLBaseContainerGrid } from '../../db/HTMLBaseContainerGrid.js';
 
 export const GRID_CELL_SCHEMA: NodeSpec = {
   attrs: {
-    class: { default: null },
+    class: { default: '' },
   },
   content: 'block+',
   group: 'gridCell',
@@ -46,7 +48,7 @@ export const GRID_SCHEMA: NodeSpec = {
     },
   ],
   toDOM: node => {
-    return ['node-grid', { data: node.attrs.data }, 0];
+    return toDOMByTemplate(html` <node-grid data=${node.attrs.data}></node-grid> `);
   },
 };
 
diff --git a/lab/html-based-buildify/schema/nodes/iframe.ts b/lab/html-based-buildify/schema/nodes/iframe.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e53faa05b2c5014fa06495137372deaf374c9df2
--- /dev/null
+++ b/lab/html-based-buildify/schema/nodes/iframe.ts
@@ -0,0 +1,30 @@
+import type { NodeSpec } from 'prosemirror-model';
+import { HTMLBaseIframe } from '../../db/HTMLBaseIframe.js';
+
+export const IFRAME_SCHEMA: NodeSpec = {
+  draggable: true,
+  atom: true,
+  isolating: true,
+  group: 'block',
+  attrs: {
+    data: { default: JSON.stringify(new HTMLBaseIframe({}).toObject()) },
+  },
+  parseDOM: [
+    {
+      tag: 'node-iframe',
+      getAttrs(dom: HTMLElement) {
+        return {
+          data: dom.getAttribute('data'),
+        };
+      },
+    },
+  ],
+  toDOM: node => {
+    return ['node-iframe', { data: node.attrs.data, 'no-content': 'true' }];
+  },
+};
+
+export const IFRAME_KEY = 'iframe';
+export const IFRAME = {
+  [IFRAME_KEY]: IFRAME_SCHEMA,
+};
diff --git a/lab/wiki/client/x-wiki-editor.ts b/lab/wiki/client/x-wiki-editor.ts
index 240b4120b6914378946f1859a0b017c708434655..c81da515e296a66217700cf8a91657d2d76820a9 100644
--- a/lab/wiki/client/x-wiki-editor.ts
+++ b/lab/wiki/client/x-wiki-editor.ts
@@ -710,6 +710,7 @@ export class XWikiEditor extends ChemistryLitElement {
         .value=${this._entry.value?.content}
         .configs=${[
           ...startedPack,
+          ...this.additionalConfigs,
           embedding(
             (this._isDraft() ? (this._entry.value as Maybe<WikiEntryDraft>)?.referenceID : this._entry.value?._id) ??
               '',