diff --git a/.gitea/workflows/sonarqube.yml b/.gitea/workflows/sonarqube.yml
new file mode 100644
index 00000000..8bf969a4
--- /dev/null
+++ b/.gitea/workflows/sonarqube.yml
@@ -0,0 +1,57 @@
+name: SonarQube
+
+on:
+ schedule:
+ - cron: '0 2 * * *'
+ workflow_dispatch:
+
+jobs:
+ sonarqube:
+ runs-on: ubuntu-latest
+ container:
+ image: gitea.siegeln.net/cameleer/cameleer-build:1
+ credentials:
+ username: cameleer
+ password: ${{ secrets.REGISTRY_TOKEN }}
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Configure Gitea Maven Registry
+ run: |
+ mkdir -p ~/.m2
+ cat > ~/.m2/settings.xml << 'SETTINGS'
+
+
+
+ gitea
+ cameleer
+ ${env.REGISTRY_TOKEN}
+
+
+
+ SETTINGS
+ env:
+ REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
+
+ - name: Cache Maven dependencies
+ uses: actions/cache@v4
+ with:
+ path: ~/.m2/repository
+ key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+ restore-keys: ${{ runner.os }}-maven-
+
+ - name: Build and Test with coverage
+ run: mvn clean verify -DskipITs -U --batch-mode
+
+ - name: SonarQube Analysis
+ run: |
+ mvn sonar:sonar --batch-mode \
+ -Dsonar.host.url="$SONAR_HOST_URL" \
+ -Dsonar.token="$SONAR_TOKEN" \
+ -Dsonar.projectKey=cameleer3-server \
+ -Dsonar.projectName="Cameleer3 Server"
+ env:
+ SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
+ SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
diff --git a/ui/package-lock.json b/ui/package-lock.json
index 04b06ad0..6af50a71 100644
--- a/ui/package-lock.json
+++ b/ui/package-lock.json
@@ -8,7 +8,7 @@
"name": "ui",
"version": "0.0.0",
"dependencies": {
- "@cameleer/design-system": "^0.1.18",
+ "@cameleer/design-system": "^0.1.19",
"@tanstack/react-query": "^5.90.21",
"lucide-react": "^1.7.0",
"openapi-fetch": "^0.17.0",
@@ -277,9 +277,9 @@
}
},
"node_modules/@cameleer/design-system": {
- "version": "0.1.18",
- "resolved": "https://gitea.siegeln.net/api/packages/cameleer/npm/%40cameleer%2Fdesign-system/-/0.1.18/design-system-0.1.18.tgz",
- "integrity": "sha512-uvGr4PFw6Eya+h9DSD0wBnzjIXhZpcndR2dDJX2tMvQqgy+32WTTTQ8BZZWZjOKLSv63UpBN/fwVSXtkA4dnqA==",
+ "version": "0.1.19",
+ "resolved": "https://gitea.siegeln.net/api/packages/cameleer/npm/%40cameleer%2Fdesign-system/-/0.1.19/design-system-0.1.19.tgz",
+ "integrity": "sha512-YpYJysWycqRiTMco3Fco8AIatJz/IU7EecTmUQLUrkcBUGfHOWzCTMYm47jRvNAjQrANoanYnXPKXRHg91NS2w==",
"dependencies": {
"lucide-react": "^1.7.0",
"react": "^19.0.0",
diff --git a/ui/package.json b/ui/package.json
index e0ae3b77..b5168f69 100644
--- a/ui/package.json
+++ b/ui/package.json
@@ -14,7 +14,7 @@
"generate-api:live": "curl -s http://localhost:8081/api/v1/api-docs -o src/api/openapi.json && openapi-typescript src/api/openapi.json -o src/api/schema.d.ts"
},
"dependencies": {
- "@cameleer/design-system": "^0.1.18",
+ "@cameleer/design-system": "^0.1.19",
"@tanstack/react-query": "^5.90.21",
"lucide-react": "^1.7.0",
"openapi-fetch": "^0.17.0",
diff --git a/ui/src/components/ExecutionDiagram/ExecutionDiagram.module.css b/ui/src/components/ExecutionDiagram/ExecutionDiagram.module.css
index b2e62406..550e4d1f 100644
--- a/ui/src/components/ExecutionDiagram/ExecutionDiagram.module.css
+++ b/ui/src/components/ExecutionDiagram/ExecutionDiagram.module.css
@@ -447,6 +447,11 @@
overflow-y: auto;
}
+.errorStackWrap pre {
+ max-height: 50vh;
+ overflow-y: auto;
+}
+
.errorStackLabel {
font-size: 10px;
font-weight: 600;
diff --git a/ui/src/components/ExecutionDiagram/tabs/ErrorTab.tsx b/ui/src/components/ExecutionDiagram/tabs/ErrorTab.tsx
index 2a8630c9..cde66434 100644
--- a/ui/src/components/ExecutionDiagram/tabs/ErrorTab.tsx
+++ b/ui/src/components/ExecutionDiagram/tabs/ErrorTab.tsx
@@ -38,7 +38,9 @@ export function ErrorTab({ processor, executionDetail }: ErrorTabProps) {
{errorStackTrace && (
<>
Stack Trace
-
+
+
+
>
)}
diff --git a/ui/src/components/LayoutShell.tsx b/ui/src/components/LayoutShell.tsx
index e860216e..44b9ef99 100644
--- a/ui/src/components/LayoutShell.tsx
+++ b/ui/src/components/LayoutShell.tsx
@@ -222,36 +222,31 @@ function LayoutContent() {
navigate(`${baseParts.join('/')}?text=${encodeURIComponent(query)}`);
}, [navigate, scope.appId, scope.routeId]);
- // Intercept Sidebar's internal navigation to re-route through current tab
- const handleSidebarClick = useCallback((e: React.MouseEvent) => {
- const anchor = (e.target as HTMLElement).closest('a[href]');
- if (!anchor) return;
- const href = anchor.getAttribute('href') || '';
-
- // Intercept /apps/:appId and /apps/:appId/:routeId links
- const appMatch = href.match(/^\/apps\/([^/]+)(?:\/(.+))?$/);
+ // Translate Sidebar's internal paths to our URL structure
+ const handleSidebarNavigate = useCallback((path: string) => {
+ // /apps/:appId and /apps/:appId/:routeId → current tab
+ const appMatch = path.match(/^\/apps\/([^/]+)(?:\/(.+))?$/);
if (appMatch) {
- e.preventDefault();
const [, sAppId, sRouteId] = appMatch;
navigate(sRouteId ? `/${scope.tab}/${sAppId}/${sRouteId}` : `/${scope.tab}/${sAppId}`);
return;
}
- // Intercept /agents/* links — redirect to runtime tab
- const agentMatch = href.match(/^\/agents\/([^/]+)(?:\/(.+))?$/);
+ // /agents/:appId/:instanceId → runtime tab
+ const agentMatch = path.match(/^\/agents\/([^/]+)(?:\/(.+))?$/);
if (agentMatch) {
- e.preventDefault();
const [, sAppId, sInstanceId] = agentMatch;
navigate(sInstanceId ? `/runtime/${sAppId}/${sInstanceId}` : `/runtime/${sAppId}`);
+ return;
}
+
+ navigate(path);
}, [navigate, scope.tab]);
return (
-
-
+
}
>
- {/* Scrollable content */}
-
- {/* Exchanges table */}
-
-
-
- {textFilter ? (
- <>
-
- Search: “{textFilter}”
-
- >
- ) : 'Recent Exchanges'}
-
-
-
- {rows.length.toLocaleString()} of {(searchResult?.total ?? 0).toLocaleString()} exchanges
-
- {!textFilter && }
-
-
-
-
-
- row.errorMessage ? (
-
-
-
-
{row.errorMessage}
-
Click to view full stack trace
-
-
- ) : null
- }
- />
-
+
+
+
+ {textFilter ? (
+ <>
+
+ Search: “{textFilter}”
+
+ >
+ ) : 'Recent Exchanges'}
+
+
+
+ {rows.length.toLocaleString()} of {(searchResult?.total ?? 0).toLocaleString()} exchanges
+
+ {!textFilter && }
- >
+
+
+ row.errorMessage ? (
+
+
+
+
{row.errorMessage}
+
Click to view full stack trace
+
+
+ ) : null
+ }
+ />
+
)
}
diff --git a/ui/src/pages/Exchanges/ExchangesPage.module.css b/ui/src/pages/Exchanges/ExchangesPage.module.css
index 3c545469..dac174db 100644
--- a/ui/src/pages/Exchanges/ExchangesPage.module.css
+++ b/ui/src/pages/Exchanges/ExchangesPage.module.css
@@ -5,7 +5,9 @@
}
.leftPanel {
- overflow: auto;
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
height: 100%;
flex-shrink: 0;
}