From 6741155d52355c909236b1e3335b4955cf96eb54 Mon Sep 17 00:00:00 2001
From: Philipp Fromme <philipp.fromme@camunda.com>
Date: Thu, 12 Dec 2024 13:59:31 +0100
Subject: [PATCH] WIP

---
 .../file-context/processors/dmnProcessor.js   |  6 +-
 .../file-context/processors/formProcessor.js  |  7 +-
 app/lib/index.js                              | 24 +++++--
 app/lib/menu/menu-builder.js                  | 20 ------
 .../icons/file-types/ProcessApplication.svg   |  5 ++
 client/src/app/App.js                         | 22 +++---
 client/src/app/primitives/Tabbed.less         | 18 +++--
 .../ProcessApplications.js                    | 40 +++++++----
 .../ProcessApplicationsStatusBar.js           | 68 ++++++++++---------
 .../ProcessApplicationsStatusBar.less         | 21 ++++--
 10 files changed, 139 insertions(+), 92 deletions(-)
 create mode 100644 client/resources/icons/file-types/ProcessApplication.svg

diff --git a/app/lib/file-context/processors/dmnProcessor.js b/app/lib/file-context/processors/dmnProcessor.js
index b1c9c7f468..155c5e8e42 100644
--- a/app/lib/file-context/processors/dmnProcessor.js
+++ b/app/lib/file-context/processors/dmnProcessor.js
@@ -13,6 +13,7 @@ const DmnModdle = require('dmn-moddle');
 const moddle = new DmnModdle();
 
 const {
+  findFileInParentDirectories,
   is,
   traverse
 } = require('./util');
