@loopstack/loopstack-studio 0.21.2 → 0.22.0

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 (89) hide show
  1. package/dist/api/auth.js +10 -0
  2. package/dist/api/client.js +13 -0
  3. package/dist/api/config.js +10 -0
  4. package/dist/api/dashboard.js +4 -0
  5. package/dist/api/documents.js +7 -0
  6. package/dist/api/index.js +24 -0
  7. package/dist/api/namespaces.js +7 -0
  8. package/dist/api/pipelines.js +13 -0
  9. package/dist/api/processor.js +4 -0
  10. package/dist/api/workflows.js +8 -0
  11. package/dist/api/workspaces.js +12 -0
  12. package/dist/app/EnvironmentEmbedRoot.js +33 -0
  13. package/dist/components/data-table/DataTableFilters.js +74 -63
  14. package/dist/components/layout/MainLayout.js +7 -4
  15. package/dist/components/page/PageBreadcrumbs.js +1 -1
  16. package/dist/components/ui/input.js +1 -1
  17. package/dist/components/ui/select.js +1 -1
  18. package/dist/components/ui/sidebar.js +359 -359
  19. package/dist/components/ui/textarea.js +1 -1
  20. package/dist/components/ui-widgets/UiActions.js +22 -15
  21. package/dist/components/ui-widgets/UiWidget.js +31 -26
  22. package/dist/components/ui-widgets/widgets/AiPromptInput.js +27 -27
  23. package/dist/components/ui-widgets/widgets/ButtonFullWidth.js +12 -12
  24. package/dist/components/ui-widgets/widgets/SandboxRun.js +32 -0
  25. package/dist/components/ui-widgets/widgets/SubmitButton.js +11 -11
  26. package/dist/features/code-explorer/utils/fileIcons.js +4 -7
  27. package/dist/features/oauth/OAuthPromptRenderer.js +1 -1
  28. package/dist/features/runs/Runs.js +197 -0
  29. package/dist/features/workbench/NavigationItems.js +1 -1
  30. package/dist/features/workbench/Workbench.js +109 -75
  31. package/dist/features/workbench/WorkflowItem.js +8 -11
  32. package/dist/features/workbench/WorkflowList.js +29 -20
  33. package/dist/features/workbench/components/DocumentList.js +20 -20
  34. package/dist/features/workbench/components/NewRunDialog.js +328 -0
  35. package/dist/features/workbench/components/WorkbenchFloatingPanel.js +88 -0
  36. package/dist/features/workbench/components/WorkbenchFlowPanel.js +48 -0
  37. package/dist/features/workbench/components/WorkbenchIconSidebar.js +68 -0
  38. package/dist/features/workbench/components/WorkbenchPreviewPanel.js +128 -0
  39. package/dist/features/workbench/components/WorkflowForms.js +7 -6
  40. package/dist/features/workbench/components/WorkflowHistoryItem.js +68 -63
  41. package/dist/features/workbench/components/buttons/WorkflowButtons.js +79 -61
  42. package/dist/features/workbench/components/document-renderer/DocumentFormRenderer.js +6 -5
  43. package/dist/features/workbench/providers/WorkbenchLayoutProvider.js +77 -0
  44. package/dist/features/workspaces/Workspaces.js +2 -2
  45. package/dist/features/workspaces/components/CreateWorkspace.js +164 -81
  46. package/dist/features/workspaces/components/EnvironmentSlotSelector.js +63 -0
  47. package/dist/features/workspaces/components/ExecutionTimeline.js +69 -68
  48. package/dist/features/workspaces/components/PipelineForm.js +4 -4
  49. package/dist/hooks/index.js +2 -0
  50. package/dist/hooks/useApi.js +9 -33
  51. package/dist/hooks/useAuth.js +18 -38
  52. package/dist/hooks/useConfig.js +31 -33
  53. package/dist/hooks/useDashboard.js +1 -4
  54. package/dist/hooks/useDocuments.js +4 -8
  55. package/dist/hooks/useFiles.js +31 -41
  56. package/dist/hooks/useNamespaces.js +5 -8
  57. package/dist/hooks/usePipelines.js +98 -108
  58. package/dist/hooks/useProcessor.js +1 -4
  59. package/dist/hooks/useWorkflows.js +25 -41
  60. package/dist/hooks/useWorkspaces.js +45 -72
  61. package/dist/index.d.ts +266 -48
  62. package/dist/index.js +10 -2
  63. package/dist/packages/contracts/dist/enums/index.js +25 -0
  64. package/dist/packages/contracts/dist/enums/pipeline-state.js +10 -0
  65. package/dist/packages/contracts/dist/enums/registry.enum.js +20 -0
  66. package/dist/packages/contracts/dist/enums/sort-order.enum.js +10 -0
  67. package/dist/packages/contracts/dist/enums/user-type.enum.js +10 -0
  68. package/dist/packages/contracts/dist/enums/workflow-state.enum.js +10 -0
  69. package/dist/pages/DebugPage.js +12 -14
  70. package/dist/pages/DebugWorkflowDetailsPage.js +1 -1
  71. package/dist/pages/DebugWorkflowsPage.js +3 -4
  72. package/dist/pages/EmbedWorkbenchPage.js +4 -3
  73. package/dist/pages/PipelineDebugPage.js +2 -2
  74. package/dist/pages/PreviewWorkbenchPage.js +416 -0
  75. package/dist/pages/RunsListPage.js +36 -0
  76. package/dist/pages/RunsPage.js +49 -0
  77. package/dist/pages/StudioLandingPage.js +145 -0
  78. package/dist/pages/WorkbenchPage.js +75 -51
  79. package/dist/providers/InvalidationEventsProvider.js +7 -7
  80. package/dist/providers/QueryProvider.js +21 -0
  81. package/dist/routing/LocalRouter.js +9 -0
  82. package/dist/services/createApiClient.js +4 -10
  83. package/dist/services/index.js +1 -1
  84. package/package.json +2 -3
  85. package/dist/features/code-explorer/CodeExplorer.js +0 -69
  86. package/dist/features/code-explorer/components/CodeExplorerTree.js +0 -43
  87. package/dist/features/code-explorer/components/CodeExplorerTreeNode.js +0 -82
  88. package/dist/features/workbench/components/WorkbenchSidebar.js +0 -109
  89. package/dist/features/workbench/providers/WorkbenchContextProvider.js +0 -3
