conductor-oss 0.2.17 → 0.2.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. package/dist/index.d.ts +1 -1
  2. package/dist/index.js +4 -2
  3. package/dist/index.js.map +1 -1
  4. package/node_modules/@conductor-oss/plugin-agent-amp/package.json +1 -1
  5. package/node_modules/@conductor-oss/plugin-agent-ccr/package.json +1 -1
  6. package/node_modules/@conductor-oss/plugin-agent-claude-code/package.json +1 -1
  7. package/node_modules/@conductor-oss/plugin-agent-codex/package.json +1 -1
  8. package/node_modules/@conductor-oss/plugin-agent-cursor-cli/package.json +1 -1
  9. package/node_modules/@conductor-oss/plugin-agent-droid/package.json +1 -1
  10. package/node_modules/@conductor-oss/plugin-agent-gemini/package.json +1 -1
  11. package/node_modules/@conductor-oss/plugin-agent-github-copilot/package.json +1 -1
  12. package/node_modules/@conductor-oss/plugin-agent-opencode/package.json +1 -1
  13. package/node_modules/@conductor-oss/plugin-agent-qwen-code/package.json +1 -1
  14. package/node_modules/@conductor-oss/plugin-mcp-server/package.json +1 -1
  15. package/node_modules/@conductor-oss/plugin-notifier-desktop/package.json +1 -1
  16. package/node_modules/@conductor-oss/plugin-notifier-discord/package.json +1 -1
  17. package/node_modules/@conductor-oss/plugin-runtime-tmux/package.json +1 -1
  18. package/node_modules/@conductor-oss/plugin-scm-github/package.json +1 -1
  19. package/node_modules/@conductor-oss/plugin-terminal-web/package.json +1 -1
  20. package/node_modules/@conductor-oss/plugin-tracker-github/package.json +1 -1
  21. package/node_modules/@conductor-oss/plugin-webhook/package.json +1 -1
  22. package/node_modules/@conductor-oss/plugin-workspace-worktree/package.json +1 -1
  23. package/package.json +21 -21
  24. package/web/.next/standalone/packages/web/.next/BUILD_ID +1 -1
  25. package/web/.next/standalone/packages/web/.next/build-manifest.json +2 -2
  26. package/web/.next/standalone/packages/web/.next/prerender-manifest.json +3 -3
  27. package/web/.next/standalone/packages/web/.next/server/app/_global-error.html +2 -2
  28. package/web/.next/standalone/packages/web/.next/server/app/_global-error.rsc +1 -1
  29. package/web/.next/standalone/packages/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  30. package/web/.next/standalone/packages/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  31. package/web/.next/standalone/packages/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  32. package/web/.next/standalone/packages/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  33. package/web/.next/standalone/packages/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  34. package/web/.next/standalone/packages/web/.next/server/app/_not-found/page/server-reference-manifest.json +7 -7
  35. package/web/.next/standalone/packages/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  36. package/web/.next/standalone/packages/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  37. package/web/.next/standalone/packages/web/.next/server/app/_not-found.html +1 -1
  38. package/web/.next/standalone/packages/web/.next/server/app/_not-found.rsc +3 -3
  39. package/web/.next/standalone/packages/web/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  40. package/web/.next/standalone/packages/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  41. package/web/.next/standalone/packages/web/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
  42. package/web/.next/standalone/packages/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  43. package/web/.next/standalone/packages/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  44. package/web/.next/standalone/packages/web/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  45. package/web/.next/standalone/packages/web/.next/server/app/api/access/route.js.nft.json +1 -1
  46. package/web/.next/standalone/packages/web/.next/server/app/api/attachments/route.js.nft.json +1 -1
  47. package/web/.next/standalone/packages/web/.next/server/app/api/boards/route.js.nft.json +1 -1
  48. package/web/.next/standalone/packages/web/.next/server/app/api/config/route.js.nft.json +1 -1
  49. package/web/.next/standalone/packages/web/.next/server/app/api/context-files/route.js.nft.json +1 -1
  50. package/web/.next/standalone/packages/web/.next/server/app/api/events/route.js.nft.json +1 -1
  51. package/web/.next/standalone/packages/web/.next/server/app/api/filesystem/directory/route.js.nft.json +1 -1
  52. package/web/.next/standalone/packages/web/.next/server/app/api/github/repos/route.js.nft.json +1 -1
  53. package/web/.next/standalone/packages/web/.next/server/app/api/health/boards/route.js.nft.json +1 -1
  54. package/web/.next/standalone/packages/web/.next/server/app/api/health/sessions/route.js.nft.json +1 -1
  55. package/web/.next/standalone/packages/web/.next/server/app/api/notifications/route.js.nft.json +1 -1
  56. package/web/.next/standalone/packages/web/.next/server/app/api/preferences/route.js.nft.json +1 -1
  57. package/web/.next/standalone/packages/web/.next/server/app/api/repositories/route.js.nft.json +1 -1
  58. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/actions/route.js.nft.json +1 -1
  59. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/checks/route.js.nft.json +1 -1
  60. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/diff/route.js.nft.json +1 -1
  61. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/feed/route.js.nft.json +1 -1
  62. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/feedback/route.js.nft.json +1 -1
  63. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/files/route.js.nft.json +1 -1
  64. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/keys/route.js.nft.json +1 -1
  65. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/kill/route.js.nft.json +1 -1
  66. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/output/route.js.nft.json +1 -1
  67. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/output/stream/route.js.nft.json +1 -1
  68. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/restore/route.js.nft.json +1 -1
  69. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/route.js.nft.json +1 -1
  70. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/send/route.js.nft.json +1 -1
  71. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/route.js.nft.json +1 -1
  72. package/web/.next/standalone/packages/web/.next/server/app/api/spawn/route.js.nft.json +1 -1
  73. package/web/.next/standalone/packages/web/.next/server/app/api/workspaces/branches/route.js.nft.json +1 -1
  74. package/web/.next/standalone/packages/web/.next/server/app/api/workspaces/route.js.nft.json +1 -1
  75. package/web/.next/standalone/packages/web/.next/server/app/index.html +1 -1
  76. package/web/.next/standalone/packages/web/.next/server/app/index.rsc +4 -4
  77. package/web/.next/standalone/packages/web/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  78. package/web/.next/standalone/packages/web/.next/server/app/index.segments/_full.segment.rsc +4 -4
  79. package/web/.next/standalone/packages/web/.next/server/app/index.segments/_head.segment.rsc +1 -1
  80. package/web/.next/standalone/packages/web/.next/server/app/index.segments/_index.segment.rsc +3 -3
  81. package/web/.next/standalone/packages/web/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  82. package/web/.next/standalone/packages/web/.next/server/app/page/react-loadable-manifest.json +1 -1
  83. package/web/.next/standalone/packages/web/.next/server/app/page/server-reference-manifest.json +7 -7
  84. package/web/.next/standalone/packages/web/.next/server/app/page.js.nft.json +1 -1
  85. package/web/.next/standalone/packages/web/.next/server/app/page_client-reference-manifest.js +1 -1
  86. package/web/.next/standalone/packages/web/.next/server/app/sessions/[id]/page/server-reference-manifest.json +7 -7
  87. package/web/.next/standalone/packages/web/.next/server/app/sessions/[id]/page.js.nft.json +1 -1
  88. package/web/.next/standalone/packages/web/.next/server/app/sessions/[id]/page_client-reference-manifest.js +1 -1
  89. package/web/.next/standalone/packages/web/.next/server/app/sign-in/[[...sign-in]]/page/server-reference-manifest.json +7 -7
  90. package/web/.next/standalone/packages/web/.next/server/app/sign-in/[[...sign-in]]/page.js.nft.json +1 -1
  91. package/web/.next/standalone/packages/web/.next/server/app/sign-in/[[...sign-in]]/page_client-reference-manifest.js +1 -1
  92. package/web/.next/standalone/packages/web/.next/server/app/unlock/page/server-reference-manifest.json +7 -7
  93. package/web/.next/standalone/packages/web/.next/server/app/unlock/page.js.nft.json +1 -1
  94. package/web/.next/standalone/packages/web/.next/server/app/unlock/page_client-reference-manifest.js +1 -1
  95. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/{[root-of-the-server]__2814b563._.js → [root-of-the-server]__12eb9005._.js} +2 -2
  96. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/[root-of-the-server]__6622b514._.js +1 -1
  97. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/[root-of-the-server]__869d9ac0._.js +1 -1
  98. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/[root-of-the-server]__9dc23e5a._.js +1 -1
  99. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/[root-of-the-server]__b388693f._.js +1 -1
  100. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/_0e1412de._.js +1 -1
  101. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/{_b0abbdd9._.js → _20a4007d._.js} +2 -2
  102. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/_69e05fca._.js +1 -1
  103. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/_80efe193._.js +1 -1
  104. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/_b6d31783._.js +1 -1
  105. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/{_0973acf3._.js → _b88bcf2c._.js} +2 -2
  106. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/_c0f0e227._.js +1 -1
  107. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/_f36ddaa9._.js +1 -1
  108. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/{node_modules_@clerk_nextjs_dist_esm_app-router_ae92e1b6._.js → node_modules_@clerk_nextjs_dist_esm_app-router_78af9fdf._.js} +2 -2
  109. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/node_modules_@clerk_nextjs_dist_esm_app-router_c4bad84a._.js +3 -0
  110. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/node_modules_f2ebd7a9._.js +1 -1
  111. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/packages_web_src_79316445._.js +1 -1
  112. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/packages_web_src_a078c137._.js +1 -1
  113. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/packages_web_src_app_page_tsx_cd282e82._.js +1 -1
  114. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/{packages_web_src_components_f2c9e753._.js → packages_web_src_components_3809c507._.js} +1 -1
  115. package/web/.next/standalone/packages/web/.next/server/pages/404.html +1 -1
  116. package/web/.next/standalone/packages/web/.next/server/pages/500.html +2 -2
  117. package/web/.next/standalone/packages/web/.next/server/server-reference-manifest.js +1 -1
  118. package/web/.next/standalone/packages/web/.next/server/server-reference-manifest.json +8 -8
  119. package/web/.next/standalone/packages/web/.next/static/chunks/1e67fbc3874d3f51.js +1 -0
  120. package/web/.next/standalone/packages/web/.next/static/chunks/{9785347bf1d88302.js → 28dd6ef2af62b509.js} +3 -3
  121. package/web/.next/standalone/packages/web/.next/static/chunks/2b2a24dff50e7dc9.js +1 -0
  122. package/web/.next/standalone/packages/web/.next/static/chunks/483eb2824f5282c7.js +1 -0
  123. package/web/.next/standalone/packages/web/.next/static/chunks/695b7cb206c6dadd.js +1 -0
  124. package/web/.next/standalone/packages/web/.next/static/chunks/860d84e1f09476a4.css +3 -0
  125. package/web/.next/standalone/packages/web/.next/static/chunks/c959976264f14eba.js +1 -0
  126. package/web/.next/{static/chunks/4c566fd1e4a92935.js → standalone/packages/web/.next/static/chunks/d9d05e7b540400af.js} +1 -1
  127. package/web/.next/standalone/packages/web/.next/static/chunks/e862e73b22fe29c2.js +1 -0
  128. package/web/.next/{static/chunks/719697e99b51d55b.js → standalone/packages/web/.next/static/chunks/f5d9ad0f62ede339.js} +1 -1
  129. package/web/.next/standalone/packages/web/src/app/page.tsx +3 -4555
  130. package/web/.next/standalone/packages/web/src/app/sessions/[id]/page.tsx +2 -115
  131. package/web/.next/standalone/packages/web/src/components/layout/AppShell.tsx +62 -2
  132. package/web/.next/standalone/packages/web/src/components/layout/TopBar.tsx +13 -11
  133. package/web/.next/standalone/packages/web/src/components/layout/WorkspaceSidebarPanel.tsx +68 -10
  134. package/web/.next/standalone/packages/web/src/features/dashboard/DashboardClient.tsx +4571 -0
  135. package/web/.next/standalone/packages/web/src/features/dashboard/components/WorkspaceOverview.tsx +296 -0
  136. package/web/.next/standalone/packages/web/src/features/sessions/SessionPageClient.tsx +125 -0
  137. package/web/.next/standalone/packages/web/src/hooks/useSessionFeed.ts +17 -13
  138. package/web/.next/standalone/packages/web/src/hooks/useSessions.ts +37 -7
  139. package/web/.next/static/chunks/1e67fbc3874d3f51.js +1 -0
  140. package/web/.next/static/chunks/{9785347bf1d88302.js → 28dd6ef2af62b509.js} +3 -3
  141. package/web/.next/static/chunks/2b2a24dff50e7dc9.js +1 -0
  142. package/web/.next/static/chunks/483eb2824f5282c7.js +1 -0
  143. package/web/.next/static/chunks/695b7cb206c6dadd.js +1 -0
  144. package/web/.next/static/chunks/860d84e1f09476a4.css +3 -0
  145. package/web/.next/static/chunks/c959976264f14eba.js +1 -0
  146. package/web/.next/{standalone/packages/web/.next/static/chunks/4c566fd1e4a92935.js → static/chunks/d9d05e7b540400af.js} +1 -1
  147. package/web/.next/static/chunks/e862e73b22fe29c2.js +1 -0
  148. package/web/.next/{standalone/packages/web/.next/static/chunks/719697e99b51d55b.js → static/chunks/f5d9ad0f62ede339.js} +1 -1
  149. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/node_modules_@clerk_nextjs_dist_esm_app-router_39a27288._.js +0 -3
  150. package/web/.next/standalone/packages/web/.next/static/chunks/1867dba46fcc022e.js +0 -1
  151. package/web/.next/standalone/packages/web/.next/static/chunks/995a5af4e8529901.js +0 -1
  152. package/web/.next/standalone/packages/web/.next/static/chunks/ab8ef6c7dfc78082.js +0 -1
  153. package/web/.next/standalone/packages/web/.next/static/chunks/b5e2d1ef92e508a0.js +0 -1
  154. package/web/.next/standalone/packages/web/.next/static/chunks/dc65fd7512517f7d.js +0 -1
  155. package/web/.next/standalone/packages/web/.next/static/chunks/e8283780c26eaa91.js +0 -1
  156. package/web/.next/standalone/packages/web/.next/static/chunks/fe557eb4039d8a8d.css +0 -3
  157. package/web/.next/static/chunks/1867dba46fcc022e.js +0 -1
  158. package/web/.next/static/chunks/995a5af4e8529901.js +0 -1
  159. package/web/.next/static/chunks/ab8ef6c7dfc78082.js +0 -1
  160. package/web/.next/static/chunks/b5e2d1ef92e508a0.js +0 -1
  161. package/web/.next/static/chunks/dc65fd7512517f7d.js +0 -1
  162. package/web/.next/static/chunks/e8283780c26eaa91.js +0 -1
  163. package/web/.next/static/chunks/fe557eb4039d8a8d.css +0 -3
  164. /package/web/.next/standalone/packages/web/.next/static/{U7zhuWy5hxiSO9ZXtDOfi → eSF3qxz6RT8UXiwr-uibJ}/_buildManifest.js +0 -0
  165. /package/web/.next/standalone/packages/web/.next/static/{U7zhuWy5hxiSO9ZXtDOfi → eSF3qxz6RT8UXiwr-uibJ}/_clientMiddlewareManifest.json +0 -0
  166. /package/web/.next/standalone/packages/web/.next/static/{U7zhuWy5hxiSO9ZXtDOfi → eSF3qxz6RT8UXiwr-uibJ}/_ssgManifest.js +0 -0
  167. /package/web/.next/static/{U7zhuWy5hxiSO9ZXtDOfi → eSF3qxz6RT8UXiwr-uibJ}/_buildManifest.js +0 -0
  168. /package/web/.next/static/{U7zhuWy5hxiSO9ZXtDOfi → eSF3qxz6RT8UXiwr-uibJ}/_clientMiddlewareManifest.json +0 -0
  169. /package/web/.next/static/{U7zhuWy5hxiSO9ZXtDOfi → eSF3qxz6RT8UXiwr-uibJ}/_ssgManifest.js +0 -0
