@codemation/next-host 0.0.1

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 (206) hide show
  1. package/README.md +25 -0
  2. package/app/(shell)/credentials/page.tsx +5 -0
  3. package/app/(shell)/dashboard/page.tsx +14 -0
  4. package/app/(shell)/layout.tsx +11 -0
  5. package/app/(shell)/page.tsx +5 -0
  6. package/app/(shell)/users/page.tsx +5 -0
  7. package/app/(shell)/workflows/[workflowId]/page.tsx +19 -0
  8. package/app/(shell)/workflows/page.tsx +5 -0
  9. package/app/api/[[...path]]/route.ts +40 -0
  10. package/app/api/auth/[...nextauth]/route.ts +3 -0
  11. package/app/globals.css +997 -0
  12. package/app/invite/[token]/page.tsx +10 -0
  13. package/app/layout.tsx +65 -0
  14. package/app/login/layout.tsx +25 -0
  15. package/app/login/page.tsx +22 -0
  16. package/components.json +21 -0
  17. package/docs/FORMS.md +46 -0
  18. package/docs/TAILWIND_SHADCN_MIGRATION.md +89 -0
  19. package/eslint.config.mjs +56 -0
  20. package/middleware.ts +29 -0
  21. package/next-env.d.ts +6 -0
  22. package/next.config.ts +34 -0
  23. package/package.json +76 -0
  24. package/postcss.config.mjs +7 -0
  25. package/public/canvas-icons/builtin/openai.svg +5 -0
  26. package/src/api/CodemationApiClient.ts +107 -0
  27. package/src/api/CodemationApiHttpError.ts +17 -0
  28. package/src/auth/CodemationNextAuthConfigResolver.ts +14 -0
  29. package/src/auth/CodemationNextAuthOAuthProviderDescriptorMapper.ts +30 -0
  30. package/src/auth/CodemationNextAuthOAuthProviderSnapshotResolver.ts +17 -0
  31. package/src/auth/CodemationNextAuthProviderCatalog.ts +107 -0
  32. package/src/auth/codemationEdgeAuth.ts +25 -0
  33. package/src/auth/codemationNextAuth.ts +32 -0
  34. package/src/components/Codemation.tsx +6 -0
  35. package/src/components/CodemationDataTable.tsx +37 -0
  36. package/src/components/CodemationDialog.tsx +137 -0
  37. package/src/components/CodemationFormattedDateTime.tsx +46 -0
  38. package/src/components/GoogleColorGIcon.tsx +39 -0
  39. package/src/components/OauthProviderIcon.tsx +33 -0
  40. package/src/components/PasswordStrengthMeter.tsx +59 -0
  41. package/src/components/forms/index.ts +28 -0
  42. package/src/components/json/JsonMonacoEditor.tsx +75 -0
  43. package/src/components/oauthProviderIconData.ts +17 -0
  44. package/src/components/ui/alert.tsx +56 -0
  45. package/src/components/ui/badge.tsx +40 -0
  46. package/src/components/ui/button.tsx +64 -0
  47. package/src/components/ui/card.tsx +70 -0
  48. package/src/components/ui/collapsible.tsx +26 -0
  49. package/src/components/ui/dialog.tsx +137 -0
  50. package/src/components/ui/dropdown-menu.tsx +238 -0
  51. package/src/components/ui/form.tsx +147 -0
  52. package/src/components/ui/input.tsx +19 -0
  53. package/src/components/ui/label.tsx +26 -0
  54. package/src/components/ui/scroll-area.tsx +47 -0
  55. package/src/components/ui/select.tsx +169 -0
  56. package/src/components/ui/separator.tsx +28 -0
  57. package/src/components/ui/switch.tsx +28 -0
  58. package/src/components/ui/table.tsx +72 -0
  59. package/src/components/ui/tabs.tsx +76 -0
  60. package/src/components/ui/textarea.tsx +18 -0
  61. package/src/components/ui/toggle.tsx +41 -0
  62. package/src/features/credentials/components/CredentialConfirmDialog.tsx +58 -0
  63. package/src/features/credentials/components/CredentialDialog.tsx +252 -0
  64. package/src/features/credentials/components/CredentialDialogFeedback.tsx +36 -0
  65. package/src/features/credentials/components/CredentialDialogFieldRows.tsx +257 -0
  66. package/src/features/credentials/components/CredentialDialogFormSections.tsx +230 -0
  67. package/src/features/credentials/components/CredentialEnvFieldStatusRow.tsx +64 -0
  68. package/src/features/credentials/components/CredentialFieldCopyButton.tsx +48 -0
  69. package/src/features/credentials/components/CredentialsScreenHealthBadge.tsx +21 -0
  70. package/src/features/credentials/components/CredentialsScreenInstancesTable.tsx +108 -0
  71. package/src/features/credentials/components/CredentialsScreenTestFailureAlert.tsx +33 -0
  72. package/src/features/credentials/hooks/useCredentialCreateDialog.ts +33 -0
  73. package/src/features/credentials/hooks/useCredentialDialogSession.ts +616 -0
  74. package/src/features/credentials/hooks/useCredentialsScreen.ts +213 -0
  75. package/src/features/credentials/lib/credentialFieldHelpers.ts +35 -0
  76. package/src/features/credentials/lib/credentialFormTypes.ts +1 -0
  77. package/src/features/credentials/lib/credentialInstanceTestPayloadParser.ts +10 -0
  78. package/src/features/credentials/screens/CredentialsScreen.tsx +187 -0
  79. package/src/features/invite/screens/InviteAcceptScreen.tsx +190 -0
  80. package/src/features/users/components/UsersInviteDialog.tsx +121 -0
  81. package/src/features/users/components/UsersRegenerateDialog.tsx +81 -0
  82. package/src/features/users/components/UsersScreenUserStatusBadge.tsx +19 -0
  83. package/src/features/users/schemas/usersInviteFormSchema.ts +7 -0
  84. package/src/features/users/screens/UsersScreen.tsx +240 -0
  85. package/src/features/workflows/components/WorkflowListFolderSection.tsx +91 -0
  86. package/src/features/workflows/components/WorkflowListItemCard.tsx +67 -0
  87. package/src/features/workflows/components/WorkflowListRoot.tsx +39 -0
  88. package/src/features/workflows/components/WorkflowsListTree.tsx +28 -0
  89. package/src/features/workflows/components/canvas/CanvasNodeChromeTooltip.tsx +96 -0
  90. package/src/features/workflows/components/canvas/CanvasNodeIconSlot.tsx +25 -0
  91. package/src/features/workflows/components/canvas/VisibleNodeStatusResolver.tsx +84 -0
  92. package/src/features/workflows/components/canvas/WorkflowCanvas.tsx +248 -0
  93. package/src/features/workflows/components/canvas/WorkflowCanvasCodemationNode.tsx +182 -0
  94. package/src/features/workflows/components/canvas/WorkflowCanvasCodemationNodeAccents.tsx +73 -0
  95. package/src/features/workflows/components/canvas/WorkflowCanvasCodemationNodeAgentBottomSourceHandles.tsx +43 -0
  96. package/src/features/workflows/components/canvas/WorkflowCanvasCodemationNodeAgentLabels.tsx +47 -0
  97. package/src/features/workflows/components/canvas/WorkflowCanvasCodemationNodeCard.tsx +202 -0
  98. package/src/features/workflows/components/canvas/WorkflowCanvasCodemationNodeHandles.tsx +77 -0
  99. package/src/features/workflows/components/canvas/WorkflowCanvasCodemationNodeLabelBelow.tsx +51 -0
  100. package/src/features/workflows/components/canvas/WorkflowCanvasCodemationNodeMainGlyph.tsx +64 -0
  101. package/src/features/workflows/components/canvas/WorkflowCanvasCodemationNodeToolbar.tsx +95 -0
  102. package/src/features/workflows/components/canvas/WorkflowCanvasLoadingPlaceholder.tsx +69 -0
  103. package/src/features/workflows/components/canvas/WorkflowCanvasNodeIcon.tsx +102 -0
  104. package/src/features/workflows/components/canvas/WorkflowCanvasSimpleIconGlyph.tsx +21 -0
  105. package/src/features/workflows/components/canvas/WorkflowCanvasStraightCountEdge.tsx +33 -0
  106. package/src/features/workflows/components/canvas/WorkflowCanvasStructureSignature.tsx +7 -0
  107. package/src/features/workflows/components/canvas/WorkflowCanvasSymmetricForkEdge.tsx +32 -0
  108. package/src/features/workflows/components/canvas/WorkflowCanvasToolbarIconButton.tsx +95 -0
  109. package/src/features/workflows/components/canvas/lib/WorkflowCanvasBuiltinIconRegistry.ts +26 -0
  110. package/src/features/workflows/components/canvas/lib/WorkflowCanvasEdgeCountResolver.ts +51 -0
  111. package/src/features/workflows/components/canvas/lib/WorkflowCanvasEdgeStyleResolver.ts +35 -0
  112. package/src/features/workflows/components/canvas/lib/WorkflowCanvasLabelLayoutEstimator.ts +42 -0
  113. package/src/features/workflows/components/canvas/lib/WorkflowCanvasOverlapResolver.ts +78 -0
  114. package/src/features/workflows/components/canvas/lib/WorkflowCanvasPortOrderResolver.ts +25 -0
  115. package/src/features/workflows/components/canvas/lib/WorkflowCanvasRoundedOrthogonalPathPlanner.ts +56 -0
  116. package/src/features/workflows/components/canvas/lib/WorkflowCanvasSiIconRegistry.ts +18 -0
  117. package/src/features/workflows/components/canvas/lib/WorkflowCanvasSymmetricForkPathPlanner.ts +43 -0
  118. package/src/features/workflows/components/canvas/lib/layoutWorkflow.ts +315 -0
  119. package/src/features/workflows/components/canvas/lib/workflowCanvasEdgeGeometry.ts +3 -0
  120. package/src/features/workflows/components/canvas/lib/workflowCanvasEmbeddedStyles.ts +62 -0
  121. package/src/features/workflows/components/canvas/lib/workflowCanvasFlowTypes.ts +10 -0
  122. package/src/features/workflows/components/canvas/lib/workflowCanvasNodeData.ts +41 -0
  123. package/src/features/workflows/components/canvas/lib/workflowCanvasNodeGeometry.ts +99 -0
  124. package/src/features/workflows/components/canvas/workflowCanvasNodeChrome.tsx +46 -0
  125. package/src/features/workflows/components/realtime/RealtimeContext.tsx +14 -0
  126. package/src/features/workflows/components/realtime/WorkflowRealtimeProvider.tsx +15 -0
  127. package/src/features/workflows/components/workflowDetail/NodeCredentialBindingRow.tsx +209 -0
  128. package/src/features/workflows/components/workflowDetail/NodeCredentialBindingsSection.tsx +227 -0
  129. package/src/features/workflows/components/workflowDetail/NodePropertiesConfigSection.tsx +51 -0
  130. package/src/features/workflows/components/workflowDetail/NodePropertiesPanelHeader.tsx +50 -0
  131. package/src/features/workflows/components/workflowDetail/NodePropertiesSlidePanel.tsx +134 -0
  132. package/src/features/workflows/components/workflowDetail/WorkflowActivationErrorDialog.tsx +71 -0
  133. package/src/features/workflows/components/workflowDetail/WorkflowActivationHeaderControl.tsx +64 -0
  134. package/src/features/workflows/components/workflowDetail/WorkflowDetailIcons.tsx +52 -0
  135. package/src/features/workflows/components/workflowDetail/WorkflowExecutionInspector.tsx +110 -0
  136. package/src/features/workflows/components/workflowDetail/WorkflowExecutionInspectorDetailBody.tsx +213 -0
  137. package/src/features/workflows/components/workflowDetail/WorkflowExecutionInspectorPanes.tsx +239 -0
  138. package/src/features/workflows/components/workflowDetail/WorkflowExecutionInspectorSidebarResizer.tsx +31 -0
  139. package/src/features/workflows/components/workflowDetail/WorkflowExecutionInspectorTreePanel.tsx +133 -0
  140. package/src/features/workflows/components/workflowDetail/WorkflowInspectorAttachmentGroupingPresenter.tsx +31 -0
  141. package/src/features/workflows/components/workflowDetail/WorkflowInspectorAttachmentList.tsx +118 -0
  142. package/src/features/workflows/components/workflowDetail/WorkflowInspectorBinaryView.tsx +15 -0
  143. package/src/features/workflows/components/workflowDetail/WorkflowInspectorErrorView.tsx +107 -0
  144. package/src/features/workflows/components/workflowDetail/WorkflowInspectorJsonView.tsx +114 -0
  145. package/src/features/workflows/components/workflowDetail/WorkflowInspectorPrettyTreePresenter.tsx +132 -0
  146. package/src/features/workflows/components/workflowDetail/WorkflowInspectorPrettyTreeViewRenderer.tsx +147 -0
  147. package/src/features/workflows/components/workflowDetail/WorkflowInspectorPrettyView.tsx +65 -0
  148. package/src/features/workflows/components/workflowDetail/WorkflowInspectorViews.tsx +5 -0
  149. package/src/features/workflows/components/workflowDetail/WorkflowJsonEditorBinaryAttachmentRow.tsx +74 -0
  150. package/src/features/workflows/components/workflowDetail/WorkflowJsonEditorBinaryUploadRow.tsx +69 -0
  151. package/src/features/workflows/components/workflowDetail/WorkflowJsonEditorDialog.tsx +254 -0
  152. package/src/features/workflows/components/workflowDetail/WorkflowRunsList.tsx +89 -0
  153. package/src/features/workflows/components/workflowDetail/WorkflowRunsSidebar.tsx +50 -0
  154. package/src/features/workflows/hooks/canvas/useWorkflowCanvasVisibleNodeStatuses.ts +14 -0
  155. package/src/features/workflows/hooks/realtime/realtime.tsx +271 -0
  156. package/src/features/workflows/hooks/realtime/runQueryPolling.ts +34 -0
  157. package/src/features/workflows/hooks/realtime/useWorkflowRealtimeInfrastructure.ts +541 -0
  158. package/src/features/workflows/hooks/realtime/useWorkflowRealtimeShowDisconnectedBadge.ts +9 -0
  159. package/src/features/workflows/hooks/workflowDetail/useWorkflowDetailController.tsx +1300 -0
  160. package/src/features/workflows/lib/realtime/realtimeApi.ts +78 -0
  161. package/src/features/workflows/lib/realtime/realtimeClientBridge.ts +52 -0
  162. package/src/features/workflows/lib/realtime/realtimeDomainTypes.ts +191 -0
  163. package/src/features/workflows/lib/realtime/realtimeQueryKeys.ts +15 -0
  164. package/src/features/workflows/lib/realtime/realtimeRunMutations.ts +167 -0
  165. package/src/features/workflows/lib/realtime/workflowTypes.ts +5 -0
  166. package/src/features/workflows/lib/workflowDetail/PersistedWorkflowSnapshotMapper.ts +205 -0
  167. package/src/features/workflows/lib/workflowDetail/WorkflowActivationHttpErrorFormat.ts +32 -0
  168. package/src/features/workflows/lib/workflowDetail/WorkflowDetailPresenter.ts +1017 -0
  169. package/src/features/workflows/lib/workflowDetail/WorkflowDetailUrlCodec.ts +70 -0
  170. package/src/features/workflows/lib/workflowDetail/workflowDetailTypes.ts +152 -0
  171. package/src/features/workflows/lib/workflowDetailTreeStyles.ts +65 -0
  172. package/src/features/workflows/screens/WorkflowDetailScreen.tsx +236 -0
  173. package/src/features/workflows/screens/WorkflowDetailScreenInspectorPanel.tsx +55 -0
  174. package/src/features/workflows/screens/WorkflowsList.tsx +35 -0
  175. package/src/features/workflows/screens/WorkflowsScreen.tsx +31 -0
  176. package/src/index.ts +1 -0
  177. package/src/lib/utils.ts +6 -0
  178. package/src/middleware/CodemationNextHostMiddlewarePathRules.ts +31 -0
  179. package/src/providers/CodemationSessionProvider.tsx +23 -0
  180. package/src/providers/Providers.tsx +36 -0
  181. package/src/providers/RealtimeBoundary.tsx +17 -0
  182. package/src/providers/WhitelabelProvider.tsx +22 -0
  183. package/src/server/CodemationAuthPrismaClient.ts +21 -0
  184. package/src/server/CodemationNextHost.ts +379 -0
  185. package/src/shell/AppLayout.tsx +141 -0
  186. package/src/shell/AppLayoutNavItems.tsx +129 -0
  187. package/src/shell/AppLayoutPageHeader.tsx +79 -0
  188. package/src/shell/AppLayoutSidebarBrand.tsx +33 -0
  189. package/src/shell/AppMainContent.tsx +17 -0
  190. package/src/shell/AppShellHeaderActions.tsx +12 -0
  191. package/src/shell/AppShellHeaderActionsAuthenticated.tsx +51 -0
  192. package/src/shell/CodemationNextClientShell.tsx +17 -0
  193. package/src/shell/CredentialsSignInRedirectResolver.ts +21 -0
  194. package/src/shell/LoginPageClient.tsx +231 -0
  195. package/src/shell/WorkflowDetailChromeContext.tsx +42 -0
  196. package/src/shell/WorkflowFolderTreeBuilder.ts +62 -0
  197. package/src/shell/WorkflowFolderUi.ts +42 -0
  198. package/src/shell/WorkflowSidebarNavFolder.tsx +112 -0
  199. package/src/shell/WorkflowSidebarNavTree.tsx +68 -0
  200. package/src/shell/appLayoutPageTitle.ts +16 -0
  201. package/src/shell/appLayoutSidebarIcons.tsx +108 -0
  202. package/src/whitelabel/CodemationWhitelabelSnapshot.ts +4 -0
  203. package/src/whitelabel/CodemationWhitelabelSnapshotFactory.ts +18 -0
  204. package/tsconfig.json +40 -0
  205. package/tsconfig.tsbuildinfo +1 -0
  206. package/vitest.config.ts +34 -0