@@ -0,0 +1,145 @@
1
+ import { useStudio } from "../providers/StudioProvider.js";
2
+ import { useFilterPipelines } from "../hooks/usePipelines.js";
3
+ import { Button } from "../components/ui/button.js";
4
+ import { NewRunDialog } from "../features/workbench/components/NewRunDialog.js";
5
+ import { c } from "react/compiler-runtime";
6
+ import { useCallback, useState } from "react";
7
+ import { jsx, jsxs } from "react/jsx-runtime";
8
+ import { ChevronDown, Loader2, Play } from "lucide-react";
9
+ import { formatDistanceToNow } from "date-fns";
10
+ var STATUS_DOT_COLORS = {
11
+ completed: "bg-green-500",
12
+ running: "bg-blue-500",
13
+ failed: "bg-red-500",
14
+ paused: "bg-yellow-500",
15
+ canceled: "bg-orange-500",
16
+ pending: "bg-muted-foreground"
17
+ };
18
+ function StudioLandingPage() {
19
+ let s = c(28), { router: p } = useStudio(), [m, h] = useState(!1), [v, y] = useState(3), b;
20
+ s[0] === Symbol.for("react.memo_cache_sentinel") ? (b = { parentId: null }, s[0] = b) : b = s[0];
21
+ let x = useFilterPipelines(void 0, b, "createdAt", "DESC", 0, v), S;
22
+ s[1] === x.data?.data ? S = s[2] : (S = x.data?.data ?? [], s[1] = x.data?.data, s[2] = S);
23
+ let C = S, w = x.data?.total ?? 0, T = C.length < w, E;
24
+ s[3] === Symbol.for("react.memo_cache_sentinel") ? (E = {
25
+ parentId: null,
26
+ status: "paused"
27
+ }, s[3] = E) : E = s[3];
28
+ let D = useFilterPipelines(void 0, E, "createdAt", "DESC", 0, 5), O;
29
+ s[4] === D.data?.data ? O = s[5] : (O = D.data?.data ?? [], s[4] = D.data?.data, s[5] = O);
30
+ let k = O, A;
31
+ s[6] === p ? A = s[7] : (A = (e) => {
32
+ h(!1), p.navigateToPipeline(e);
33
+ }, s[6] = p, s[7] = A);
34
+ let j = A, M;
35
+ s[8] === p ? M = s[9] : (M = (e) => {
36
+ p.navigateToPipeline(e);
37
+ }, s[8] = p, s[9] = M);
38
+ let N = M, P;
39
+ s[10] === Symbol.for("react.memo_cache_sentinel") ? (P = /* @__PURE__ */ jsx("div", {
40
+ className: "flex justify-center",
41
+ children: /* @__PURE__ */ jsxs(Button, {
42
+ variant: "secondary",
43
+ size: "sm",
44
+ className: "gap-1.5",
45
+ onClick: () => h(!0),
46
+ children: [/* @__PURE__ */ jsx(Play, { className: "h-3.5 w-3.5" }), "New Run"]
47
+ })
48
+ }), s[10] = P) : P = s[10];
49
+ let F;
50
+ s[11] !== N || s[12] !== k ? (F = k.length > 0 && /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("p", {
51
+ className: "text-muted-foreground mb-2 text-xs font-medium",
52
+ children: "Needs Attention"
53
+ }), /* @__PURE__ */ jsx("div", {
54
+ className: "divide-border divide-y",
55
+ children: k.map((e) => /* @__PURE__ */ jsx(RecentRunItem, {
56
+ pipeline: e,
57
+ onClick: () => N(e.id)
58
+ }, e.id))
59
+ })] }), s[11] = N, s[12] = k, s[13] = F) : F = s[13];
60
+ let I;
61
+ s[14] !== x.isLoading || s[15] !== N || s[16] !== T || s[17] !== C ? (I = x.isLoading && C.length === 0 ? /* @__PURE__ */ jsx("div", {
62
+ className: "flex justify-center py-4",
63
+ children: /* @__PURE__ */ jsx(Loader2, { className: "text-muted-foreground h-4 w-4 animate-spin" })
64
+ }) : C.length > 0 ? /* @__PURE__ */ jsxs("div", { children: [
65
+ /* @__PURE__ */ jsx("p", {
66
+ className: "text-muted-foreground mb-2 text-xs font-medium",
67
+ children: "Recent"
68
+ }),
69
+ /* @__PURE__ */ jsx("div", {
70
+ className: "max-h-[280px] overflow-auto",
71
+ children: /* @__PURE__ */ jsx("div", {
72
+ className: "divide-border divide-y",
73
+ children: C.map((e) => /* @__PURE__ */ jsx(RecentRunItem, {
74
+ pipeline: e,
75
+ onClick: () => N(e.id)
76
+ }, e.id))
77
+ })
78
+ }),
79
+ T && /* @__PURE__ */ jsxs("button", {
80
+ className: "text-muted-foreground hover:text-foreground mt-2 flex w-full items-center justify-center gap-1 py-1 text-xs transition-colors",
81
+ onClick: () => y(_temp),
82
+ children: [/* @__PURE__ */ jsx(ChevronDown, { className: "h-3 w-3" }), "Load more"]
83
+ })
84
+ ] }) : null, s[14] = x.isLoading, s[15] = N, s[16] = T, s[17] = C, s[18] = I) : I = s[18];
85
+ let L;
86
+ s[19] !== F || s[20] !== I ? (L = /* @__PURE__ */ jsxs("div", {
87
+ className: "w-full max-w-sm space-y-6 px-4",
88
+ children: [
89
+ P,
90
+ F,
91
+ I
92
+ ]
93
+ }), s[19] = F, s[20] = I, s[21] = L) : L = s[21];
94
+ let R;
95
+ s[22] !== j || s[23] !== m ? (R = /* @__PURE__ */ jsx(NewRunDialog, {
96
+ open: m,
97
+ onOpenChange: h,
98
+ onSuccess: j
99
+ }), s[22] = j, s[23] = m, s[24] = R) : R = s[24];
100
+ let z;
101
+ return s[25] !== R || s[26] !== L ? (z = /* @__PURE__ */ jsxs("div", {
102
+ className: "flex h-full flex-col items-center justify-center",
103
+ children: [L, R]
104
+ }), s[25] = R, s[26] = L, s[27] = z) : z = s[27], z;
105
+ }
106
+ function _temp(e) {
107
+ return e + 5;
108
+ }
109
+ function RecentRunItem(e) {
110
+ let i = c(17), { pipeline: a, onClick: o } = e, s = `h-1.5 w-1.5 shrink-0 rounded-full ${STATUS_DOT_COLORS[a.status] ?? "bg-muted-foreground"}`, l;
111
+ i[0] === s ? l = i[1] : (l = /* @__PURE__ */ jsx("span", { className: s }), i[0] = s, i[1] = l);
112
+ let u;
113
+ i[2] !== a.blockName || i[3] !== a.run ? (u = /* @__PURE__ */ jsxs("span", {
114
+ className: "truncate text-sm font-medium",
115
+ children: [
116
+ "Run #",
117
+ a.run,
118
+ " · ",
119
+ a.blockName
120
+ ]
121
+ }), i[2] = a.blockName, i[3] = a.run, i[4] = u) : u = i[4];
122
+ let d;
123
+ i[5] !== l || i[6] !== u ? (d = /* @__PURE__ */ jsxs("div", {
124
+ className: "flex items-center gap-2",
125
+ children: [l, u]
126
+ }), i[5] = l, i[6] = u, i[7] = d) : d = i[7];
127
+ let f = a.status, h;
128
+ i[8] === a.createdAt ? h = i[9] : (h = formatDistanceToNow(new Date(a.createdAt), { addSuffix: !0 }), i[8] = a.createdAt, i[9] = h);
129
+ let g;
130
+ i[10] !== a.status || i[11] !== h ? (g = /* @__PURE__ */ jsxs("p", {
131
+ className: "text-muted-foreground mt-0.5 pl-3.5 text-xs",
132
+ children: [
133
+ f,
134
+ " · ",
135
+ h
136
+ ]
137
+ }), i[10] = a.status, i[11] = h, i[12] = g) : g = i[12];
138
+ let _;
139
+ return i[13] !== o || i[14] !== d || i[15] !== g ? (_ = /* @__PURE__ */ jsxs("button", {
140
+ className: "hover:bg-accent w-full rounded-md px-2 py-2.5 text-left transition-colors",
141
+ onClick: o,
142
+ children: [d, g]
143
+ }), i[13] = o, i[14] = d, i[15] = g, i[16] = _) : _ = i[16], _;
144
+ }
145
+ export { StudioLandingPage as default };
@@ -1,67 +1,91 @@
1
1
  import { useStudio } from "../providers/StudioProvider.js";