@@ -1,120 +1,7 @@
1
1
  "use client";
2
2
 
3
- import { useEffect, useMemo, useState } from "react";
4
- import { useParams, useRouter } from "next/navigation";
5
- import { AppShell } from "@/components/layout/AppShell";
6
- import { WorkspaceSidebarPanel } from "@/components/layout/WorkspaceSidebarPanel";
7
- import { SessionDetail } from "@/components/sessions/SessionDetail";
8
- import { useConfig } from "@/hooks/useConfig";
9
- import { useSessions } from "@/hooks/useSessions";
10
- import type { DashboardSession } from "@/lib/types";
3
+ import SessionPageClient from "@/features/sessions/SessionPageClient";
11
4
 
12
5
  export default function SessionPage() {
13
- const params = useParams<{ id: string }>();
14
- const router = useRouter();
15
- const { projects } = useConfig();
16
- const { sessions, refresh } = useSessions();
17
- const dashboardSessions = sessions as unknown as DashboardSession[];
18
- const [sidebarOpen, setSidebarOpen] = useState(true);
19
- const [selectedProjectId, setSelectedProjectId] = useState<string | null>(null);
20
-
21
- useEffect(() => {
22
- if (typeof window === "undefined") return;
23
- if (window.innerWidth < 1024) {
24
- setSidebarOpen(false);
25
- }
26
- }, []);
27
-
28
- const selectedSession = useMemo(
29
- () => dashboardSessions.find((session) => session.id === params.id) ?? null,
30
- [dashboardSessions, params.id],
31
- );
32
-
33
- useEffect(() => {
34
- if (selectedSession?.projectId) {
35
- setSelectedProjectId(selectedSession.projectId);
36
- return;
37
- }
38
- if (!selectedProjectId && projects.length > 0) {
39
- setSelectedProjectId(projects[0]?.id ?? null);
40
- }
41
- }, [projects, selectedProjectId, selectedSession?.projectId]);
42
-
43
- const toggleSidebar = () => setSidebarOpen((prev) => !prev);
44
-
45
- const closeSidebarOnMobile = () => {
46
- if (typeof window !== "undefined" && window.innerWidth < 1024) {
47
- setSidebarOpen(false);
48
- }
49
- };
50
-
51
- async function handleArchiveSession(sessionId: string) {
52
- let res = await fetch(`/api/sessions/${encodeURIComponent(sessionId)}/archive`, {
53
- method: "POST",
54
- });
55
- let data = (await res.json().catch(() => null)) as
56
- | { ok?: boolean; error?: string }
57
- | null;
58
-
59
- if (res.status === 404) {
60
- res = await fetch(`/api/sessions/${encodeURIComponent(sessionId)}/actions`, {
61
- method: "POST",
62
- headers: { "Content-Type": "application/json" },
63
- body: JSON.stringify({ action: "archive" }),
64
- });
65
- data = (await res.json().catch(() => null)) as
66
- | { ok?: boolean; error?: string }
67
- | null;
68
- }
69
-
70
- if (!res.ok) {
71
- throw new Error(data?.error ?? `Failed to archive session: ${res.status}`);
72
- }
73
-
74
- if (sessionId === params.id) {
75
- router.push("/");
76
- return;
77
- }
78
-
79
- await refresh();
80
- }
81
-
82
- if (!params.id) {
83
- return (
84
- <div className="flex h-dvh min-h-[100dvh] items-center justify-center">
85
- <span className="text-[13px] text-[var(--text-muted)]">No session ID provided</span>
86
- </div>
87
- );
88
- }
89
-
90
- return (
91
- <AppShell
92
- sidebarOpen={sidebarOpen}
93
- onToggleSidebar={toggleSidebar}
94
- sidebar={
95
- <WorkspaceSidebarPanel
96
- orgLabel="conductor-oss"
97
- projects={projects}
98
- selectedProjectId={selectedProjectId}
99
- onSelectProject={(projectId) => {
100
- setSelectedProjectId(projectId);
101
- }}
102
- sessions={dashboardSessions}
103
- selectedSessionId={params.id}
104
- onSelectSession={(sessionId) => {
105
- router.push(`/sessions/${encodeURIComponent(sessionId)}?tab=chat`);
106
- closeSidebarOnMobile();
107
- }}
108
- onArchiveSession={handleArchiveSession}
109
- onCreateWorkspace={() => {
110
- router.push("/");
111
- }}
112
- />
113
- }
114
- >
115
- <div className="min-h-0 flex-1 overflow-hidden">
116
- <SessionDetail sessionId={params.id} />
117
- </div>
118
- </AppShell>
119
- );
6
+ return <SessionPageClient />;
120
7
  }
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
 
