{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "workbench",
  "type": "registry:component",
  "title": "Workbench",
  "description": "The frame of an editor: a canvas with the work on it, a toolbar over it, an inspector beside it and a timeline under it.",
  "dependencies": [
    "@base-ui/react",
    "tailwind-variants"
  ],
  "registryDependencies": [
    "https://panelui.dev/r/web/sheet.json"
  ],
  "files": [
    {
      "path": "components/panelui/workbench.tsx",
      "type": "registry:component",
      "target": "@components/panelui/workbench.tsx",
      "content": "'use client';\n\n/**\n * Workbench — the frame of an editor: a canvas with the work on it, a\n * toolbar floating over it, an inspector beside it and a timeline under it.\n *\n * ```tsx\n * <Workbench className=\"h-[640px]\">\n *   <Workbench.Bar><Toolbar>…</Toolbar></Workbench.Bar>\n *   <Workbench.Canvas>\n *     <Workbench.Artboard aspect=\"16 / 9\">…</Workbench.Artboard>\n *   </Workbench.Canvas>\n *   <Workbench.Inspector><Inspector>…</Inspector></Workbench.Inspector>\n *   <Workbench.Timeline><Timeline>…</Timeline></Workbench.Timeline>\n * </Workbench>\n * ```\n *\n * ## The work is in the middle\n *\n * Everything else stands round the canvas as a panel of its own, floating\n * a few pixels clear of its neighbours, so the work reads as the thing on\n * the desk and the tools as things beside it. The artboard sits centred on\n * a field of dots at the largest size its shape fits.\n *\n * ## Focus clears the desk\n *\n * In focus the sidebar, the inspector and the timeline slide away and the\n * canvas takes the room they leave. `\\` toggles it from the keyboard, and\n * `useWorkbench()` gives a button of your own the same switch.\n *\n * ## On a narrow screen the inspector becomes a sheet\n *\n * Below `compactBelow` pixels wide there is no room for a column beside the\n * canvas. The inspector then opens as a sheet from the side, and\n * `Workbench.InspectorTrigger` is the button that opens it. The width that\n * counts is the workbench's own, not the window's, so it works in a pane.\n */\nimport {\n  createContext,\n  useCallback,\n  useContext,\n  useEffect,\n  useMemo,\n  useRef,\n  useState,\n  type ComponentProps,\n  type CSSProperties,\n  type KeyboardEvent,\n  type PointerEvent as ReactPointerEvent,\n  type ReactNode,\n} from 'react';\nimport { tv } from 'tailwind-variants';\nimport { Sheet } from '@/components/panelui/sheet';\nimport { mergeProps } from '@base-ui/react/merge-props';\nimport { useRender } from '@base-ui/react/use-render';\nimport type * as ReactTypes from 'react';\nimport type { ElementType as RenderElementType } from 'react';\n\n/** Draws a part as another element, or composes it with a component: `render={<a href=\"/\" />}`. */\ntype RenderProp<Tag extends RenderElementType> = { render?: useRender.ComponentProps<Tag>['render'] };\n\nconst EASE = 'ease-[cubic-bezier(0.32,0.72,0,1)]';\n\nconst workbenchVariants = tv({\n  slots: {\n    root: [\n      'group/workbench relative grid w-full min-w-0 overflow-hidden rounded-[26px] bg-muted/60 p-2 text-foreground',\n      'ring-1 ring-foreground/[0.06] dark:bg-muted/40 dark:ring-foreground/10',\n      '[grid-template-areas:\"sidebar_canvas_inspector\"\"timeline_timeline_timeline\"]',\n      'grid-cols-[var(--wb-sidebar)_minmax(0,1fr)_var(--wb-inspector)] grid-rows-[minmax(0,1fr)_var(--wb-timeline)]',\n      // No grid gap: each panel carries its own 8px on the canvas side, so a\n      // column that closes takes its gap with it.\n      `transition-[grid-template-columns,grid-template-rows] duration-500 ${EASE} motion-reduce:transition-none`,\n      'data-resizing:transition-none',\n    ],\n    sidebar: [\n      'flex min-h-0 min-w-0 flex-col overflow-hidden pe-2 [grid-area:sidebar]',\n      `transition-[opacity,translate] duration-500 ${EASE} motion-reduce:transition-none`,\n      'group-data-focus/workbench:-translate-x-4 group-data-focus/workbench:opacity-0',\n    ],\n    canvas: [\n      'relative isolate flex min-h-0 min-w-0 items-center justify-center overflow-hidden rounded-[20px] px-6 pt-[4.5rem] pb-6 [grid-area:canvas] [container-type:size]',\n      // The top padding is the floating bar's room, so the work is centred in what is left.\n      'bg-background ring-1 ring-foreground/[0.05] dark:bg-background/70',\n      'bg-[radial-gradient(color-mix(in_oklab,var(--foreground)_13%,transparent)_1px,transparent_1.5px)] bg-size-[20px_20px] bg-center',\n    ],\n    artboard: [\n      'relative overflow-hidden rounded-xl bg-background shadow-[0_24px_60px_-24px_rgb(0_0_0/0.45)] ring-1 ring-foreground/10',\n      'aspect-(--wb-aspect) w-[min(100cqw,100cqh*var(--wb-ratio))]',\n      `transition-[width] duration-500 ${EASE} motion-reduce:transition-none`,\n    ],\n    bar: 'pointer-events-none z-10 mt-3 flex justify-center self-start justify-self-center [grid-area:canvas] *:pointer-events-auto',\n    inspector: [\n      'flex min-h-0 min-w-0 justify-end overflow-hidden ps-2 [grid-area:inspector]',\n      `transition-[opacity,translate] duration-500 ${EASE} motion-reduce:transition-none`,\n      'group-data-focus/workbench:translate-x-4 group-data-focus/workbench:opacity-0',\n      // The panel inside fills the column's height.\n      '[&_[data-slot=inspector]]:h-full [&_[data-slot=inspector-panel]]:h-full',\n    ],\n    timeline: [\n      'relative flex min-h-0 min-w-0 flex-col overflow-hidden pt-2 [grid-area:timeline]',\n      `transition-[opacity,translate] duration-500 ${EASE} motion-reduce:transition-none`,\n      'group-data-focus/workbench:translate-y-4 group-data-focus/workbench:opacity-0',\n      '[&>[data-slot=timeline]]:min-h-0 [&>[data-slot=timeline]]:flex-1',\n    ],\n    resize: [\n      'absolute inset-x-0 top-0 z-20 flex h-2 cursor-row-resize items-center justify-center outline-none',\n      \"after:h-1 after:w-10 after:rounded-full after:bg-foreground/20 after:transition-[background-color,width] after:duration-150 after:content-['']\",\n      'hover:after:w-16 hover:after:bg-foreground/40 focus-visible:after:w-16 focus-visible:after:bg-ring data-active:after:w-16 data-active:after:bg-foreground/50',\n    ],\n    trigger: [\n      'inline-flex size-9 shrink-0 cursor-pointer items-center justify-center rounded-xl text-muted-foreground outline-none',\n      'transition-[color,background-color,scale] duration-150 hover:bg-foreground/[0.06] hover:text-foreground active:scale-[0.94]',\n      'focus-visible:ring-2 focus-visible:ring-ring [&_svg]:size-[1.125rem] [&_svg]:shrink-0',\n    ],\n    // In the sheet, the sheet is the surface: the inspector's own card gives\n    // up its fill, ring and shadow rather than stand as a box in a box.\n    sheetBody: [\n      'flex min-h-0 flex-1 px-2 pb-2 [&_[data-slot=inspector]]:h-full [&_[data-slot=inspector]]:w-full',\n      '[&_[data-slot=inspector-panel]]:h-full [&_[data-slot=inspector-panel]]:w-full [&_[data-slot=inspector-panel]]:bg-transparent',\n      '[&_[data-slot=inspector-panel]]:shadow-none [&_[data-slot=inspector-panel]]:ring-0',\n    ],\n  },\n});\n\ninterface WorkbenchContextValue {\n  focus: boolean;\n  setFocus(focus: boolean): void;\n  compact: boolean;\n  inspectorOpen: boolean;\n  setInspectorOpen(open: boolean): void;\n  hasSidebar(present: boolean): void;\n  hasInspector(present: boolean): void;\n  hasTimeline(present: boolean): void;\n  timelineHeight: number;\n  setTimelineHeight(height: number): void;\n  setResizing(resizing: boolean): void;\n  rootHeight(): number;\n}\n\nconst WorkbenchContext = createContext<WorkbenchContextValue | null>(null);\n\nfunction useWorkbenchContext(part: string) {\n  const context = useContext(WorkbenchContext);\n  if (!context) throw new Error(`${part} must be used inside a <Workbench>.`);\n  return context;\n}\n\n/**\n * The workbench's state, for controls of your own: a focus button in the\n * toolbar, a button that opens the inspector on a narrow screen.\n */\nexport function useWorkbench() {\n  const { focus, setFocus, compact, inspectorOpen, setInspectorOpen } = useWorkbenchContext('useWorkbench');\n  return { focus, setFocus, compact, inspectorOpen, setInspectorOpen };\n}\n\nexport interface WorkbenchProps extends ComponentProps<'div'> {\n  /** Whether the panels are cleared away, when the caller holds it. */\n  focus?: boolean;\n  /** Whether it starts in focus. Default false. */\n  defaultFocus?: boolean;\n  /** Called when focus turns on or off. */\n  onFocusChange?: (focus: boolean) => void;\n  /** Toggle focus with the `\\` key while nothing is being typed. Default true. */\n  focusShortcut?: boolean;\n  /** Below this many pixels wide, the inspector opens as a sheet. Default 760. */\n  compactBelow?: number;\n  /** The inspector column's width in pixels. Default 348, a rail and a panel. */\n  inspectorWidth?: number;\n  /** The sidebar's width in pixels. Default 240. */\n  sidebarWidth?: number;\n  /** The timeline's height in pixels to start at. Default 232. */\n  defaultTimelineHeight?: number;\n}\n\n/** The editor's frame. Give it a height, such as `h-[640px]` or `h-dvh`. */\nfunction WorkbenchRoot({\n  focus: focusProp,\n  defaultFocus = false,\n  onFocusChange,\n  focusShortcut = true,\n  compactBelow = 760,\n  inspectorWidth = 348,\n  sidebarWidth = 240,\n  defaultTimelineHeight = 232,\n  className,\n  style,\n  children,\n  ...props\n}: WorkbenchProps) {\n  const slots = workbenchVariants();\n  const [innerFocus, setInnerFocus] = useState(defaultFocus);\n  const focus = focusProp ?? innerFocus;\n  const [compact, setCompact] = useState(false);\n  const [inspectorOpen, setInspectorOpen] = useState(false);\n  const [parts, setParts] = useState({ sidebar: false, inspector: false, timeline: false });\n  const [timelineHeight, setTimelineHeight] = useState(defaultTimelineHeight);\n  const [resizing, setResizing] = useState(false);\n  const root = useRef<HTMLDivElement>(null);\n\n  const live = useRef({ focus, onFocusChange, controlled: focusProp !== undefined });\n  live.current = { focus, onFocusChange, controlled: focusProp !== undefined };\n  const setFocus = useCallback((next: boolean) => {\n    if (!live.current.controlled) setInnerFocus(next);\n    live.current.onFocusChange?.(next);\n  }, []);\n\n  // The workbench's own width decides, so it works in a pane as on a page.\n  useEffect(() => {\n    const box = root.current;\n    if (!box) return;\n    const observer = new ResizeObserver(() => setCompact(box.clientWidth < compactBelow));\n    observer.observe(box);\n    return () => observer.disconnect();\n  }, [compactBelow]);\n  useEffect(() => {\n    if (!compact) setInspectorOpen(false);\n  }, [compact]);\n\n  useEffect(() => {\n    if (!focusShortcut) return;\n    const onKey = (event: globalThis.KeyboardEvent) => {\n      if (event.key !== '\\\\' || event.metaKey || event.ctrlKey || event.altKey || event.defaultPrevented) return;\n      const target = event.target;\n      if (\n        target instanceof Element &&\n        target.closest('input, textarea, select, [contenteditable=\"\"], [contenteditable=\"true\"]')\n      ) {\n        return;\n      }\n      event.preventDefault();\n      setFocus(!live.current.focus);\n    };\n    document.addEventListener('keydown', onKey);\n    return () => document.removeEventListener('keydown', onKey);\n  }, [focusShortcut, setFocus]);\n\n  const mark = (part: keyof typeof parts) => (present: boolean) =>\n    setParts((all) => (all[part] === present ? all : { ...all, [part]: present }));\n\n  const context = useMemo<WorkbenchContextValue>(\n    () => ({\n      focus,\n      setFocus,\n      compact,\n      inspectorOpen,\n      setInspectorOpen,\n      hasSidebar: mark('sidebar'),\n      hasInspector: mark('inspector'),\n      hasTimeline: mark('timeline'),\n      timelineHeight,\n      setTimelineHeight,\n      setResizing,\n      rootHeight: () => root.current?.clientHeight ?? 0,\n    }),\n    [focus, setFocus, compact, inspectorOpen, timelineHeight]\n  );\n\n  const showSidebar = parts.sidebar && !focus && !compact;\n  const showInspector = parts.inspector && !focus && !compact;\n  const showTimeline = parts.timeline && !focus;\n\n  return (\n    <WorkbenchContext.Provider value={context}>\n      <div\n        ref={root}\n        data-slot=\"workbench\"\n        data-focus={focus ? '' : undefined}\n        data-compact={compact ? '' : undefined}\n        data-resizing={resizing ? '' : undefined}\n        className={slots.root({ className })}\n        style={\n          {\n            ...style,\n            '--wb-sidebar': showSidebar ? `${sidebarWidth}px` : '0px',\n            '--wb-inspector': showInspector ? `${inspectorWidth}px` : '0px',\n            '--wb-timeline': showTimeline ? `${timelineHeight}px` : '0px',\n          } as CSSProperties\n        }\n        {...props}\n      >\n        {children}\n      </div>\n    </WorkbenchContext.Provider>\n  );\n}\nWorkbenchRoot.displayName = 'Workbench';\n\n/** Tells the workbench a part is in it, so its column or row gets room. */\nfunction usePresence(register: (present: boolean) => void) {\n  useEffect(() => {\n    register(true);\n    return () => register(false);\n  }, []);\n}\n\nexport interface WorkbenchBarProps extends ComponentProps<'div'> {}\n\n/** The strip floating at the top of the canvas, for a toolbar. */\nfunction WorkbenchBar({ className, render, ...props }: WorkbenchBarProps & RenderProp<'div'>) {\n  useWorkbenchContext('Workbench.Bar');\n  const slots = workbenchVariants();\n  return useRender({\n    defaultTagName: 'div',\n    render,\n    props: mergeProps({ 'data-slot': 'workbench-bar', className: slots.bar({ className }) }, props),\n  });\n}\nWorkbenchBar.displayName = 'Workbench.Bar';\n\nexport interface WorkbenchSidebarProps extends ComponentProps<'aside'> {}\n\n/** A column before the canvas, for layers, pages or assets. It is hidden on a narrow screen and in focus. */\nfunction WorkbenchSidebar({ className, render, ...props }: WorkbenchSidebarProps & RenderProp<'aside'>) {\n  const workbench = useWorkbenchContext('Workbench.Sidebar');\n  usePresence(workbench.hasSidebar);\n  const slots = workbenchVariants();\n  return useRender({\n    defaultTagName: 'aside',\n    render,\n    props: mergeProps(\n      {\n        'data-slot': 'workbench-sidebar',\n        inert: workbench.focus || workbench.compact || undefined,\n        className: slots.sidebar({ className }),\n      },\n      props\n    ),\n  });\n}\nWorkbenchSidebar.displayName = 'Workbench.Sidebar';\n\nexport interface WorkbenchCanvasProps extends ComponentProps<'div'> {}\n\n/** The stage in the middle: a field of dots with the work centred on it. */\nfunction WorkbenchCanvas({ className, render, ...props }: WorkbenchCanvasProps & RenderProp<'div'>) {\n  useWorkbenchContext('Workbench.Canvas');\n  const slots = workbenchVariants();\n  return useRender({\n    defaultTagName: 'div',\n    render,\n    props: mergeProps({ 'data-slot': 'workbench-canvas', className: slots.canvas({ className }) }, props),\n  });\n}\nWorkbenchCanvas.displayName = 'Workbench.Canvas';\n\nexport interface WorkbenchArtboardProps extends ComponentProps<'div'> {\n  /** The work's shape, as a CSS aspect ratio such as `16 / 9` or `4 / 5`. Default `16 / 9`. */\n  aspect?: string;\n}\n\n/** The work itself: as large as its shape fits on the canvas, clear of the floating bar. */\nfunction WorkbenchArtboard({\n  aspect = '16 / 9',\n  className,\n  style,\n  render,\n  ...props\n}: WorkbenchArtboardProps & RenderProp<'div'>) {\n  const slots = workbenchVariants();\n  const [wide, tall] = aspect.split('/').map((part) => Number(part.trim()) || 1);\n  return useRender({\n    defaultTagName: 'div',\n    render,\n    props: mergeProps(\n      {\n        'data-slot': 'workbench-artboard',\n        className: slots.artboard({ className }),\n        style: { ...style, '--wb-aspect': aspect, '--wb-ratio': (wide ?? 1) / (tall ?? 1) } as CSSProperties,\n      },\n      props\n    ),\n  });\n}\nWorkbenchArtboard.displayName = 'Workbench.Artboard';\n\nexport interface WorkbenchInspectorProps extends ComponentProps<'div'> {\n  /** The sheet's heading on a narrow screen, read out when it opens. Default `Inspector`. */\n  title?: string;\n  children?: ReactNode;\n}\n\n/**\n * The column after the canvas, for an `Inspector`. On a narrow screen the\n * same children open in a sheet from the side instead.\n */\nfunction WorkbenchInspector({\n  title = 'Inspector',\n  className,\n  children,\n  render,\n  ...props\n}: WorkbenchInspectorProps & RenderProp<'div'>) {\n  const workbench = useWorkbenchContext('Workbench.Inspector');\n  usePresence(workbench.hasInspector);\n  const slots = workbenchVariants();\n  // Called on both sizes, so crossing the breakpoint keeps the hook count.\n  const panel = useRender({\n    defaultTagName: 'div',\n    render,\n    props: mergeProps(\n      {\n        'data-slot': 'workbench-inspector',\n        inert: workbench.focus || undefined,\n        className: slots.inspector({ className }),\n        children: <>{children}</>,\n      },\n      props\n    ),\n  });\n  if (!workbench.compact) return panel;\n  return (\n    <Sheet side=\"right\" open={workbench.inspectorOpen} onOpenChange={workbench.setInspectorOpen}>\n      <Sheet.Content size=\"full\">\n        <Sheet.Header title={title} />\n        <div data-slot=\"workbench-inspector\" className={slots.sheetBody()}>\n          {children}\n        </div>\n      </Sheet.Content>\n    </Sheet>\n  );\n}\nWorkbenchInspector.displayName = 'Workbench.Inspector';\n\nexport interface WorkbenchInspectorTriggerProps extends ComponentProps<'button'> {}\n\n/**\n * The button that opens the inspector's sheet on a narrow screen. It is\n * only there when the inspector is a sheet, so put it where it belongs on\n * either size: in the bar, beside the tools.\n */\nfunction WorkbenchInspectorTrigger({\n  className,\n  children,\n  onClick,\n  render,\n  ...props\n}: WorkbenchInspectorTriggerProps & RenderProp<'button'>) {\n  const workbench = useWorkbenchContext('Workbench.InspectorTrigger');\n  const slots = workbenchVariants();\n  // The hook runs on every render, and the result is dropped on a wide\n  // screen: returning before it would change the hook count whenever the\n  // window crosses the breakpoint, which React treats as a crash.\n  const element = useRender({\n    defaultTagName: 'button',\n    render,\n    props: mergeProps(\n      {\n        type: 'button',\n        'data-slot': 'workbench-inspector-trigger',\n        'aria-label': 'Open inspector',\n        'aria-expanded': workbench.inspectorOpen,\n        className: slots.trigger({ className }),\n        onClick: (event: ReactTypes.MouseEvent<HTMLButtonElement>) => {\n          onClick?.(event);\n          if (!event.defaultPrevented) workbench.setInspectorOpen(true);\n        },\n        children: (\n          <>\n            {children ?? (\n              <svg\n                viewBox=\"0 0 24 24\"\n                fill=\"none\"\n                stroke=\"currentColor\"\n                strokeWidth={2}\n                strokeLinecap=\"round\"\n                strokeLinejoin=\"round\"\n                aria-hidden\n              >\n                <path d=\"M4 6h10M18 6h2M4 12h4M12 12h8M4 18h12M20 18h0\" />\n                <circle cx=\"16\" cy=\"6\" r=\"2\" />\n                <circle cx=\"10\" cy=\"12\" r=\"2\" />\n                <circle cx=\"18\" cy=\"18\" r=\"2\" />\n              </svg>\n            )}\n          </>\n        ),\n      },\n      props\n    ),\n  });\n  return workbench.compact ? element : null;\n}\nWorkbenchInspectorTrigger.displayName = 'Workbench.InspectorTrigger';\n\nexport interface WorkbenchTimelineProps extends ComponentProps<'div'> {\n  /** Let its top edge be dragged to make it taller or shorter. Default true. */\n  resizable?: boolean;\n  /** The shortest it can be dragged to, in pixels. Default 140. */\n  minHeight?: number;\n}\n\n/** The row under everything, for a `Timeline`. Its top edge drags to resize it. */\nfunction WorkbenchTimeline({\n  resizable = true,\n  minHeight = 140,\n  className,\n  children,\n  render,\n  ...props\n}: WorkbenchTimelineProps & RenderProp<'div'>) {\n  const workbench = useWorkbenchContext('Workbench.Timeline');\n  usePresence(workbench.hasTimeline);\n  const slots = workbenchVariants();\n  const [active, setActive] = useState(false);\n  const maxHeight = () => Math.max(minHeight, workbench.rootHeight() * 0.6);\n\n  const drag = (event: ReactPointerEvent<HTMLDivElement>) => {\n    if (event.button !== 0) return;\n    event.preventDefault();\n    event.currentTarget.setPointerCapture(event.pointerId);\n    const startY = event.clientY;\n    const startHeight = workbench.timelineHeight;\n    setActive(true);\n    workbench.setResizing(true);\n    const target = event.currentTarget;\n    const move = (next: PointerEvent) =>\n      workbench.setTimelineHeight(\n        Math.round(Math.min(Math.max(startHeight + startY - next.clientY, minHeight), maxHeight()))\n      );\n    const end = () => {\n      setActive(false);\n      workbench.setResizing(false);\n      target.removeEventListener('pointermove', move);\n      target.removeEventListener('pointerup', end);\n      target.removeEventListener('pointercancel', end);\n    };\n    target.addEventListener('pointermove', move);\n    target.addEventListener('pointerup', end);\n    target.addEventListener('pointercancel', end);\n  };\n\n  const keys = (event: KeyboardEvent<HTMLDivElement>) => {\n    const by = event.key === 'ArrowUp' ? 16 : event.key === 'ArrowDown' ? -16 : 0;\n    if (!by) return;\n    event.preventDefault();\n    workbench.setTimelineHeight(Math.min(Math.max(workbench.timelineHeight + by, minHeight), maxHeight()));\n  };\n\n  return useRender({\n    defaultTagName: 'div',\n    render,\n    props: mergeProps(\n      {\n        'data-slot': 'workbench-timeline',\n        inert: workbench.focus || undefined,\n        className: slots.timeline({ className }),\n        children: (\n          <>\n            {resizable ? (\n              <div\n                role=\"separator\"\n                tabIndex={0}\n                aria-orientation=\"horizontal\"\n                aria-label=\"Timeline height\"\n                aria-valuenow={workbench.timelineHeight}\n                aria-valuemin={minHeight}\n                data-slot=\"workbench-resize\"\n                data-active={active ? '' : undefined}\n                className={slots.resize()}\n                onPointerDown={drag}\n                onKeyDown={keys}\n              />\n            ) : null}\n            {children}\n          </>\n        ),\n      },\n      props\n    ),\n  });\n}\nWorkbenchTimeline.displayName = 'Workbench.Timeline';\n\nexport const Workbench = Object.assign(WorkbenchRoot, {\n  Bar: WorkbenchBar,\n  Sidebar: WorkbenchSidebar,\n  Canvas: WorkbenchCanvas,\n  Artboard: WorkbenchArtboard,\n  Inspector: WorkbenchInspector,\n  InspectorTrigger: WorkbenchInspectorTrigger,\n  Timeline: WorkbenchTimeline,\n});\n\nexport {\n  WorkbenchBar,\n  WorkbenchSidebar,\n  WorkbenchCanvas,\n  WorkbenchArtboard,\n  WorkbenchInspector,\n  WorkbenchInspectorTrigger,\n  WorkbenchTimeline,\n  workbenchVariants,\n};\n"
    }
  ]
}