2
+ import { usePipeline } from "../hooks/usePipelines.js";
2
3
  import { useWorkspace } from "../hooks/useWorkspaces.js";
3
- import MainLayout_default from "../components/layout/MainLayout.js";
4
4
  import ErrorSnackbar_default from "../components/snackbars/ErrorSnackbar.js";
5
5
  import LoadingCentered_default from "../components/LoadingCentered.js";
6
- import { usePipeline } from "../hooks/usePipelines.js";
7
6
  import Workbench from "../features/workbench/Workbench.js";
8
7
  import { requireParam } from "../lib/requireParam.js";
9
8
  import { c } from "react/compiler-runtime";
9
+ import { useCallback, useMemo } from "react";
10
10
  import { jsx, jsxs } from "react/jsx-runtime";
11
11
  import { useParams } from "react-router-dom";
12
12
  import { Home } from "lucide-react";
13
- function WorkbenchPage() {
14
- let m = c(37), { router: h } = useStudio(), g = useParams(), _;
15
- m[0] === g ? _ = m[1] : (_ = requireParam(g, "pipelineId"), m[0] = g, m[1] = _);
16
- let v = usePipeline(_), y = v.data?.workspaceId, b = useWorkspace(y), x;
17
- m[2] === h ? x = m[3] : (x = h.getDashboard(), m[2] = h, m[3] = x);
18
- let S;
19
- m[4] === Symbol.for("react.memo_cache_sentinel") ? (S = /* @__PURE__ */ jsx(Home, { className: "h-4 w-4" }), m[4] = S) : S = m[4];
20
- let C;
21
- m[5] === x ? C = m[6] : (C = {
13
+ function WorkbenchPage(m) {
14
+ let h = c(45), g;
15
+ h[0] === m ? g = h[1] : (g = m === void 0 ? {} : m, h[0] = m, h[1] = g);
16
+ let { previewPanelOpen: _, onPreviewPanelOpenChange: v, isDeveloperMode: y, getPreviewUrl: b, getEnvironmentPreviewUrl: x, environments: S } = g, { router: C } = useStudio(), w = useParams(), T;
17
+ h[2] === w ? T = h[3] : (T = requireParam(w, "pipelineId"), h[2] = w, h[3] = T);
18
+ let E = usePipeline(T), D = E.data?.workspaceId, O = useWorkspace(D);
19
+ O.data?.environments;
20
+ let k = S ?? O.data?.environments, A = x ?? _temp, j;
21
+ h[4] === C ? j = h[5] : (j = C.getDashboard(), h[4] = C, h[5] = j);
22
+ let M;
23
+ h[6] === Symbol.for("react.memo_cache_sentinel") ? (M = /* @__PURE__ */ jsx(Home, { className: "h-4 w-4" }), h[6] = M) : M = h[6];
24
+ let N;
25
+ h[7] === j ? N = h[8] : (N = {
22
26
  label: "Dashboard",
23
- href: x,
24
- icon: S
25
- }, m[5] = x, m[6] = C);
26
- let w;
27
- m[7] === h ? w = m[8] : (w = h.getWorkspaces(), m[7] = h, m[8] = w);
28
- let T;
29
- m[9] === w ? T = m[10] : (T = {
30
- label: "Workspaces",
31
- href: w
32
- }, m[9] = w, m[10] = T);
33
- let E = b.data?.title ?? "", D;
34
- m[11] !== h || m[12] !== y ? (D = y ? h.getWorkspace(y) : void 0, m[11] = h, m[12] = y, m[13] = D) : D = m[13];
35
- let O;
36
- m[14] !== E || m[15] !== D ? (O = {
37
- label: E,
38
- href: D
39
- }, m[14] = E, m[15] = D, m[16] = O) : O = m[16];
40
- let k = `Run #${v.data?.run}${v.data?.title ? ` (${v.data.title})` : ""}`, A;
41
- m[17] === k ? A = m[18] : (A = { label: k }, m[17] = k, m[18] = A);
42
- let j;
43
- m[19] !== A || m[20] !== C || m[21] !== T || m[22] !== O ? (j = [
44
- C,
45
- T,
46
- O,
47
- A
48
- ], m[19] = A, m[20] = C, m[21] = T, m[22] = O, m[23] = j) : j = m[23];
49
- let M = j, N;
50
- m[24] === v.error ? N = m[25] : (N = /* @__PURE__ */ jsx(ErrorSnackbar_default, { error: v.error }), m[24] = v.error, m[25] = N);
27
+ href: j,
28
+ icon: M
29
+ }, h[7] = j, h[8] = N);
51
30
  let P;
52
- m[26] !== v.data || m[27] !== v.error || m[28] !== v.isLoading ? (P = v.data ? /* @__PURE__ */ jsx(Workbench, { pipeline: v.data }) : !v.isLoading && !v.error ? /* @__PURE__ */ jsx("p", {
31
+ h[9] === C ? P = h[10] : (P = C.getWorkspaces(), h[9] = C, h[10] = P);
32
+ let F;
33
+ h[11] === P ? F = h[12] : (F = {
34
+ label: "Workspaces",
35
+ href: P
36
+ }, h[11] = P, h[12] = F);
37
+ let I = O.data?.title ?? "", L;
38
+ h[13] !== C || h[14] !== D ? (L = D ? C.getWorkspace(D) : void 0, h[13] = C, h[14] = D, h[15] = L) : L = h[15];
39
+ let R;
40
+ h[16] !== I || h[17] !== L ? (R = {
41
+ label: I,
42
+ href: L
43
+ }, h[16] = I, h[17] = L, h[18] = R) : R = h[18];
44
+ let z = `Run #${E.data?.run}${E.data?.title ? ` (${E.data.title})` : ""}`, B;
45
+ h[19] === z ? B = h[20] : (B = { label: z }, h[19] = z, h[20] = B);
46
+ let V;
47
+ h[21] !== R || h[22] !== B || h[23] !== N || h[24] !== F ? (V = [
48
+ N,
49
+ F,
50
+ R,
51
+ B
52
+ ], h[21] = R, h[22] = B, h[23] = N, h[24] = F, h[25] = V) : V = h[25];
53
+ let H = V, U;
54
+ h[26] === E.error ? U = h[27] : (U = /* @__PURE__ */ jsx(ErrorSnackbar_default, { error: E.error }), h[26] = E.error, h[27] = U);
55
+ let W;
56
+ h[28] !== H || h[29] !== E.data || h[30] !== E.error || h[31] !== E.isLoading || h[32] !== b || h[33] !== y || h[34] !== v || h[35] !== _ || h[36] !== k || h[37] !== A ? (W = E.data ? /* @__PURE__ */ jsx(Workbench, {
57
+ pipeline: E.data,
58
+ breadcrumbData: H,
59
+ previewPanelOpen: _,
60
+ onPreviewPanelOpenChange: v,
61
+ isDeveloperMode: y,
62
+ getPreviewUrl: b,
63
+ getEnvironmentPreviewUrl: A,
64
+ environments: k
65
+ }) : !E.isLoading && !E.error ? /* @__PURE__ */ jsx("p", {
53
66
  className: "text-muted-foreground py-8 text-center text-sm",
54
67
  children: "Pipeline not found."
55
- }) : null, m[26] = v.data, m[27] = v.error, m[28] = v.isLoading, m[29] = P) : P = m[29];
56
- let F;
57
- m[30] !== v.isLoading || m[31] !== P ? (F = /* @__PURE__ */ jsx(LoadingCentered_default, {
58
- loading: v.isLoading,
59
- children: P
60
- }), m[30] = v.isLoading, m[31] = P, m[32] = F) : F = m[32];
61
- let I;
62
- return m[33] !== M || m[34] !== N || m[35] !== F ? (I = /* @__PURE__ */ jsxs(MainLayout_default, {
63
- breadcrumbsData: M,
64
- children: [N, F]
65
- }), m[33] = M, m[34] = N, m[35] = F, m[36] = I) : I = m[36], I;
68
+ }) : null, h[28] = H, h[29] = E.data, h[30] = E.error, h[31] = E.isLoading, h[32] = b, h[33] = y, h[34] = v, h[35] = _, h[36] = k, h[37] = A, h[38] = W) : W = h[38];
69
+ let G;
70
+ h[39] !== E.isLoading || h[40] !== W ? (G = /* @__PURE__ */ jsx(LoadingCentered_default, {
71
+ loading: E.isLoading,
72
+ children: W
73
+ }), h[39] = E.isLoading, h[40] = W, h[41] = G) : G = h[41];
74
+ let K;
75
+ return h[42] !== U || h[43] !== G ? (K = /* @__PURE__ */ jsx("div", {
76
+ className: "flex h-svh flex-col",
77
+ children: /* @__PURE__ */ jsxs("div", {
78
+ className: "flex-1 overflow-hidden",
79
+ children: [U, G]
80
+ })
81
+ }), h[42] = U, h[43] = G, h[44] = K) : K = h[44], K;
82
+ }
83
+ function _temp(t, f) {
84
+ if (!t.connectionUrl) return "";
85
+ let p = new URLSearchParams({
86
+ url: t.connectionUrl,
87
+ name: t.envName || t.workerId || ""
88
+ });
89
+ return f ? `/embed/env/preview/pipelines/${f}?${p}` : `/embed/env/preview?${p}`;
66
90
  }
67
91
  export { WorkbenchPage as default };
@@ -9,7 +9,7 @@ import { getNamespacesByPipelineCacheKey } from "../hooks/useNamespaces.js";
9
9
  import { getWorkflowCacheKey, getWorkflowsByPipelineCacheKey, getWorkflowsCacheKey } from "../hooks/useWorkflows.js";
10
10
  import { c } from "react/compiler-runtime";
11
11
  import { useEffect, useRef } from "react";
12
- import { useQueryClient } from "@tanstack/react-query";
12
+ import { QueryClient, useQueryClient } from "@tanstack/react-query";
13
13
  var import_debounce = /* @__PURE__ */ __toESM(require_debounce(), 1), DEBOUNCE_MS = 300;
14
14
  function createDebouncedInvalidator(e, u) {
15
15
  return (0, import_debounce.default)(() => {
@@ -17,25 +17,25 @@ function createDebouncedInvalidator(e, u) {
17
17
  }, DEBOUNCE_MS);
18
18
  }
19
19
  function InvalidationEventsProvider() {
20
- let e = c(5), { environment: f } = useStudio(), _ = useQueryClient(), v;
20
+ let e = c(5), { environment: f } = useStudio(), g = useQueryClient(), v;
21
21
  e[0] === Symbol.for("react.memo_cache_sentinel") ? (v = /* @__PURE__ */ new Map(), e[0] = v) : v = e[0];
22
22
  let y = useRef(v), b, x;
23
- return e[1] !== f.id || e[2] !== _ ? (b = () => {
23
+ return e[1] !== f.id || e[2] !== g ? (b = () => {
24
24
  if (!f.id) return;
25
25
  let e = f.id, d = y.current, p = function(e) {
26
26
  let u = JSON.stringify(e);
27
- d.has(u) || d.set(u, createDebouncedInvalidator(_, e)), d.get(u)();
27
+ d.has(u) || d.set(u, createDebouncedInvalidator(g, e)), d.get(u)();
28
28
  }, m = eventBus.on(SseClientEvents.WORKFLOW_CREATED, (u) => {
29
29
  u.namespaceId && p(getWorkflowsCacheKey(e, u.namespaceId)), u.pipelineId && (p(getNamespacesByPipelineCacheKey(e, u.pipelineId)), p(getWorkflowsByPipelineCacheKey(e, u.pipelineId)));
30
30
  }), h = eventBus.on(SseClientEvents.WORKFLOW_UPDATED, (u) => {
31
31
  u.id && p(getWorkflowCacheKey(e, u.id)), u.namespaceId && p(getWorkflowsCacheKey(e, u.namespaceId)), u.pipelineId && (p(getNamespacesByPipelineCacheKey(e, u.pipelineId)), p(getWorkflowsByPipelineCacheKey(e, u.pipelineId)));
32
- }), g = eventBus.on(SseClientEvents.DOCUMENT_CREATED, (u) => {
32
+ }), _ = eventBus.on(SseClientEvents.DOCUMENT_CREATED, (u) => {
33
33
  u.workflowId && p(getDocumentsCacheKey(e, u.workflowId));
34
34
  });
35
35
  return () => {
36
- m(), h(), g(), d.forEach(_temp), d.clear();
36
+ m(), h(), _(), d.forEach(_temp), d.clear();
37
37
  };
38
- }, x = [_, f.id], e[1] = f.id, e[2] = _, e[3] = b, e[4] = x) : (b = e[3], x = e[4]), useEffect(b, x), null;
38
+ }, x = [g, f.id], e[1] = f.id, e[2] = g, e[3] = b, e[4] = x) : (b = e[3], x = e[4]), useEffect(b, x), null;
39
39
  }
40
40
  function _temp(e) {
41
41
  return e.cancel();
@@ -0,0 +1,21 @@
1
+ import { c } from "react/compiler-runtime";
2
+ import { useState } from "react";
3
+ import { Fragment as Fragment$1, jsx } from "react/jsx-runtime";
4
+ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
5
+ function QueryProvider(o) {
6
+ let s = c(5), { children: l } = o, [u] = useState(_temp), d;
7
+ s[0] === l ? d = s[1] : (d = /* @__PURE__ */ jsx(Fragment$1, { children: l }), s[0] = l, s[1] = d);
8
+ let f;
9
+ return s[2] !== u || s[3] !== d ? (f = /* @__PURE__ */ jsx(QueryClientProvider, {
10
+ client: u,
11
+ children: d
12
+ }), s[2] = u, s[3] = d, s[4] = f) : f = s[4], f;
13
+ }
14
+ function _temp() {
15
+ return new QueryClient({ defaultOptions: { queries: {
16
+ staleTime: 6e4,
17
+ refetchOnWindowFocus: !1,
18
+ retry: 1
19
+ } } });
20
+ }
21
+ export { QueryProvider };
@@ -12,6 +12,12 @@ var LocalRouter = class {
12
12
  async navigateToEnvironmentInfo() {
13
13
  await this.navigate("/info");
14
14
  }
15
+ getRuns() {
16
+ return "/runs";
17
+ }
18
+ getRunsActionRequired() {
19
+ return "/runs/action-required";
20
+ }
15
21
  getDashboard() {
16
22
  return "/dashboard";
17
23
  }
@@ -60,6 +66,9 @@ var LocalRouter = class {
60
66
  getEmbedPipeline(e) {
61
67
  return `/embed/pipelines/${e}`;
62
68
  }
69
+ getPreviewPipeline(e) {
70
+ return `/embed/preview/pipelines/${e}`;
71
+ }
63
72
  getCurrentEnvironmentId() {
64
73
  return this.envId;
65
74
  }
@@ -1,12 +1,6 @@
1
- import { ApiClientEvents } from "../events/api-client-events.js";
2
- import { eventBus } from "./eventEmitter.js";
3
- import axios from "axios";
4
- import { ApiV1AuthApi, Configuration } from "@loopstack/api-client";
5
- function createApiClient(a) {
6
- let o = new Configuration({ baseOptions: {
7
- withCredentials: !0,
8
- headers: { "Content-Type": "application/json" }
9
- } }), s = axios.create();
10
- return s.interceptors.response.use((e) => e, (i) => ([401, 403].includes(i.response?.status) && eventBus.emit(ApiClientEvents.UNAUTHORIZED, a.id), i.code === "ERR_NETWORK" && eventBus.emit(ApiClientEvents.ERR_NETWORK, a.id), Promise.reject(i))), { auth: new ApiV1AuthApi(o, a.url, s) };
1
+ import { createAxiosClient } from "../api/client.js";
2
+ import { createApi } from "../api/index.js";
3
+ function createApiClient(n) {
4
+ return { auth: createApi(createAxiosClient(n.url, n.id)).auth };
11
5
  }
12
6
  export { createApiClient };
@@ -1,2 +1,2 @@
1
- import { eventBus } from "./eventEmitter.js";
2
1
  import { createApiClient } from "./createApiClient.js";
2
+ import { eventBus } from "./eventEmitter.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@loopstack/loopstack-studio",
3
- "version": "0.21.2",
3
+ "version": "0.22.0",
4
4
  "repository": "loopstack-ai/loopstack-studio",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -26,8 +26,7 @@
26
26
  "dependencies": {
27
27
  "@fontsource/roboto": "^5.2.10",
28
28
  "@hookform/resolvers": "^5.2.2",
29
- "@loopstack/api-client": "^0.20.1",
30
- "@loopstack/contracts": "^0.22.0",
29
+ "@loopstack/contracts": "^0.23.0",
31
30
  "@radix-ui/react-accordion": "^1.2.12",
32
31
  "@radix-ui/react-alert-dialog": "^1.1.15",
33
32
  "@radix-ui/react-avatar": "^1.1.11",
@@ -1,69 +0,0 @@
1
- import { cn } from "../../lib/utils.js";
2
- import { Input } from "../../components/ui/input.js";
3
- import { useCodeExplorerContext } from "./providers/CodeExplorerProvider.js";
4
- import { CodeExplorerTree } from "./components/CodeExplorerTree.js";
5
- import { c } from "react/compiler-runtime";
6
- import { jsx, jsxs } from "react/jsx-runtime";
7
- function CodeExplorer(s) {
8
- let l = c(20), { className: u } = s, { fileTree: d, isTreeLoading: f, error: p, searchQuery: m, setSearchQuery: h, selectFile: g, selectedFile: _, clearSelection: v, closeFile: y } = useCodeExplorerContext(), b;
9
- l[0] === u ? b = l[1] : (b = cn("flex h-full w-full flex-col gap-2 overflow-hidden", u), l[0] = u, l[1] = b);
10
- let x;
11
- l[2] === h ? x = l[3] : (x = (e) => h(e.target.value), l[2] = h, l[3] = x);
12
- let S;
13
- l[4] !== m || l[5] !== x ? (S = /* @__PURE__ */ jsx("div", {
14
- className: "flex shrink-0 flex-col gap-2",
15
- children: /* @__PURE__ */ jsx(Input, {
16
- type: "search",
17
- placeholder: "Search files...",
18
- value: m,
19
- onChange: x,
20
- className: "h-8 w-full",
21
- "aria-label": "Search files"
22
- })
23
- }), l[4] = m, l[5] = x, l[6] = S) : S = l[6];
24
- let C;
25
- l[7] !== v || l[8] !== y || l[9] !== p || l[10] !== d || l[11] !== f || l[12] !== m || l[13] !== g || l[14] !== _?.id ? (C = /* @__PURE__ */ jsx("div", {
26
- className: "flex min-h-0 flex-1 flex-col overflow-hidden",
27
- children: /* @__PURE__ */ jsx("div", {
28
- className: "flex min-h-0 flex-1 flex-col overflow-hidden rounded-md border bg-background",
29
- children: p ? /* @__PURE__ */ jsx("div", {
30
- className: "flex flex-1 flex-col items-center justify-center p-6 text-center",
31
- children: /* @__PURE__ */ jsxs("div", {
32
- className: "rounded-lg border border-destructive bg-destructive/10 p-4 max-w-md",
33
- children: [/* @__PURE__ */ jsx("p", {
34
- className: "text-sm font-semibold text-destructive mb-2",
35
- children: "File Explorer Error"
36
- }), /* @__PURE__ */ jsx("p", {
37
- className: "text-xs text-destructive/90",
38
- children: p.message
39
- })]
40
- })
41
- }) : f ? /* @__PURE__ */ jsx("div", {
42
- className: "flex flex-1 items-center justify-center p-4",
43
- children: /* @__PURE__ */ jsx("p", {
44
- className: "text-xs text-muted-foreground",
45
- children: "Loading file tree..."
46
- })
47
- }) : d.length === 0 ? /* @__PURE__ */ jsx("div", {
48
- className: "flex flex-1 items-center justify-center p-4",
49
- children: /* @__PURE__ */ jsx("p", {
50
- className: "text-xs text-muted-foreground",
51
- children: "No files found"
52
- })
53
- }) : /* @__PURE__ */ jsx(CodeExplorerTree, {
54
- nodes: d,
55
- searchQuery: m,
56
- onSelectFile: g,
57
- onClearSelection: v,
58
- onCloseFile: y,
59
- selectedFileId: _?.id
60
- })
61
- })
62
- }), l[7] = v, l[8] = y, l[9] = p, l[10] = d, l[11] = f, l[12] = m, l[13] = g, l[14] = _?.id, l[15] = C) : C = l[15];
63
- let w;
64
- return l[16] !== b || l[17] !== S || l[18] !== C ? (w = /* @__PURE__ */ jsxs("div", {
65
- className: b,
66
- children: [S, C]
67
- }), l[16] = b, l[17] = S, l[18] = C, l[19] = w) : w = l[19], w;
68
- }
69
- export { CodeExplorer };
@@ -1,43 +0,0 @@
1
- import { ScrollArea } from "../../../components/ui/scroll-area.js";
2
- import { useCodeExplorerContext } from "../providers/CodeExplorerProvider.js";
3
- import { CodeExplorerTreeNode } from "./CodeExplorerTreeNode.js";
4
- import { useMemo } from "react";
5
- import { jsx } from "react/jsx-runtime";
6
- function filterTree(e, i) {
7
- if (!i.trim()) return e;
8
- let a = i.trim().toLowerCase();
9
- return e.map((e) => filterNode(e, a)).filter((e) => e !== null);
10
- }
11
- function filterNode(e, i) {
12
- if (e.type === "file") return e.name.toLowerCase().includes(i) ? e : null;
13
- let a = e.children ? e.children.map((e) => filterNode(e, i)).filter((e) => e !== null) : [];
14
- return e.name.toLowerCase().includes(i) || a.length > 0 ? {
15
- ...e,
16
- children: a
17
- } : null;
18
- }
19
- function CodeExplorerTree({ nodes: o, searchQuery: s = "", onSelectFile: c, onClearSelection: l, onCloseFile: u, selectedFileId: d }) {
20
- let f = useMemo(() => filterTree(o, s), [o, s]), { expandedFolders: p, toggleFolder: m } = useCodeExplorerContext();
21
- return /* @__PURE__ */ jsx(ScrollArea, {
22
- className: "h-full w-full",
23
- children: /* @__PURE__ */ jsx("div", {
24
- className: "w-full py-1",
25
- role: "tree",
26
- "aria-label": "File tree",
27
- children: f.length === 0 ? /* @__PURE__ */ jsx("p", {
28
- className: "px-2 py-4 text-sm text-muted-foreground",
29
- children: "No files match"
30
- }) : f.map((e) => /* @__PURE__ */ jsx(CodeExplorerTreeNode, {
31
- node: e,
32
- depth: 0,
33
- onSelectFile: c,
34
- onClearSelection: l,
35
- onCloseFile: u,
36
- selectedFileId: d,
37
- expandedFolders: p,
38
- toggleFolder: m
39
- }, e.id))
40
- })
41
- });
42
- }
43
- export { CodeExplorerTree };
@@ -1,82 +0,0 @@
1
- import { cn } from "../../../lib/utils.js";
2
- import { Button } from "../../../components/ui/button.js";
3
- import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "../../../components/ui/collapsible.js";
4
- import { getFileIcon, getFolderIcon } from "../utils/fileIcons.js";
5
- import { jsx, jsxs } from "react/jsx-runtime";
6
- import { ChevronDown, ChevronRight, X } from "lucide-react";
7
- var INDENT_PX = 16, BASE_PADDING_PX = 8;
8
- function CodeExplorerTreeNode({ node: h, depth: g, onSelectFile: _, onClearSelection: v, onCloseFile: y, isSelected: b = !1, selectedFileId: x, expandedFolders: S = /* @__PURE__ */ new Set(), toggleFolder: C }) {
9
- let w = S.has(h.id), T = g * INDENT_PX + BASE_PADDING_PX, E = x === h.id || b;
10
- if (h.type === "file") {
11
- let m = getFileIcon(h.name);
12
- return /* @__PURE__ */ jsxs("div", {
13
- className: cn("flex w-full min-w-0 items-center gap-2 rounded-sm py-1 group", "text-muted-foreground", E && "bg-muted"),
14
- style: { paddingLeft: T },
15
- children: [/* @__PURE__ */ jsxs("button", {
16
- type: "button",
17
- className: cn("flex flex-1 min-w-0 items-center gap-2 rounded-sm text-left text-sm hover:bg-muted/50", "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"),
18
- onClick: () => _?.(h),
19
- "aria-label": `Select file ${h.name}`,
20
- children: [/* @__PURE__ */ jsx(m, {
21
- className: "h-4 w-4 shrink-0 text-muted-foreground",
22
- "aria-hidden": !0
23
- }), /* @__PURE__ */ jsx("span", {
24
- className: "truncate",
25
- children: h.name
26
- })]
27
- }), E && (y || v) && /* @__PURE__ */ jsx(Button, {
28
- variant: "ghost",
29
- size: "icon",
30
- className: "h-6 w-6 shrink-0 opacity-0 group-hover:opacity-100 transition-opacity",
31
- onClick: (e) => {
32
- e.stopPropagation(), y ? y(h) : v && v();
33
- },
34
- "aria-label": "Close file",
35
- children: /* @__PURE__ */ jsx(X, { className: "h-3.5 w-3.5" })
36
- })]
37
- });
38
- }
39
- let D = h.children && h.children.length > 0, O = getFolderIcon(w);
40
- return /* @__PURE__ */ jsxs(Collapsible, {
41
- open: w,
42
- onOpenChange: () => {
43
- C && C(h.id);
44
- },
45
- children: [/* @__PURE__ */ jsxs(CollapsibleTrigger, {
46
- className: cn("flex w-full min-w-0 items-center gap-1 rounded-sm py-1 text-left text-sm hover:bg-muted", "text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", "disabled:cursor-default disabled:opacity-50"),
47
- style: { paddingLeft: T },
48
- disabled: !D,
49
- "aria-label": `${w ? "Collapse" : "Expand"} folder ${h.name}`,
50
- children: [
51
- D ? jsx(w ? ChevronDown : ChevronRight, {
52
- className: "h-4 w-4 shrink-0",
53
- "aria-hidden": !0
54
- }) : /* @__PURE__ */ jsx("span", {
55
- className: "w-4 shrink-0",
56
- "aria-hidden": !0
57
- }),
58
- /* @__PURE__ */ jsx(O, {
59
- className: "h-4 w-4 shrink-0 text-muted-foreground",
60
- "aria-hidden": !0
61
- }),
62
- /* @__PURE__ */ jsx("span", {
63
- className: "truncate",
64
- children: h.name
65
- })
66
- ]
67
- }), D && /* @__PURE__ */ jsx(CollapsibleContent, {
68
- className: "h-full",
69
- children: h.children.map((e) => /* @__PURE__ */ jsx(CodeExplorerTreeNode, {
70
- node: e,
71
- depth: g + 1,
72
- onSelectFile: _,
73
- onClearSelection: v,
74
- onCloseFile: y,
75
- selectedFileId: x,
76
- expandedFolders: S,
77
- toggleFolder: C
78
- }, e.id))
79
- })]
80
- });
81
- }
82
- export { CodeExplorerTreeNode };