3
- import type { CSSProperties, ReactNode } from "react";
3
+ import { useEffect, useState, type CSSProperties, type ReactNode } from "react";
4
4
  import { PanelLeftOpen, PanelRightClose } from "lucide-react";
5
5
  import { cn } from "@/lib/cn";
6
6
 
@@ -11,13 +11,63 @@ interface AppShellProps {
11
11
  onToggleSidebar: () => void;
12
12
  }
13
13
 
14
+ const DEFAULT_SIDEBAR_WIDTH = 356;
15
+ const MIN_SIDEBAR_WIDTH = 296;
16
+ const MAX_SIDEBAR_WIDTH = 460;
17
+ const SIDEBAR_WIDTH_STORAGE_KEY = "conductor-workspace-sidebar-width";
18
+
14
19
  export function AppShell({
15
20
  sidebar,
16
21
  children,
17
22
  sidebarOpen,
18
23
  onToggleSidebar,
19
24
  }: AppShellProps) {
20
- const shellStyle = { "--workspace-sidebar-width": "356px" } as CSSProperties;
25
+ const [sidebarWidth, setSidebarWidth] = useState(DEFAULT_SIDEBAR_WIDTH);
26
+ const [resizing, setResizing] = useState(false);
27
+
28
+ useEffect(() => {
29
+ try {
30
+ const stored = window.localStorage.getItem(SIDEBAR_WIDTH_STORAGE_KEY);
31
+ const parsed = stored ? Number.parseInt(stored, 10) : Number.NaN;
32
+ if (Number.isFinite(parsed)) {
33
+ setSidebarWidth(Math.min(MAX_SIDEBAR_WIDTH, Math.max(MIN_SIDEBAR_WIDTH, parsed)));
34
+ }
35
+ } catch {
36
+ // Ignore invalid persisted width values.
37
+ }
38
+ }, []);
39
+
40
+ useEffect(() => {
41
+ if (!resizing) return;
42
+
43
+ const handlePointerMove = (event: MouseEvent) => {
44
+ const nextWidth = Math.min(MAX_SIDEBAR_WIDTH, Math.max(MIN_SIDEBAR_WIDTH, event.clientX));
45
+ setSidebarWidth(nextWidth);
46
+ };
47
+
48
+ const handlePointerUp = () => {
49
+ setResizing(false);
50
+ };
51
+
52
+ window.addEventListener("mousemove", handlePointerMove);
53
+ window.addEventListener("mouseup", handlePointerUp);
54
+ document.body.style.userSelect = "none";
55
+ document.body.style.cursor = "col-resize";
56
+
57
+ return () => {
58
+ window.removeEventListener("mousemove", handlePointerMove);
59
+ window.removeEventListener("mouseup", handlePointerUp);
60
+ document.body.style.userSelect = "";
61
+ document.body.style.cursor = "";
62
+ try {
63
+ window.localStorage.setItem(SIDEBAR_WIDTH_STORAGE_KEY, String(sidebarWidth));
64
+ } catch {
65
+ // Ignore storage write failures.
66
+ }
67
+ };
68
+ }, [resizing, sidebarWidth]);
69
+
70
+ const shellStyle = { "--workspace-sidebar-width": `${sidebarWidth}px` } as CSSProperties;
21
71
 
22
72
  return (
23
73
  <div
@@ -45,6 +95,16 @@ export function AppShell({
45
95
  {sidebar}
46
96
  </aside>
47
97
 
98
+ {sidebarOpen ? (
99
+ <div
100
+ className="absolute bottom-0 left-[var(--workspace-sidebar-width)] top-0 z-30 hidden w-2 -translate-x-1/2 cursor-col-resize lg:block"
101
+ onMouseDown={() => setResizing(true)}
102
+ aria-hidden="true"
103
+ >
104
+ <div className="mx-auto h-full w-px bg-transparent transition-colors hover:bg-[var(--vk-border)]" />
105
+ </div>
106
+ ) : null}
107
+
48
108
  {sidebarOpen && (
49
109
  <button
50
110
  type="button"
@@ -18,17 +18,19 @@ export const TopBar = memo(function TopBar({ session, fallbackTitle, onOpenPrefe
18
18
  <div className="min-w-0 flex-1 text-center">
19
19
  <span className="block truncate">{title}</span>
20
20
  </div>
21
- <div className="ml-1 flex shrink-0">
22
- <button
23
- type="button"
24
- onClick={onOpenPreferences}
25
- className="inline-flex h-6 w-6 items-center justify-center rounded-[4px] text-[var(--vk-text-muted)] hover:bg-[var(--vk-bg-hover)] hover:text-[var(--vk-text-normal)]"
26
- aria-label="Open preferences"
27
- title="Preferences"
28
- >
29
- <Settings className="h-3.5 w-3.5" />
30
- </button>
31
- </div>
21
+ {onOpenPreferences ? (
22
+ <div className="ml-1 flex shrink-0">
23
+ <button
24
+ type="button"
25
+ onClick={onOpenPreferences}
26
+ className="inline-flex h-6 w-6 items-center justify-center rounded-[4px] text-[var(--vk-text-muted)] hover:bg-[var(--vk-bg-hover)] hover:text-[var(--vk-text-normal)]"
27
+ aria-label="Open preferences"
28
+ title="Preferences"
29
+ >
30
+ <Settings className="h-3.5 w-3.5" />
31
+ </button>
32
+ </div>
33
+ ) : null}
32
34
  </header>
33
35
  );
34
36
  });
@@ -1,9 +1,9 @@
1
1
  "use client";
2
2
 
3
- import { memo } from "react";
4
- import { Plus } from "lucide-react";
3
+ import { memo, useMemo } from "react";
4
+ import { Layers3, Plus } from "lucide-react";
5
5
  import { cn } from "@/lib/cn";
6
- import type { DashboardSession } from "@/lib/types";
6
+ import { getAttentionLevel, type DashboardSession } from "@/lib/types";
7
7
  import { Sidebar } from "@/components/layout/Sidebar";
8
8
 
9
9
  interface ProjectItem {
@@ -34,16 +34,47 @@ export const WorkspaceSidebarPanel = memo(function WorkspaceSidebarPanel({
34
34
  onArchiveSession,
35
35
  onCreateWorkspace,
36
36
  }: WorkspaceSidebarPanelProps) {
37
+ const sessionCountByProject = useMemo(() => {
38
+ const counts = new Map<string, { total: number; active: number }>();
39
+
40
+ for (const session of sessions) {
41
+ if (session.status === "archived") continue;
42
+ const current = counts.get(session.projectId) ?? { total: 0, active: 0 };
43
+ current.total += 1;
44
+ if (getAttentionLevel(session) !== "done") {
45
+ current.active += 1;
46
+ }
47
+ counts.set(session.projectId, current);
48
+ }
49
+
50
+ return counts;
51
+ }, [sessions]);
52
+
53
+ const totalActiveSessions = useMemo(() => {
54
+ return sessions.filter((session) => session.status !== "archived" && getAttentionLevel(session) !== "done").length;
55
+ }, [sessions]);
56
+
37
57
  return (
38
58
  <div className="flex h-full min-h-0 w-full flex-col bg-[var(--vk-bg-panel)]">
39
- <section className="flex h-[57px] items-center border-b border-[var(--vk-border)] px-4">
40
- <p className="truncate text-[15px] font-medium leading-[21px] text-[var(--vk-text-strong)]">
41
- {orgLabel}
42
- </p>
59
+ <section className="border-b border-[var(--vk-border)] px-4 py-4">
60
+ <div className="flex items-start gap-3">
61
+ <span className="inline-flex h-10 w-10 items-center justify-center rounded-[10px] border border-[var(--vk-border)] bg-[var(--vk-bg-main)] text-[var(--vk-text-normal)]">
62
+ <Layers3 className="h-4 w-4" />
63
+ </span>
64
+ <div className="min-w-0 flex-1">
65
+ <p className="truncate text-[15px] font-medium leading-[21px] text-[var(--vk-text-strong)]">
66
+ {orgLabel}
67
+ </p>
68
+ <p className="mt-1 text-[12px] text-[var(--vk-text-muted)]">
69
+ {projects.length} projects, {totalActiveSessions} active sessions
70
+ </p>
71
+ </div>
72
+ </div>
73
+
43
74
  <button
44
75
  type="button"
45
76
  onClick={onCreateWorkspace}
46
- className="ml-auto inline-flex h-7 items-center gap-1 rounded-[4px] border border-[var(--vk-border)] px-2 text-[12px] text-[var(--vk-text-normal)] hover:bg-[var(--vk-bg-hover)]"
77
+ className="mt-3 inline-flex h-8 w-full items-center justify-center gap-1 rounded-[6px] border border-[var(--vk-border)] px-2 text-[12px] text-[var(--vk-text-normal)] hover:bg-[var(--vk-bg-hover)]"
47
78
  aria-label="Add workspace"
48
79
  >
49
80
  <Plus className="h-3.5 w-3.5" />
@@ -56,8 +87,24 @@ export const WorkspaceSidebarPanel = memo(function WorkspaceSidebarPanel({
56
87
  <p className="text-[11px] uppercase tracking-[0.08em] text-[var(--vk-text-muted)]">Projects</p>
57
88
  </div>
58
89
  <div className="max-h-[260px] overflow-y-auto px-2">
90
+ <button
91
+ type="button"
92
+ onClick={() => onSelectProject(null)}
93
+ className={cn(
94
+ "mb-1.5 flex w-full items-center gap-3 rounded-[6px] px-3 py-2.5 text-left text-[14px] leading-[21px]",
95
+ selectedProjectId === null
96
+ ? "bg-[var(--vk-bg-hover)] text-[var(--vk-text-normal)]"
97
+ : "text-[var(--vk-text-muted)] hover:bg-[var(--vk-bg-hover)]",
98
+ )}
99
+ >
100
+ <span className="h-2.5 w-2.5 rounded-full bg-[var(--vk-text-muted)]" />
101
+ <span className="truncate">All projects</span>
102
+ <span className="ml-auto text-[11px] text-[var(--vk-text-muted)]">{sessions.length}</span>
103
+ </button>
104
+
59
105
  {projects.map((project) => {
60
106
  const selected = selectedProjectId === project.id;
107
+ const counts = sessionCountByProject.get(project.id) ?? { total: 0, active: 0 };
61
108
  return (
62
109
  <button
63
110
  key={project.id}
@@ -70,8 +117,19 @@ export const WorkspaceSidebarPanel = memo(function WorkspaceSidebarPanel({
70
117
  : "text-[var(--vk-text-muted)] hover:bg-[var(--vk-bg-hover)]",
71
118
  )}
72
119
  >
73
- <span className="h-2.5 w-2.5 rounded-full bg-[#3c83f6]" />
74
- <span className="truncate">{project.id}</span>
120
+ <span className="mt-1 h-2.5 w-2.5 shrink-0 rounded-full bg-[#3c83f6]" />
121
+ <span className="min-w-0 flex-1">
122
+ <span className="block truncate">{project.id}</span>
123
+ {project.description ? (
124
+ <span className="block truncate text-[11px] text-[var(--vk-text-muted)]">
125
+ {project.description}
126
+ </span>
127
+ ) : null}
128
+ </span>
129
+ <span className="shrink-0 text-right">
130
+ <span className="block text-[11px] text-[var(--vk-text-normal)]">{counts.active} active</span>
131
+ <span className="block text-[10px] text-[var(--vk-text-muted)]">{counts.total} total</span>
132
+ </span>
75
133
  </button>
76
134
  );
77
135
  })}