@@ -20,11 +21,14 @@ const {
 module.exports = {
   extensions: [ '.dmn' ],
   process: async (item) => {
+    const processApplicationFilePath = findFileInParentDirectories(item.file.path, '.process-application');
+
     const { rootElement } = await moddle.fromXML(item.file.contents);
 
     return {
       type: 'dmn',
-      ids: findDecisionIds(rootElement)
+      ids: findDecisionIds(rootElement),
+      processApplication: processApplicationFilePath
     };
   }
 };
diff --git a/app/lib/file-context/processors/formProcessor.js b/app/lib/file-context/processors/formProcessor.js
index 219ece980e..c1356e835a 100644
--- a/app/lib/file-context/processors/formProcessor.js
+++ b/app/lib/file-context/processors/formProcessor.js
@@ -8,14 +8,19 @@
  * except in compliance with the MIT License.
  */
 
+const { findFileInParentDirectories } = require('./util');
+
 module.exports = {
   extensions: [ '.form' ],
   process: async (item) => {
+    const processApplicationFilePath = findFileInParentDirectories(item.file.path, '.process-application');
+
     const form = JSON.parse(item.file.contents);
 
     return {
       type: 'form',
-      ids: [ form.id ]
+      ids: [ form.id ],
+      processApplication: processApplicationFilePath
     };
   }
 };
\ No newline at end of file
diff --git a/app/lib/index.js b/app/lib/index.js
index e1128eb2c5..1eeb2202f0 100644
--- a/app/lib/index.js
+++ b/app/lib/index.js
@@ -219,6 +219,18 @@ renderer.on('system-clipboard:write-text', function(options, done) {
 });
 
 // file context //////////
+renderer.on('file-context:add-root', function(filePath, done) {
+  fileContext.addRoot(filePath);
+
+  done(null);
+});
+
+renderer.on('file-context:remove-root', function(filePath, done) {
+  fileContext.removeRoot(filePath);
+
+  done(null);
+});
+
 renderer.on('file:opened', function(filePath, done) {
   fileContext.fileOpened({ uri: toFileUrl(filePath) });
 
@@ -727,16 +739,18 @@ function bootstrap() {
 
   const fileContext = new FileContext(fileContextLog);
 
-  /**
-   * @param { import('./file-context/types').IndexItem } item
-   */
-  function onIndexerUpdated(item) {
+  function onIndexerUpdated() {
     fileContextLog.info('files', JSON.stringify(fileContext._indexer.getItems().map(({ file, metadata }) => ({ file: { ...file, contents: file.contents.substring(0, 10) + '...' }, metadata })), null, 2));
 
-    renderer.send('file-context:changed', fileContext._indexer.getItems().map(({ file, metadata }) => ({ file, metadata })));
+
+    /* @type {import('./file-context/types').IndexItem} */
+    const items = fileContext._indexer.getItems();
+
+    renderer.send('file-context:changed', items.map(({ file, metadata }) => ({ file, metadata })));
   }
 
   fileContext.on('indexer:updated', onIndexerUpdated);
+  fileContext.on('indexer:removed', onIndexerUpdated);
 
   app.on('quit', () => fileContext.close());
 
diff --git a/app/lib/menu/menu-builder.js b/app/lib/menu/menu-builder.js
index fac0a480a1..706b9da966 100644
--- a/app/lib/menu/menu-builder.js
+++ b/app/lib/menu/menu-builder.js
@@ -75,7 +75,6 @@ class MenuBuilder {
         .appendSeparator()
         .appendExportAs()
         .appendCloseTab()
-        .appendCloseProcessApplication()
         .appendSeparator()
         .appendQuit()
         .get()
@@ -197,13 +196,6 @@ class MenuBuilder {
       }
     }));
 
-    this.menu.append(new MenuItem({
-      label: 'Open Process Application...',
-      click: function() {
-        app.emit('menu:action', 'open-process-application');
-      }
-    }));
-
     this.appendOpenRecent();
 
     return this;
@@ -335,18 +327,6 @@ class MenuBuilder {
     return this;
   }
 
-  appendCloseProcessApplication() {
-    this.menu.append(new MenuItem({
-      label: 'Close Process Application',
-      enabled: this.options.state.openProcessApplication,
-      click: function() {
-        app.emit('menu:action', 'close-process-application');
-      }
-    }));
-
-    return this;
-  }
-
   appendSwitchTab(submenu) {
     this.menu.append(new MenuItem({
       label: 'Switch Tab...',
diff --git a/client/resources/icons/file-types/ProcessApplication.svg b/client/resources/icons/file-types/ProcessApplication.svg
new file mode 100644
index 0000000000..dfcdf23d81
--- /dev/null
+++ b/client/resources/icons/file-types/ProcessApplication.svg
@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
+  <path d="m6.387 2.824-1.735-1.73H1.015V11.24h2.323q.087.522.25 1.015H1.016A1.015 1.015 0 0 1 0 11.24V1.094A1.015 1.015 0 0 1 1.015.079h3.637a1.01 1.01 0 0 1 .72.3l1.73 1.73h6.088a1.015 1.015 0 0 1 1.015 1.014v1.6a7 7 0 0 0-1.015-.636v-.964H6.681z"></path>
+  <path d="m11.959 10.57-2.007 1.661a.586.586 0 0 1-.957-.445V8.464a.58.58 0 0 1 .336-.525.59.59 0 0 1 .62.08l2.008 1.66a.58.58 0 0 1 .155.692.6.6 0 0 1-.155.2"></path>
+  <path fill-rule="evenodd" d="M10.766 15.891a.284.284 0 0 0 .248-.234l.151-.868a.3.3 0 0 1 .222-.237c.372-.1.73-.246 1.065-.435a.31.31 0 0 1 .32.01l.727.508a.285.285 0 0 0 .341-.009q.472-.38.855-.848a.28.28 0 0 0 .009-.34l-.51-.717a.3.3 0 0 1-.012-.322q.286-.502.438-1.057a.3.3 0 0 1 .237-.22l.002-.001.874-.15a.28.28 0 0 0 .236-.246q.02-.203.028-.411v-.04q.011-.375-.028-.75a.28.28 0 0 0-.236-.245l-.888-.152a.3.3 0 0 1-.238-.218 4.5 4.5 0 0 0-.44-1.034.3.3 0 0 1 .012-.325l.525-.739a.28.28 0 0 0-.009-.339 6 6 0 0 0-.855-.848.284.284 0 0 0-.341-.01l-.76.534a.3.3 0 0 1-.323.013 4.6 4.6 0 0 0-1.02-.412.3.3 0 0 1-.222-.238l-.16-.918a.28.28 0 0 0-.248-.234 6 6 0 0 0-.59-.031h-.014q-.303 0-.604.03a.28.28 0 0 0-.248.235l-.155.89a.3.3 0 0 1-.223.238c-.365.098-.717.24-1.047.424a.31.31 0 0 1-.319-.01l-.74-.52a.284.284 0 0 0-.342.008 6 6 0 0 0-.855.85.28.28 0 0 0-.009.339l.512.72a.3.3 0 0 1 .011.324q-.288.5-.442 1.055a.3.3 0 0 1-.24.22l-.87.149a.28.28 0 0 0-.236.245q-.062.601 0 1.201c.014.125.111.224.236.246l.87.149a.3.3 0 0 1 .24.219c.1.365.249.72.442 1.055a.3.3 0 0 1-.011.325l-.512.72a.28.28 0 0 0 .01.338q.382.469.854.85c.098.079.238.08.341.008l.74-.519a.3.3 0 0 1 .32-.011q.252.14.517.247.26.109.535.186c.111.031.197.12.219.23l.154.89a.28.28 0 0 0 .248.233q.604.06 1.208 0m-.604-2.288c.929 0 1.82-.366 2.477-1.019a3.47 3.47 0 0 0 1.026-2.46c0-.922-.37-1.806-1.026-2.459a3.52 3.52 0 0 0-2.477-1.018c-.929 0-1.82.366-2.477 1.018a3.47 3.47 0 0 0-1.026 2.46c0 .922.37 1.807 1.026 2.46a3.52 3.52 0 0 0 2.477 1.018" clip-rule="evenodd"></path>
+</svg>
\ No newline at end of file
diff --git a/client/src/app/App.js b/client/src/app/App.js
index eca640f27e..66a552415e 100644
--- a/client/src/app/App.js
+++ b/client/src/app/App.js
@@ -75,6 +75,8 @@ import * as css from './App.less';
 import Notifications, { NOTIFICATION_TYPES } from './notifications';
 import { RecentTabs } from './RecentTabs';
 
+import ProcessApplicationIcon from '../../resources/icons/file-types/ProcessApplication.svg';
+
 const log = debug('App');
 
 export const EMPTY_TAB = {
@@ -1849,16 +1851,14 @@ export class App extends PureComponent {
     }
 
     const file = {
-      name: 'new-process-application.process-application',
-      contents: JSON.stringify({
-        name: 'New Process Application'
-      }, null, 2),
+      name: '.process-application',
+      contents: JSON.stringify({}, null, 2),
       path: null
     };
 
     const fileSystem = this.getGlobal('fileSystem');
 
-    const savedFile = await fileSystem.writeFile(`${directoryPath}/${file.name}`, file);
+    await fileSystem.writeFile(`${directoryPath}/${file.name}`, file);
 
     this.processApplications.open(`${directoryPath}/${file.name}`);
   }
@@ -2298,6 +2298,7 @@ export class App extends PureComponent {
 
                   await this.openFiles(files);
                 } }
+                tabsProvider={ this.props.tabsProvider }
               />
 
               <PluginsRoot
@@ -2328,11 +2329,7 @@ export class App extends PureComponent {
       {
         text: 'Process application',
         group: 'Camunda 8',
-        icon: () => <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 16 16" width="24" height="24">
-          <path fill="#000" d="m6.387 2.824-1.735-1.73H1.015V11.24h2.323q.087.522.25 1.015H1.016A1.015 1.015 0 0 1 0 11.24V1.094A1.015 1.015 0 0 1 1.015.079h3.637a1.01 1.01 0 0 1 .72.3l1.73 1.73h6.088a1.015 1.015 0 0 1 1.015 1.014v1.6a7 7 0 0 0-1.015-.636v-.964H6.681z"></path>
-          <path fill="#000" d="m11.959 10.57-2.007 1.661a.586.586 0 0 1-.957-.445V8.464a.58.58 0 0 1 .336-.525.59.59 0 0 1 .62.08l2.008 1.66a.58.58 0 0 1 .155.692.6.6 0 0 1-.155.2"></path>
-          <path fill="#000" fillRule="evenodd" d="M10.766 15.891a.284.284 0 0 0 .248-.234l.151-.868a.3.3 0 0 1 .222-.237c.372-.1.73-.246 1.065-.435a.31.31 0 0 1 .32.01l.727.508a.285.285 0 0 0 .341-.009q.472-.38.855-.848a.28.28 0 0 0 .009-.34l-.51-.717a.3.3 0 0 1-.012-.322q.286-.502.438-1.057a.3.3 0 0 1 .237-.22l.002-.001.874-.15a.28.28 0 0 0 .236-.246q.02-.203.028-.411v-.04q.011-.375-.028-.75a.28.28 0 0 0-.236-.245l-.888-.152a.3.3 0 0 1-.238-.218 4.5 4.5 0 0 0-.44-1.034.3.3 0 0 1 .012-.325l.525-.739a.28.28 0 0 0-.009-.339 6 6 0 0 0-.855-.848.284.284 0 0 0-.341-.01l-.76.534a.3.3 0 0 1-.323.013 4.6 4.6 0 0 0-1.02-.412.3.3 0 0 1-.222-.238l-.16-.918a.28.28 0 0 0-.248-.234 6 6 0 0 0-.59-.031h-.014q-.303 0-.604.03a.28.28 0 0 0-.248.235l-.155.89a.3.3 0 0 1-.223.238c-.365.098-.717.24-1.047.424a.31.31 0 0 1-.319-.01l-.74-.52a.284.284 0 0 0-.342.008 6 6 0 0 0-.855.85.28.28 0 0 0-.009.339l.512.72a.3.3 0 0 1 .011.324q-.288.5-.442 1.055a.3.3 0 0 1-.24.22l-.87.149a.28.28 0 0 0-.236.245q-.062.601 0 1.201c.014.125.111.224.236.246l.87.149a.3.3 0 0 1 .24.219c.1.365.249.72.442 1.055a.3.3 0 0 1-.011.325l-.512.72a.28.28 0 0 0 .01.338q.382.469.854.85c.098.079.238.08.341.008l.74-.519a.3.3 0 0 1 .32-.011q.252.14.517.247.26.109.535.186c.111.031.197.12.219.23l.154.89a.28.28 0 0 0 .248.233q.604.06 1.208 0m-.604-2.288c.929 0 1.82-.366 2.477-1.019a3.47 3.47 0 0 0 1.026-2.46c0-.922-.37-1.806-1.026-2.459a3.52 3.52 0 0 0-2.477-1.018c-.929 0-1.82.366-2.477 1.018a3.47 3.47 0 0 0-1.026 2.46c0 .922.37 1.807 1.026 2.46a3.52 3.52 0 0 0 2.477 1.018" clipRule="evenodd"></path>
-        </svg>,
+        icon: ProcessApplicationIcon,
         onClick: () => this.triggerAction('create-process-application')
       }
     ];
@@ -2377,9 +2374,14 @@ export class App extends PureComponent {
     } = this.props;
 
     const {
+      file,
       type
     } = tab;
 
+    if (this.processApplications.hasOpen() && this.processApplications.getItem(file.path)) {
+      return ProcessApplicationIcon;
+    }
+
     return tabsProvider.getTabIcon(type);
   };
 }
diff --git a/client/src/app/primitives/Tabbed.less b/client/src/app/primitives/Tabbed.less
index bb0aeab162..da90279b67 100644
--- a/client/src/app/primitives/Tabbed.less
+++ b/client/src/app/primitives/Tabbed.less
@@ -65,9 +65,19 @@
     border-right: 1px solid var(--tab-border-right-color);
     user-select: none;
 
-    &.tab--process-application {
-      background: linear-gradient(90deg, fuchsia 0%, transparent 75%);
-    }
+    // &.tab--process-application {
+    //   background: hsl(205, 100%, 45%) !important;
+    //   color: white;
+
+    //   .tab__close svg path {
+    //     fill: white !important;
+    //   }
+
+    //   &.tab--active::after,
+    //   &:not(.tab--active):hover::after {
+    //     background: white !important;
+    //   }
+    // }
 
     .tab__content {
       position: absolute;
@@ -133,7 +143,7 @@
       width: 18px;
       height: 18px;
       border-radius: 50%;
-      background-color: var(--tab-close-background-color);
+      // background-color: var(--tab-close-background-color);
 
       .tab__icon-close {
         margin: 1px 0px 1px -1px;
diff --git a/client/src/app/process-applications/ProcessApplications.js b/client/src/app/process-applications/ProcessApplications.js
index 649f5832d7..66cf5f0fd2 100644
--- a/client/src/app/process-applications/ProcessApplications.js
+++ b/client/src/app/process-applications/ProcessApplications.js
@@ -17,9 +17,17 @@ export default class ProcessApplications {
 
       const activeTab = this._app.state.activeTab;
 
-      if (activeTab && activeTab.file) {
+      if (this.hasOpen()) {
+        this._processApplicationItems = this._items.filter(item => item.metadata.processApplication === this._processApplication.file.path);
+
+        this._app.emit('process-applications:changed');
+      } else if (activeTab) {
         const { file } = activeTab;
 
+        if (!file) {
+          return;
+        }
+
         const item = this._items.find(item => item.file.path === file.path);
 
         if (item && item.metadata.processApplication) {
@@ -31,6 +39,12 @@ export default class ProcessApplications {
     this._app.on('app.activeTabChanged', ({ activeTab }) => {
       const { file } = activeTab;
 
+      if (!file) {
+        this.close();
+
+        return;
+      }
+
       const item = this._items.find(item => item.file.path === file.path);
 
       if (!item || !item.metadata.processApplication) {
@@ -39,14 +53,13 @@ export default class ProcessApplications {
         return;
       }
 
-      const { metadata } = item;
-
-      if (metadata.processApplication) {
-        this.open(metadata.processApplication);
+      if (item.metadata.processApplication) {
+        this.open(item.metadata.processApplication);
       }
     });
 
     this._processApplication = null;
+    this._processApplicationItems = [];
     this._items = [];
   }
 
@@ -66,12 +79,14 @@ export default class ProcessApplications {
         dirname
       } = file;
 
-      this._app.getGlobal('backend').send('file-context:add-root', dirname);
-
       this._processApplication = {
         file,
         ...JSON.parse(contents)
       };
+
+      this._processApplicationItems = [];
+
+      this._app.getGlobal('backend').send('file-context:add-root', dirname);
     } catch (err) {
       console.error(err);
     }
@@ -83,6 +98,7 @@ export default class ProcessApplications {
     this._app.getGlobal('backend').send('file-context:remove-root', this._processApplication.file.dirname);
 
     this._processApplication = null;
+    this._processApplicationItems = [];
 
     this._app.emit('process-applications:changed');
   }
@@ -96,12 +112,10 @@ export default class ProcessApplications {
   }
 
   getItems() {
-    if (!this._processApplication) {
-      return [];
-    }
-
-    const items = this._items.filter(({ metadata }) => metadata.processApplication === this._processApplication.file.path);
+    return this._processApplicationItems;
+  }
 
-    return items;
+  getItem(path) {
+    return this._processApplicationItems.find(item => item.file.path === path);
   }
 }
\ No newline at end of file
diff --git a/client/src/app/process-applications/components/ProcessApplicationsStatusBar.js b/client/src/app/process-applications/components/ProcessApplicationsStatusBar.js
index 64f8e255f8..a93ce3e063 100644
--- a/client/src/app/process-applications/components/ProcessApplicationsStatusBar.js
+++ b/client/src/app/process-applications/components/ProcessApplicationsStatusBar.js
@@ -10,10 +10,14 @@
 
 import React, { useEffect, useRef, useState } from 'react';
 
+import classnames from 'classnames';
+
 import { Fill } from '../../slot-fill';
 
 import { Overlay, Section } from '../../../shared/ui';
 
+import ProcessApplicationIcon from '../../../../resources/icons/file-types/ProcessApplication.svg';
+
 import * as css from './ProcessApplicationsStatusBar.less';
 
 export default function ProcessApplicationsStatusBar(props) {
@@ -23,7 +27,8 @@ export default function ProcessApplicationsStatusBar(props) {
 
   const {
     onOpen,
-    processApplication
+    processApplication,
+    tabsProvider
   } = props;
 
   useEffect(() => {
@@ -40,8 +45,8 @@ export default function ProcessApplicationsStatusBar(props) {
 
   return <>
     <Fill slot="status-bar__file" group="0_process-application">
-      <button className={ "btn " + css.ProcessApplicationsButton } ref={ ref } onClick={ () => setIsOpen(!isOpen) } title="This file is part of a process application">
-        <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 16 16" width="16" height="16"><path fill="#000" d="m6.387 2.824-1.735-1.73H1.015V11.24h2.323q.087.522.25 1.015H1.016A1.015 1.015 0 0 1 0 11.24V1.094A1.015 1.015 0 0 1 1.015.079h3.637a1.01 1.01 0 0 1 .72.3l1.73 1.73h6.088a1.015 1.015 0 0 1 1.015 1.014v1.6a7 7 0 0 0-1.015-.636v-.964H6.681z"></path><path fill="#000" d="m11.959 10.57-2.007 1.661a.586.586 0 0 1-.957-.445V8.464a.58.58 0 0 1 .336-.525.59.59 0 0 1 .62.08l2.008 1.66a.58.58 0 0 1 .155.692.6.6 0 0 1-.155.2"></path><path fill="#000" fill-rule="evenodd" d="M10.766 15.891a.284.284 0 0 0 .248-.234l.151-.868a.3.3 0 0 1 .222-.237c.372-.1.73-.246 1.065-.435a.31.31 0 0 1 .32.01l.727.508a.285.285 0 0 0 .341-.009q.472-.38.855-.848a.28.28 0 0 0 .009-.34l-.51-.717a.3.3 0 0 1-.012-.322q.286-.502.438-1.057a.3.3 0 0 1 .237-.22l.002-.001.874-.15a.28.28 0 0 0 .236-.246q.02-.203.028-.411v-.04q.011-.375-.028-.75a.28.28 0 0 0-.236-.245l-.888-.152a.3.3 0 0 1-.238-.218 4.5 4.5 0 0 0-.44-1.034.3.3 0 0 1 .012-.325l.525-.739a.28.28 0 0 0-.009-.339 6 6 0 0 0-.855-.848.284.284 0 0 0-.341-.01l-.76.534a.3.3 0 0 1-.323.013 4.6 4.6 0 0 0-1.02-.412.3.3 0 0 1-.222-.238l-.16-.918a.28.28 0 0 0-.248-.234 6 6 0 0 0-.59-.031h-.014q-.303 0-.604.03a.28.28 0 0 0-.248.235l-.155.89a.3.3 0 0 1-.223.238c-.365.098-.717.24-1.047.424a.31.31 0 0 1-.319-.01l-.74-.52a.284.284 0 0 0-.342.008 6 6 0 0 0-.855.85.28.28 0 0 0-.009.339l.512.72a.3.3 0 0 1 .011.324q-.288.5-.442 1.055a.3.3 0 0 1-.24.22l-.87.149a.28.28 0 0 0-.236.245q-.062.601 0 1.201c.014.125.111.224.236.246l.87.149a.3.3 0 0 1 .24.219c.1.365.249.72.442 1.055a.3.3 0 0 1-.011.325l-.512.72a.28.28 0 0 0 .01.338q.382.469.854.85c.098.079.238.08.341.008l.74-.519a.3.3 0 0 1 .32-.011q.252.14.517.247.26.109.535.186c.111.031.197.12.219.23l.154.89a.28.28 0 0 0 .248.233q.604.06 1.208 0m-.604-2.288c.929 0 1.82-.366 2.477-1.019a3.47 3.47 0 0 0 1.026-2.46c0-.922-.37-1.806-1.026-2.459a3.52 3.52 0 0 0-2.477-1.018c-.929 0-1.82.366-2.477 1.018a3.47 3.47 0 0 0-1.026 2.46c0 .922.37 1.807 1.026 2.46a3.52 3.52 0 0 0 2.477 1.018" clip-rule="evenodd"></path></svg>
+      <button className={ classnames('btn', css.ProcessApplicationsButton) } ref={ ref } onClick={ () => setIsOpen(!isOpen) } title="This file is part of a process application">
+        <ProcessApplicationIcon width="16" height="16" />
       </button>
     </Fill>
     {
@@ -53,41 +58,18 @@ export default function ProcessApplicationsStatusBar(props) {
           <Section.Body>
             {
               items.filter(item => item.metadata.type === 'bpmn').length > 0 && <>
-                <p>BPMN</p>
-                <ul className="dashed">
+                <ul>
                   {
-                    items.filter(item => item.metadata.type === 'bpmn').map(item => {
-                      const { file } = item;
+                    sortByType(items).map(item => {
+                      const provider = tabsProvider.getProvider(item.metadata.type);
 
-                      return <li className="link" onClick={ () => onOpen(file.path) } key={ file.path } title={ file.path }>{ file.name }</li>;
-                    })
-                  }
-                </ul>
-              </>
-            }
-            {
-              items.filter(item => item.metadata.type === 'dmn').length > 0 && <>
-                <p>DMN</p>
-                <ul className="dashed">
-                  {
-                    items.filter(item => item.metadata.type === 'dmn').map(item => {
-                      const { file } = item;
+                      const Icon = provider.getIcon(item.file);
 
-                      return <li className="link" onClick={ () => onOpen(file.path) } key={ file.path } title={ file.path }>{ file.name }</li>;
-                    })
-                  }
-                </ul>
-              </>
-            }
-            {
-              items.filter(item => item.metadata.type === 'form').length > 0 && <>
-                <p>Form</p>
-                <ul className="dashed">
-                  {
-                    items.filter(item => item.metadata.type === 'form').map(item => {
                       const { file } = item;
 
-                      return <li className="link" onClick={ () => onOpen(file.path) } key={ file.path } title={ file.path }>{ file.name }</li>;
+                      return <li className="link" onClick={ () => onOpen(file.path) } key={ file.path } title={ file.path }>
+                        <Icon width="16" height="16" />{ file.name }
+                      </li>;
                     })
                   }
                 </ul>
@@ -99,3 +81,23 @@ export default function ProcessApplicationsStatusBar(props) {
     }
   </>;
 }
+
+function sortByType(items) {
+  const groupedByType = items.reduce((acc, item) => {
+    const { type } = item.metadata;
+
+    if (!acc[type]) {
+      acc[type] = [];
+    }
+
+    acc[type].push(item);
+
+    return acc;
+  }, {});
+
+  for (const type in groupedByType) {
+    groupedByType[type].sort((a, b) => a.file.name.localeCompare(b.file.name));
+  }
+
+  return Object.values(groupedByType).flat();
+}
\ No newline at end of file
diff --git a/client/src/app/process-applications/components/ProcessApplicationsStatusBar.less b/client/src/app/process-applications/components/ProcessApplicationsStatusBar.less
index 297e2a73e4..65e5eaa9d9 100644
--- a/client/src/app/process-applications/components/ProcessApplicationsStatusBar.less
+++ b/client/src/app/process-applications/components/ProcessApplicationsStatusBar.less
@@ -1,18 +1,29 @@
 :local(.ProcessApplicationsButton) {
-  background: linear-gradient(90deg, fuchsia 0%, transparent 100%) !important;
+  background: hsl(205, 100%, 45%) !important;
+
+  svg {
+    fill: white;
+  }
 }
   
 :local(.ProcessApplicationsOverlay) {
+  width: min-content;
+
   .link {
     cursor: pointer;
     text-decoration: underline;
   }
 
-  .section {
-    height: 500px !important;
+  ul {
+    padding: 0;
   }
 
-  ul.dashed {
-    max-height: none !important;
+  li {
+    display: flex;
+    align-items: center;
+
+    svg {
+      margin-right: 6px;
+    }
   }
 }