@@ -0,0 +1,62 @@
1
+ import type { WorkflowSummary } from "../features/workflows/hooks/realtime/realtime";
2
+
3
+ export type WorkflowFolderTreeNode = Readonly<{
4
+ segment: string;
5
+ children: ReadonlyArray<WorkflowFolderTreeNode>;
6
+ workflows: ReadonlyArray<WorkflowSummary>;
7
+ }>;
8
+
9
+ type MutableNode = {
10
+ segment: string;
11
+ childMap: Map<string, MutableNode>;
12
+ workflows: WorkflowSummary[];
13
+ };
14
+
15
+ export class WorkflowFolderTreeBuilder {
16
+ build(workflows: ReadonlyArray<WorkflowSummary>): WorkflowFolderTreeNode {
17
+ const root: MutableNode = { segment: "", childMap: new Map(), workflows: [] };
18
+ const sorted = [...workflows].sort((left, right) => {
19
+ const leftKey = (left.discoveryPathSegments ?? []).join("/");
20
+ const rightKey = (right.discoveryPathSegments ?? []).join("/");
21
+ const keyCompare = leftKey.localeCompare(rightKey);
22
+ if (keyCompare !== 0) {
23
+ return keyCompare;
24
+ }
25
+ return left.name.localeCompare(right.name);
26
+ });
27
+ for (const workflow of sorted) {
28
+ this.insertWorkflow(root, workflow);
29
+ }
30
+ return this.freezeNode(root);
31
+ }
32
+
33
+ private insertWorkflow(root: MutableNode, workflow: WorkflowSummary): void {
34
+ const segments = workflow.discoveryPathSegments ?? [];
35
+ if (segments.length === 0) {
36
+ root.workflows.push(workflow);
37
+ return;
38
+ }
39
+ const folderParts = segments.length > 1 ? segments.slice(0, -1) : [];
40
+ let node = root;
41
+ for (const part of folderParts) {
42
+ let next = node.childMap.get(part);
43
+ if (!next) {
44
+ next = { segment: part, childMap: new Map(), workflows: [] };
45
+ node.childMap.set(part, next);
46
+ }
47
+ node = next;
48
+ }
49
+ node.workflows.push(workflow);
50
+ }
51
+
52
+ private freezeNode(node: MutableNode): WorkflowFolderTreeNode {
53
+ const children = [...node.childMap.values()]
54
+ .sort((left, right) => left.segment.localeCompare(right.segment))
55
+ .map((child) => this.freezeNode(child));
56
+ return {
57
+ segment: node.segment,
58
+ children,
59
+ workflows: [...node.workflows],
60
+ };
61
+ }
62
+ }
@@ -0,0 +1,42 @@
1
+ import type { WorkflowSummary } from "../features/workflows/hooks/realtime/realtime";
2
+
3
+ import type { WorkflowFolderTreeNode } from "./WorkflowFolderTreeBuilder";
4
+
5
+ export class WorkflowFolderUi {
6
+ /** Expand folders that lie on the path to the workflow open in the shell. */
7
+ static computeDefaultFolderOpen(
8
+ folderPath: readonly string[],
9
+ pathname: string,
10
+ workflows: ReadonlyArray<WorkflowSummary>,
11
+ ): boolean {
12
+ if (folderPath.length === 0) {
13
+ return true;
14
+ }
15
+ const match = pathname.match(/^\/workflows\/([^/]+)/);
16
+ if (!match) {
17
+ return true;
18
+ }
19
+ const active = workflows.find((w) => w.id === decodeURIComponent(match[1]!));
20
+ if (!active) {
21
+ return true;
22
+ }
23
+ const segs = active.discoveryPathSegments;
24
+ if (segs.length < folderPath.length) {
25
+ return false;
26
+ }
27
+ for (let i = 0; i < folderPath.length; i += 1) {
28
+ if (segs[i] !== folderPath[i]) {
29
+ return false;
30
+ }
31
+ }
32
+ return true;
33
+ }
34
+
35
+ static countWorkflowsInSubtree(node: WorkflowFolderTreeNode): number {
36
+ let total = node.workflows.length;
37
+ for (const child of node.children) {
38
+ total += WorkflowFolderUi.countWorkflowsInSubtree(child);
39
+ }
40
+ return total;
41
+ }
42
+ }
@@ -0,0 +1,112 @@
1
+ "use client";
2
+
3
+ import Link from "next/link";
4
+
5
+ import { ChevronRight, Folder } from "lucide-react";
6
+ import type { ReactNode } from "react";
7
+
8
+ import type { WorkflowSummary } from "../features/workflows/hooks/realtime/realtime";
9
+
10
+ import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
11
+ import { cn } from "@/lib/utils";
12
+
13
+ import type { WorkflowFolderTreeNode } from "./WorkflowFolderTreeBuilder";
14
+ import { WorkflowFolderUi } from "./WorkflowFolderUi";
15
+
16
+ import { IconWorkflow } from "./appLayoutSidebarIcons";
17
+
18
+ function folderTestId(path: ReadonlyArray<string>): string {
19
+ return `nav-workflow-folder-${path.join("__")}`;
20
+ }
21
+
22
+ export function WorkflowSidebarNavFolder(
23
+ args: Readonly<{
24
+ node: WorkflowFolderTreeNode;
25
+ pathPrefix: ReadonlyArray<string>;
26
+ pathname: string;
27
+ workflows: ReadonlyArray<WorkflowSummary>;
28
+ workflowLinkClass: (isActive: boolean) => string;
29
+ depth: number;
30
+ collapsed?: boolean;
31
+ }>,
32
+ ): ReactNode {
33
+ const { node, pathPrefix, pathname, workflows, workflowLinkClass, depth } = args;
34
+ const collapsed = args.collapsed === true;
35
+ const folderPath = [...pathPrefix, node.segment];
36
+ const defaultOpen = WorkflowFolderUi.computeDefaultFolderOpen(folderPath, pathname, workflows);
37
+ const totalInTree = WorkflowFolderUi.countWorkflowsInSubtree(node);
38
+
39
+ return (
40
+ <Collapsible
41
+ defaultOpen={defaultOpen}
42
+ className={cn("rounded-lg", !collapsed && depth > 0 && "ml-0.5 border-l border-sidebar-border/60 pl-2")}
43
+ >
44
+ <CollapsibleTrigger
45
+ type="button"
46
+ data-testid={folderTestId(folderPath)}
47
+ aria-label={collapsed ? folderPath.join(" / ") : undefined}
48
+ className={cn(
49
+ "group/trigger flex w-full min-w-0 items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm outline-none transition-colors",
50
+ collapsed && "justify-center px-1 py-2",
51
+ "text-sidebar-foreground/90 hover:bg-sidebar-accent/70",
52
+ "focus-visible:ring-2 focus-visible:ring-sidebar-ring/40",
53
+ "[&[data-state=open]>svg:first-child]:rotate-90",
54
+ )}
55
+ >
56
+ <ChevronRight
57
+ className="size-3.5 shrink-0 text-muted-foreground transition-transform duration-200 ease-out"
58
+ aria-hidden
59
+ />
60
+ <Folder className="size-3.5 shrink-0 text-primary/70" aria-hidden />
61
+ {!collapsed && (
62
+ <>
63
+ <span className="min-w-0 flex-1 truncate font-medium">{node.segment}</span>
64
+ <span className="shrink-0 rounded-md bg-sidebar-accent/80 px-1.5 py-0.5 text-[0.65rem] font-semibold tabular-nums text-muted-foreground">
65
+ {totalInTree}
66
+ </span>
67
+ </>
68
+ )}
69
+ </CollapsibleTrigger>
70
+ <CollapsibleContent>
71
+ <div className={cn("space-y-0.5 pb-1 pt-0.5", collapsed ? "pl-0" : "pl-1")}>
72
+ {node.workflows.map((w) => {
73
+ const href = `/workflows/${encodeURIComponent(w.id)}`;
74
+ const isActive = pathname === href;
75
+ return (
76
+ <Link
77
+ key={w.id}
78
+ href={href}
79
+ className={workflowLinkClass(isActive)}
80
+ data-testid={`nav-workflow-${w.id}`}
81
+ aria-label={
82
+ collapsed
83
+ ? w.discoveryPathSegments.length > 0
84
+ ? w.discoveryPathSegments.join(" / ")
85
+ : w.name
86
+ : undefined
87
+ }
88
+ >
89
+ <span className="flex shrink-0 opacity-70" aria-hidden>
90
+ <IconWorkflow />
91
+ </span>
92
+ {!collapsed && <span className="truncate text-sm">{w.name}</span>}
93
+ </Link>
94
+ );
95
+ })}
96
+ {node.children.map((child) => (
97
+ <WorkflowSidebarNavFolder
98
+ key={child.segment}
99
+ node={child}
100
+ pathPrefix={folderPath}
101
+ pathname={pathname}
102
+ workflows={workflows}
103
+ workflowLinkClass={workflowLinkClass}
104
+ depth={depth + 1}
105
+ collapsed={collapsed}
106
+ />
107
+ ))}
108
+ </div>
109
+ </CollapsibleContent>
110
+ </Collapsible>
111
+ );
112
+ }
@@ -0,0 +1,68 @@
1
+ "use client";
2
+
3
+ import Link from "next/link";
4
+
5
+ import type { ReactNode } from "react";
6
+
7
+ import type { WorkflowSummary } from "../features/workflows/hooks/realtime/realtime";
8
+
9
+ import { WorkflowFolderTreeBuilder } from "./WorkflowFolderTreeBuilder";
10
+
11
+ import { WorkflowSidebarNavFolder } from "./WorkflowSidebarNavFolder";
12
+
13
+ import { IconWorkflow } from "./appLayoutSidebarIcons";
14
+
15
+ const treeBuilder = new WorkflowFolderTreeBuilder();
16
+
17
+ export function WorkflowSidebarNavTree(
18
+ args: Readonly<{
19
+ workflows: ReadonlyArray<WorkflowSummary>;
20
+ pathname: string;
21
+ workflowLinkClass: (isActive: boolean) => string;
22
+ /** Icon-only / compact layout (collapsed shell sidebar). */
23
+ collapsed?: boolean;
24
+ }>,
25
+ ): ReactNode {
26
+ const tree = treeBuilder.build(args.workflows);
27
+ const collapsed = args.collapsed === true;
28
+ return (
29
+ <div className="space-y-0.5" data-testid="workflow-sidebar-nav-tree">
30
+ {tree.workflows.map((w) => {
31
+ const href = `/workflows/${encodeURIComponent(w.id)}`;
32
+ const isActive = args.pathname === href;
33
+ return (
34
+ <Link
35
+ key={w.id}
36
+ href={href}
37
+ className={args.workflowLinkClass(isActive)}
38
+ data-testid={`nav-workflow-${w.id}`}
39
+ aria-label={
40
+ collapsed
41
+ ? w.discoveryPathSegments.length > 0
42
+ ? w.discoveryPathSegments.join(" / ")
43
+ : w.name
44
+ : undefined
45
+ }
46
+ >
47
+ <span className="flex shrink-0 opacity-70" aria-hidden>
48
+ <IconWorkflow />
49
+ </span>
50
+ {!collapsed && <span className="truncate text-sm">{w.name}</span>}
51
+ </Link>
52
+ );
53
+ })}
54
+ {tree.children.map((child) => (
55
+ <WorkflowSidebarNavFolder
56
+ key={child.segment}
57
+ node={child}
58
+ pathPrefix={[]}
59
+ pathname={args.pathname}
60
+ workflows={args.workflows}
61
+ workflowLinkClass={args.workflowLinkClass}
62
+ depth={0}
63
+ collapsed={collapsed}
64
+ />
65
+ ))}
66
+ </div>
67
+ );
68
+ }
@@ -0,0 +1,16 @@
1
+ export function getPageTitle(
2
+ pathname: string,
3
+ workflows: ReadonlyArray<{ id: string; name: string }>,
4
+ shellDefaultTitle: string,
5
+ ): string {
6
+ if (pathname === "/dashboard") return "Dashboard";
7
+ if (pathname === "/credentials") return "Credentials";
8
+ if (pathname === "/users") return "Users";
9
+ if (pathname === "/workflows") return "Workflows";
10
+ const workflowMatch = pathname.match(/^\/workflows\/([^/]+)/);
11
+ if (workflowMatch) {
12
+ const w = workflows.find((x) => x.id === decodeURIComponent(workflowMatch[1]));
13
+ return w?.name ?? "Workflow";
14
+ }
15
+ return shellDefaultTitle;
16
+ }
@@ -0,0 +1,108 @@
1
+ export const IconDashboard = () => (
2
+ <svg
3
+ width="20"
4
+ height="20"
5
+ viewBox="0 0 24 24"
6
+ fill="none"
7
+ stroke="currentColor"
8
+ strokeWidth="2"
9
+ strokeLinecap="round"
10
+ strokeLinejoin="round"
11
+ aria-hidden
12
+ >
13
+ <rect width="7" height="9" x="3" y="3" rx="1" />
14
+ <rect width="7" height="5" x="14" y="3" rx="1" />
15
+ <rect width="7" height="9" x="14" y="12" rx="1" />
16
+ <rect width="7" height="5" x="3" y="16" rx="1" />
17
+ </svg>
18
+ );
19
+
20
+ export const IconCredentials = () => (
21
+ <svg
22
+ width="20"
23
+ height="20"
24
+ viewBox="0 0 24 24"
25
+ fill="none"
26
+ stroke="currentColor"
27
+ strokeWidth="2"
28
+ strokeLinecap="round"
29
+ strokeLinejoin="round"
30
+ aria-hidden
31
+ >
32
+ <circle cx="8" cy="15" r="4" />
33
+ <path d="M10.85 12.15L19 4" />
34
+ <path d="m18 5 2 2" />
35
+ <path d="m15 8 2 2" />
36
+ </svg>
37
+ );
38
+
39
+ export const IconChevronLeft = () => (
40
+ <svg
41
+ width="18"
42
+ height="18"
43
+ viewBox="0 0 24 24"
44
+ fill="none"
45
+ stroke="currentColor"
46
+ strokeWidth="2"
47
+ strokeLinecap="round"
48
+ strokeLinejoin="round"
49
+ aria-hidden
50
+ >
51
+ <path d="m15 18-6-6 6-6" />
52
+ </svg>
53
+ );
54
+
55
+ export const IconChevronRight = () => (
56
+ <svg
57
+ width="18"
58
+ height="18"
59
+ viewBox="0 0 24 24"
60
+ fill="none"
61
+ stroke="currentColor"
62
+ strokeWidth="2"
63
+ strokeLinecap="round"
64
+ strokeLinejoin="round"
65
+ aria-hidden
66
+ >
67
+ <path d="m9 18 6-6-6-6" />
68
+ </svg>
69
+ );
70
+
71
+ export const IconWorkflow = () => (
72
+ <svg
73
+ width="16"
74
+ height="16"
75
+ viewBox="0 0 24 24"
76
+ fill="none"
77
+ stroke="currentColor"
78
+ strokeWidth="2"
79
+ strokeLinecap="round"
80
+ strokeLinejoin="round"
81
+ aria-hidden
82
+ >
83
+ <rect width="8" height="8" x="3" y="3" rx="1" />
84
+ <path d="M7 11v4a2 2 0 0 0 2 2h4" />
85
+ <path d="M15 7h4a2 2 0 0 1 2 2v4" />
86
+ <path d="M3 11h4" />
87
+ <path d="M11 3h4" />
88
+ </svg>
89
+ );
90
+
91
+ export const IconUsers = () => (
92
+ <svg
93
+ width="20"
94
+ height="20"
95
+ viewBox="0 0 24 24"
96
+ fill="none"
97
+ stroke="currentColor"
98
+ strokeWidth="2"
99
+ strokeLinecap="round"
100
+ strokeLinejoin="round"
101
+ aria-hidden
102
+ >
103
+ <path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" />
104
+ <circle cx="9" cy="7" r="4" />
105
+ <path d="M22 21v-2a4 4 0 0 0-3-3.87" />
106
+ <path d="M16 3.13a4 4 0 0 1 0 7.75" />
107
+ </svg>
108
+ );
@@ -0,0 +1,4 @@
1
+ export type CodemationWhitelabelSnapshot = Readonly<{
2
+ productName: string;
3
+ logoUrl: string | null;
4
+ }>;
@@ -0,0 +1,18 @@
1
+ import type { CodemationConfig } from "@codemation/host";
2
+ import { ApiPaths } from "@codemation/host-src/presentation/http/ApiPaths";
3
+
4
+ import type { CodemationWhitelabelSnapshot } from "./CodemationWhitelabelSnapshot";
5
+
6
+ /**
7
+ * Builds shell branding from the consumer `codemation.config` object (same source as {@link CodemationApplication.useConfig}).
8
+ */
9
+ export class CodemationWhitelabelSnapshotFactory {
10
+ static fromConsumerConfig(config: CodemationConfig): CodemationWhitelabelSnapshot {
11
+ const w = config.whitelabel;
12
+ const rawName = w?.productName?.trim();
13
+ const productName = rawName !== undefined && rawName.length > 0 ? rawName : "Codemation";
14
+ const logoPath = w?.logoPath?.trim();
15
+ const logoUrl = logoPath !== undefined && logoPath.length > 0 ? ApiPaths.whitelabelLogo() : null;
16
+ return { productName, logoUrl };
17
+ }
18
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "types": ["node"],
5
+ "jsx": "preserve",
6
+ "noEmit": true,
7
+ "incremental": true,
8
+ "plugins": [
9
+ {
10
+ "name": "next"
11
+ }
12
+ ],
13
+ "baseUrl": ".",
14
+ "paths": {
15
+ "@/*": ["./src/*"],
16
+ "@codemation/core": ["../../packages/core/src/index.ts"],
17
+ "@codemation/core/bootstrap": ["../../packages/core/src/bootstrap/index.ts"],
18
+ "@codemation/core/testing": ["../../packages/core/src/testing.ts"],
19
+ "@codemation/cli": ["../../packages/cli/src/index.ts"],
20
+ "@codemation/core-nodes": ["../../packages/core-nodes/src/index.ts"],
21
+ "@codemation/core-nodes-gmail": ["../../packages/core-nodes-gmail/src/index.ts"],
22
+ "@codemation/eventbus-redis": ["../../packages/eventbus-redis/src/index.ts"],
23
+ "@codemation/host": ["../../packages/host/src/index.ts"],
24
+ "@codemation/host/client": ["../../packages/host/src/client.ts"],
25
+ "@codemation/host/credentials": ["../../packages/host/src/credentials.ts"],
26
+ "@codemation/host/consumer": ["../../packages/host/src/consumer.ts"],
27
+ "@codemation/host/next/server": ["../../packages/host/src/nextServer.ts"],
28
+ "@codemation/host/server": ["../../packages/host/src/server.ts"],
29
+ "@codemation/host/dev-server-sidecar": ["../../packages/host/src/devServerSidecar.ts"],
30
+ "@codemation/host/persistence": ["../../packages/host/src/persistenceServer.ts"],
31
+ "@codemation/host-src/*": ["../../packages/host/src/*"],
32
+ "@codemation/next-host": ["./src/index.ts"],
33
+ "@codemation/next-host/src/*": ["./src/*"],
34
+ "@codemation/node-example": ["../../packages/node-example/src/index.ts"],
35
+ "@codemation/queue-bullmq": ["../../packages/queue-bullmq/src/index.ts"]
36
+ }
37
+ },
38
+ "include": ["next-env.d.ts", "app/**/*.ts", "app/**/*.tsx", "src/**/*.ts", "src/**/*.tsx", ".next/types/**/*.ts"],
39
+ "exclude": ["node_modules"]
40
+ }