diff --git a/CHANGELOG.md b/CHANGELOG.md
index e5875524..53b16898 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
# aicodeman
+## 0.3.2
+
+### Patch Changes
+
+- Make file browser panel draggable by its header
+
## 0.3.1
### Patch Changes
diff --git a/CLAUDE.md b/CLAUDE.md
index 9f49bfbb..04384e39 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -52,7 +52,7 @@ When user says "COM":
4. **Sync CLAUDE.md version**: Update the `**Version**` line below to match the new version from `package.json`
5. **Commit and deploy**: `git add -A && git commit -m "chore: version packages" && git push && npm run build && systemctl --user restart codeman-web`
-**Version**: 0.3.1 (must match `package.json`)
+**Version**: 0.3.2 (must match `package.json`)
## Project Overview
diff --git a/package-lock.json b/package-lock.json
index afd7d7ff..3221ef39 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "aicodeman",
- "version": "0.3.0",
+ "version": "0.3.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "aicodeman",
- "version": "0.3.0",
+ "version": "0.3.1",
"hasInstallScript": true,
"license": "MIT",
"workspaces": [
@@ -17,6 +17,8 @@
"@fastify/compress": "^8.3.1",
"@fastify/cookie": "^11.0.2",
"@fastify/static": "^8.0.0",
+ "@remotion/compositor-linux-x64-gnu": "^4.0.432",
+ "@rspack/binding-linux-x64-gnu": "^1.7.7",
"chalk": "^5.3.0",
"chokidar": "^3.6.0",
"commander": "^12.1.0",
@@ -515,6 +517,40 @@
"node": ">=18"
}
},
+ "node_modules/@emnapi/core": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.8.1.tgz",
+ "integrity": "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/wasi-threads": "1.1.0",
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/runtime": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz",
+ "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/wasi-threads": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz",
+ "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
"node_modules/@eslint-community/eslint-utils": {
"version": "4.9.1",
"dev": true,
@@ -1163,6 +1199,19 @@
"@module-federation/sdk": "0.22.0"
}
},
+ "node_modules/@napi-rs/wasm-runtime": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.0.7.tgz",
+ "integrity": "sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/core": "^1.5.0",
+ "@emnapi/runtime": "^1.5.0",
+ "@tybys/wasm-util": "^0.10.1"
+ }
+ },
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
"dev": true,
@@ -1324,6 +1373,95 @@
"react-dom": ">=16.8.0"
}
},
+ "node_modules/@remotion/compositor-darwin-arm64": {
+ "version": "4.0.429",
+ "resolved": "https://registry.npmjs.org/@remotion/compositor-darwin-arm64/-/compositor-darwin-arm64-4.0.429.tgz",
+ "integrity": "sha512-V3Cv7TZyKulBPOBQJu2GONiKA+DDjGT6rWAc3WX4M9J9EDUwKiFFTDuTEpC7s4mcUDoAUXbnYE8ZlnWGaAuJhQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@remotion/compositor-darwin-x64": {
+ "version": "4.0.429",
+ "resolved": "https://registry.npmjs.org/@remotion/compositor-darwin-x64/-/compositor-darwin-x64-4.0.429.tgz",
+ "integrity": "sha512-vB8NM0j7gvCniu1dZ0p4MmTDN44Dvgd2iq6vStigzEQ1P2vlcx7qHEdVfaZBHsjdjMXD/UdwNmb34+lw3v6KEw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@remotion/compositor-linux-arm64-gnu": {
+ "version": "4.0.429",
+ "resolved": "https://registry.npmjs.org/@remotion/compositor-linux-arm64-gnu/-/compositor-linux-arm64-gnu-4.0.429.tgz",
+ "integrity": "sha512-F8N1HsRiCDw1EWWWI9hLNFTQy1mY7QLsWh0ywnrNCx62aF+8NpdYdW4pt/e7s4SQmkLh7enrkuqI0a6aGw4rpw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@remotion/compositor-linux-arm64-musl": {
+ "version": "4.0.429",
+ "resolved": "https://registry.npmjs.org/@remotion/compositor-linux-arm64-musl/-/compositor-linux-arm64-musl-4.0.429.tgz",
+ "integrity": "sha512-rvDjjascY9HAQ1nfbrdlVxyQ/xYj84jf0LylLmWJgr0kZVw5l8D/QXH4HTQeoPKhM3cBsCMKpW7oseHMoDyM5A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@remotion/compositor-linux-x64-gnu": {
+ "version": "4.0.432",
+ "resolved": "https://registry.npmjs.org/@remotion/compositor-linux-x64-gnu/-/compositor-linux-x64-gnu-4.0.432.tgz",
+ "integrity": "sha512-KR/oSdCX6PBjO6K2KhYpC+QBbVMo8aXwQoTHaVwNl94+t7tyXDj7Vi8b3EJaIDSeXAc7lNjs54Tzx/WQ615Avg==",
+ "cpu": [
+ "x64"
+ ],
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@remotion/compositor-linux-x64-musl": {
+ "version": "4.0.429",
+ "resolved": "https://registry.npmjs.org/@remotion/compositor-linux-x64-musl/-/compositor-linux-x64-musl-4.0.429.tgz",
+ "integrity": "sha512-tlgWyrOWFDw1Z8IyPGDrQhd2B3saZ/sGRC7nN0XhcbMnaq3wxWe+cUSCJPT9kFNR9tKNCZ0y/jG/B8h6Z0GiHQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@remotion/compositor-win32-x64-msvc": {
+ "version": "4.0.429",
+ "resolved": "https://registry.npmjs.org/@remotion/compositor-win32-x64-msvc/-/compositor-win32-x64-msvc-4.0.429.tgz",
+ "integrity": "sha512-YnpRTVwrOnDA8nYCZEP6Tdx+Ea+LQO4kXm9sjg2Rxe3s55lt+dUURl1yvOSKTW8nKZlfDAqldI1YDVvfD8NnCQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
"node_modules/@remotion/licensing": {
"version": "4.0.429",
"dev": true,
@@ -1393,6 +1531,19 @@
"react-dom": ">=16.8.0"
}
},
+ "node_modules/@remotion/renderer/node_modules/@remotion/compositor-linux-x64-gnu": {
+ "version": "4.0.429",
+ "resolved": "https://registry.npmjs.org/@remotion/compositor-linux-x64-gnu/-/compositor-linux-x64-gnu-4.0.429.tgz",
+ "integrity": "sha512-YRe6+oSnNFpc2jgkrmAISAO2xUA0LqzE49ykqe0zCH3MXKtof76lMwDPoFJT1C4vDKErjSedltQNnnG7bPQGlw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
"node_modules/@remotion/renderer/node_modules/source-map": {
"version": "0.8.0-beta.0",
"dev": true,
@@ -1646,6 +1797,158 @@
"@rspack/binding-win32-x64-msvc": "1.7.6"
}
},
+ "node_modules/@rspack/binding-darwin-arm64": {
+ "version": "1.7.6",
+ "resolved": "https://registry.npmjs.org/@rspack/binding-darwin-arm64/-/binding-darwin-arm64-1.7.6.tgz",
+ "integrity": "sha512-NZ9AWtB1COLUX1tA9HQQvWpTy07NSFfKBU8A6ylWd5KH8AePZztpNgLLAVPTuNO4CZXYpwcoclf8jG/luJcQdQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rspack/binding-darwin-x64": {
+ "version": "1.7.6",
+ "resolved": "https://registry.npmjs.org/@rspack/binding-darwin-x64/-/binding-darwin-x64-1.7.6.tgz",
+ "integrity": "sha512-J2g6xk8ZS7uc024dNTGTHxoFzFovAZIRixUG7PiciLKTMP78svbSSWrmW6N8oAsAkzYfJWwQpVgWfFNRHvYxSw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rspack/binding-linux-arm64-gnu": {
+ "version": "1.7.6",
+ "resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.7.6.tgz",
+ "integrity": "sha512-eQfcsaxhFrv5FmtaA7+O1F9/2yFDNIoPZzV/ZvqvFz5bBXVc4FAm/1fVpBg8Po/kX1h0chBc7Xkpry3cabFW8w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rspack/binding-linux-arm64-musl": {
+ "version": "1.7.6",
+ "resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.7.6.tgz",
+ "integrity": "sha512-DfQXKiyPIl7i1yECHy4eAkSmlUzzsSAbOjgMuKn7pudsWf483jg0UUYutNgXSlBjc/QSUp7906Cg8oty9OfwPA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rspack/binding-linux-x64-gnu": {
+ "version": "1.7.7",
+ "resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.7.7.tgz",
+ "integrity": "sha512-aRvf8gCI7jDeEN9i4u9fY5coa3ZAyHzGVA4ZhTJCgZ5wWA5A9SQewMSq7khS1WAAFE1USlk1tUuPujnrGoYrGg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rspack/binding-linux-x64-musl": {
+ "version": "1.7.6",
+ "resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-musl/-/binding-linux-x64-musl-1.7.6.tgz",
+ "integrity": "sha512-rEy6MHKob02t/77YNgr6dREyJ0e0tv1X6Xsg8Z5E7rPXead06zefUbfazj4RELYySWnM38ovZyJAkPx/gOn3VA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rspack/binding-wasm32-wasi": {
+ "version": "1.7.6",
+ "resolved": "https://registry.npmjs.org/@rspack/binding-wasm32-wasi/-/binding-wasm32-wasi-1.7.6.tgz",
+ "integrity": "sha512-YupOrz0daSG+YBbCIgpDgzfMM38YpChv+afZpaxx5Ml7xPeAZIIdgWmLHnQ2rts73N2M1NspAiBwV00Xx0N4Vg==",
+ "cpu": [
+ "wasm32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@napi-rs/wasm-runtime": "1.0.7"
+ }
+ },
+ "node_modules/@rspack/binding-win32-arm64-msvc": {
+ "version": "1.7.6",
+ "resolved": "https://registry.npmjs.org/@rspack/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.7.6.tgz",
+ "integrity": "sha512-INj7aVXjBvlZ84kEhSK4kJ484ub0i+BzgnjDWOWM1K+eFYDZjLdAsQSS3fGGXwVc3qKbPIssFfnftATDMTEJHQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rspack/binding-win32-ia32-msvc": {
+ "version": "1.7.6",
+ "resolved": "https://registry.npmjs.org/@rspack/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-1.7.6.tgz",
+ "integrity": "sha512-lXGvC+z67UMcw58In12h8zCa9IyYRmuptUBMItQJzu+M278aMuD1nETyGLL7e4+OZ2lvrnnBIcjXN1hfw2yRzw==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rspack/binding-win32-x64-msvc": {
+ "version": "1.7.6",
+ "resolved": "https://registry.npmjs.org/@rspack/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.7.6.tgz",
+ "integrity": "sha512-zeUxEc0ZaPpmaYlCeWcjSJUPuRRySiSHN23oJ2Xyw0jsQ01Qm4OScPdr0RhEOFuK/UE+ANyRtDo4zJsY52Hadw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rspack/binding/node_modules/@rspack/binding-linux-x64-gnu": {
+ "version": "1.7.6",
+ "resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.7.6.tgz",
+ "integrity": "sha512-NdA+2X3lk2GGrMMnTGyYTzM3pn+zNjaqXqlgKmFBXvjfZqzSsKq3pdD1KHZCd5QHN+Fwvoszj0JFsquEVhE1og==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
"node_modules/@rspack/core": {
"version": "1.7.6",
"dev": true,
@@ -1700,6 +2003,17 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/@tybys/wasm-util": {
+ "version": "0.10.1",
+ "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz",
+ "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
"node_modules/@types/chai": {
"version": "5.2.3",
"dev": true,
diff --git a/package.json b/package.json
index d87deaf5..3244784d 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "aicodeman",
- "version": "0.3.1",
+ "version": "0.3.2",
"description": "The missing control plane for AI coding agents - run 20 autonomous agents with real-time monitoring and session persistence",
"type": "module",
"main": "dist/index.js",
@@ -51,6 +51,8 @@
"@fastify/compress": "^8.3.1",
"@fastify/cookie": "^11.0.2",
"@fastify/static": "^8.0.0",
+ "@remotion/compositor-linux-x64-gnu": "^4.0.432",
+ "@rspack/binding-linux-x64-gnu": "^1.7.7",
"chalk": "^5.3.0",
"chokidar": "^3.6.0",
"commander": "^12.1.0",
diff --git a/public b/public
new file mode 120000
index 00000000..b9e407f6
--- /dev/null
+++ b/public
@@ -0,0 +1 @@
+tools/remotion/public
\ No newline at end of file
diff --git a/src/web/public/app.js b/src/web/public/app.js
index 5f226a6d..3817b42e 100644
--- a/src/web/public/app.js
+++ b/src/web/public/app.js
@@ -3560,6 +3560,24 @@ class CodemanApp {
if (fileBrowserPanel) {
fileBrowserPanel.classList.add('visible');
this.loadFileBrowser(sessionId);
+ // Attach drag listeners if not already attached
+ if (!this.fileBrowserDragListeners) {
+ const header = fileBrowserPanel.querySelector('.file-browser-header');
+ if (header) {
+ const onFirstDrag = () => {
+ if (!fileBrowserPanel.style.left) {
+ const rect = fileBrowserPanel.getBoundingClientRect();
+ fileBrowserPanel.style.left = `${rect.left}px`;
+ fileBrowserPanel.style.top = `${rect.top}px`;
+ fileBrowserPanel.style.right = 'auto';
+ }
+ };
+ header.addEventListener('mousedown', onFirstDrag);
+ header.addEventListener('touchstart', onFirstDrag, { passive: true });
+ this.fileBrowserDragListeners = this.makeWindowDraggable(fileBrowserPanel, header);
+ this.fileBrowserDragListeners._onFirstDrag = onFirstDrag;
+ }
+ }
}
}
});
@@ -7050,6 +7068,25 @@ class CodemanApp {
if (showFileBrowser && this.activeSessionId) {
fileBrowserPanel.classList.add('visible');
this.loadFileBrowser(this.activeSessionId);
+ // Attach drag listeners if not already attached
+ if (!this.fileBrowserDragListeners) {
+ const header = fileBrowserPanel.querySelector('.file-browser-header');
+ if (header) {
+ // Convert right-positioned to left/top before drag so makeWindowDraggable works
+ const onFirstDrag = () => {
+ if (!fileBrowserPanel.style.left) {
+ const rect = fileBrowserPanel.getBoundingClientRect();
+ fileBrowserPanel.style.left = `${rect.left}px`;
+ fileBrowserPanel.style.top = `${rect.top}px`;
+ fileBrowserPanel.style.right = 'auto';
+ }
+ };
+ header.addEventListener('mousedown', onFirstDrag);
+ header.addEventListener('touchstart', onFirstDrag, { passive: true });
+ this.fileBrowserDragListeners = this.makeWindowDraggable(fileBrowserPanel, header);
+ this.fileBrowserDragListeners._onFirstDrag = onFirstDrag;
+ }
+ }
} else {
fileBrowserPanel.classList.remove('visible');
}
@@ -10349,6 +10386,7 @@ class CodemanApp {
fileBrowserExpandedDirs = new Set();
fileBrowserFilter = '';
fileBrowserAllExpanded = false;
+ fileBrowserDragListeners = null;
filePreviewContent = '';
async loadFileBrowser(sessionId) {
@@ -10532,6 +10570,29 @@ class CodemanApp {
const panel = this.$('fileBrowserPanel');
if (panel) {
panel.classList.remove('visible');
+ // Reset position so it reopens at default location
+ panel.style.left = '';
+ panel.style.top = '';
+ panel.style.bottom = '';
+ panel.style.right = '';
+ }
+ // Clean up drag listeners
+ if (this.fileBrowserDragListeners) {
+ const dl = this.fileBrowserDragListeners;
+ document.removeEventListener('mousemove', dl.move);
+ document.removeEventListener('mouseup', dl.up);
+ document.removeEventListener('touchmove', dl.touchMove);
+ document.removeEventListener('touchend', dl.up);
+ document.removeEventListener('touchcancel', dl.up);
+ if (dl.handle) {
+ dl.handle.removeEventListener('mousedown', dl.handleMouseDown);
+ dl.handle.removeEventListener('touchstart', dl.handleTouchStart);
+ if (dl._onFirstDrag) {
+ dl.handle.removeEventListener('mousedown', dl._onFirstDrag);
+ dl.handle.removeEventListener('touchstart', dl._onFirstDrag);
+ }
+ }
+ this.fileBrowserDragListeners = null;
}
// Save setting
const settings = this.loadAppSettingsFromStorage();
diff --git a/src/web/public/styles.css b/src/web/public/styles.css
index 6fba197d..4418d1a5 100644
--- a/src/web/public/styles.css
+++ b/src/web/public/styles.css
@@ -5825,6 +5825,8 @@ kbd {
background: var(--bg-input);
border-bottom: 1px solid var(--border);
flex-shrink: 0;
+ cursor: move;
+ user-select: none;
}
.file-browser-title {
diff --git a/tools/remotion/Root.tsx b/tools/remotion/Root.tsx
index b4592351..9fda9557 100644
--- a/tools/remotion/Root.tsx
+++ b/tools/remotion/Root.tsx
@@ -1,16 +1,27 @@
import React from 'react';
import { Composition } from 'remotion';
import { CodemanDemo, TOTAL_FRAMES } from './compositions/CodemanDemo';
+import { ZerolagDemo, ZEROLAG_TOTAL_FRAMES } from './compositions/ZerolagDemo';
export const RemotionRoot: React.FC = () => {
return (
-
+
+ {/* iOS status bar in the safe area */}
+