@loopstack/loopstack-studio 0.35.1 → 0.37.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 (43) hide show
  1. package/dist/features/documents/DocumentRenderer.js +7 -7
  2. package/dist/features/documents/renderers/DocumentFormRenderer.js +1 -1
  3. package/dist/features/documents/renderers/DocumentMessageRenderer.js +23 -9
  4. package/dist/features/documents/renderers/LlmMessage.js +90 -50
  5. package/dist/features/file-explorer/components/FileExplorerPanel.js +66 -73
  6. package/dist/features/file-explorer/hooks/useFileExplorer.js +29 -25
  7. package/dist/features/file-explorer/providers/FileExplorerProvider.js +53 -53
  8. package/dist/features/git/components/WorkbenchGitPanel.js +36 -33
  9. package/dist/features/git/hooks/useGit.js +36 -33
  10. package/dist/features/oauth/OAuthCallbackPage.js +2 -0
  11. package/dist/features/run-view/RunView.js +153 -0
  12. package/dist/features/run-view/Transcript.js +127 -0
  13. package/dist/features/run-view/prompts/FormPrompt.js +129 -0
  14. package/dist/features/run-view/prompts/OAuthPrompt.js +94 -0
  15. package/dist/features/run-view/prompts/SandboxRunButton.js +35 -0
  16. package/dist/features/run-view/prompts/SecretPrompt.js +69 -0
  17. package/dist/features/run-view/prompts/SimplePrompts.js +196 -0
  18. package/dist/features/run-view/prompts/cards.js +90 -0
  19. package/dist/features/run-view/prompts/registry.js +16 -0
  20. package/dist/features/run-view/transcript.js +24 -0
  21. package/dist/features/run-view/useRunPrompts.js +111 -0
  22. package/dist/features/run-view/useRunTree.js +59 -0
  23. package/dist/features/run-view/useTreeLlmStreams.js +91 -0
  24. package/dist/features/secrets/components/WorkbenchSecretsPanel.js +1 -1
  25. package/dist/features/secrets/renderers/SecretInputRenderer.js +1 -1
  26. package/dist/features/workbench/WorkflowList.js +111 -52
  27. package/dist/features/workbench/components/EnvironmentSelector.js +46 -0
  28. package/dist/features/workbench/components/NewRunDialog.js +1 -1
  29. package/dist/features/workbench/index.js +1 -1
  30. package/dist/features/workbench/providers/WorkbenchLayoutProvider.js +68 -56
  31. package/dist/features/workspaces/components/WorkspaceHomePage.js +1 -1
  32. package/dist/index.d.ts +2 -0
  33. package/dist/node_modules/dompurify/dist/purify.es.js +351 -228
  34. package/dist/packages/client/dist/resources/workflows.js +2 -1
  35. package/dist/packages/client/dist/stream/invalidations.js +9 -2
  36. package/dist/packages/client/dist/stream/stream.js +73 -28
  37. package/dist/packages/contracts/dist/park-view/index.js +2 -0
  38. package/dist/packages/contracts/dist/park-view/park-view.rules.js +102 -0
  39. package/dist/packages/contracts/dist/park-view/park-view.types.js +15 -0
  40. package/dist/packages/contracts/dist/types/types/ui-message.type.js +15 -2
  41. package/dist/pages/PreviewWorkbenchPage.js +23 -23
  42. package/dist/providers/StudioPreferencesProvider.js +2 -1
  43. package/package.json +4 -4
@@ -7,7 +7,8 @@ function createWorkflowsResource(e) {
7
7
  update: (t, n) => e.put(`/api/v1/workflows/${t}`, n),
8
8
  delete: (t) => e.delete(`/api/v1/workflows/id/${t}`),
9
9
  batchDelete: (t) => e.delete("/api/v1/workflows/batch", { ids: t }),
10
- checkpoints: (t) => e.get(`/api/v1/workflows/${t}/checkpoints`)
10
+ checkpoints: (t) => e.get(`/api/v1/workflows/${t}/checkpoints`),
11
+ toolCalls: (t) => e.get(`/api/v1/workflows/${t}/tool-calls`)
11
12
  };
12
13
  }
13
14
  export { createWorkflowsResource };
@@ -1,9 +1,16 @@
1
1
  import { queryKeys } from "../queries/query-keys.js";
2
2
  function resolveInvalidations(t, n) {
3
3
  switch (t.type) {
4
- case "workflow.created": return t.parentId ? [queryKeys.childWorkflows(n, t.parentId)] : [];
4
+ case "workflow.created": {
5
+ let r = [queryKeys.workflows(n)];
6
+ return t.parentId && r.push(queryKeys.childWorkflows(n, t.parentId)), r;
7
+ }
5
8
  case "workflow.updated": {
6
- let r = [queryKeys.workflow(n, t.id), queryKeys.workflowStatus(n, t.id)];
9
+ let r = [
10
+ queryKeys.workflow(n, t.id),
11
+ queryKeys.workflowStatus(n, t.id),
12
+ queryKeys.workflows(n)
13
+ ];
7
14
  return t.parentId && r.push(queryKeys.childWorkflows(n, t.parentId)), r;
8
15
  }
9
16
  case "document.created": return [queryKeys.documents(n, t.workflowId)];
@@ -3,6 +3,9 @@ import { ClientMessageSchema } from "../../../contracts/dist/events/client-messa
3
3
  import "../../../contracts/dist/events/index.js";
4
4
  import { createSseParser } from "./sse-parser.js";
5
5
  var INITIAL_RETRY_DELAY_MS = 500;
6
+ function isFatalStreamStatus(e) {
7
+ return e >= 400 && e < 500 && e !== 408 && e !== 429;
8
+ }
6
9
  function matchesWorkflow(e, t) {
7
10
  return !t || e.type === "stream.reset" ? !0 : "workflowId" in e ? e.workflowId === t : !0;
8
11
  }
@@ -11,6 +14,8 @@ var LoopstackStream = class {
11
14
  handlers = /* @__PURE__ */ new Map();
12
15
  anyHandlers = /* @__PURE__ */ new Set();
13
16
  statusHandlers = /* @__PURE__ */ new Set();
17
+ errorHandlers = /* @__PURE__ */ new Set();
18
+ fatalError;
14
19
  subscriberCount = 0;
15
20
  abortController;
16
21
  watchdog;
@@ -41,27 +46,47 @@ var LoopstackStream = class {
41
46
  onStatus(e) {
42
47
  return this.statusHandlers.add(e), () => this.statusHandlers.delete(e);
43
48
  }
49
+ onError(e) {
50
+ return this.errorHandlers.add(e), this.fatalError && e(this.fatalError), () => this.errorHandlers.delete(e);
51
+ }
52
+ waitForOpen() {
53
+ return this.currentStatus === "open" ? Promise.resolve() : this.fatalError ? Promise.reject(this.fatalError) : new Promise((e, t) => {
54
+ let n = () => {
55
+ r(), i();
56
+ }, r = this.onStatus((r) => {
57
+ r === "open" ? (n(), e()) : (r === "closed" || r === "idle") && (n(), t(/* @__PURE__ */ Error("Event stream closed before it opened.")));
58
+ }), i = this.onError((e) => {
59
+ n(), t(e);
60
+ });
61
+ });
62
+ }
44
63
  events(e = {}) {
45
- let t = [], n, r = !1, a = this.onAny((r) => {
64
+ let t = [], n, r = !1, i, o = this.onAny((r) => {
46
65
  "userId" in r && matchesWorkflow(r, e.workflowId) && (t.push(r), n?.());
47
- }), o = {
48
- [Symbol.asyncIterator]: () => o,
66
+ }), s = this.onError((e) => {
67
+ i = e, n?.();
68
+ }), c = () => {
69
+ o(), s();
70
+ }, l = {
71
+ [Symbol.asyncIterator]: () => l,
49
72
  next: async () => {
50
- for (; t.length === 0 && !r;) await new Promise((e) => n = e), n = void 0;
51
- return t.length === 0 ? {
52
- done: !0,
53
- value: void 0
54
- } : {
73
+ for (; t.length === 0 && !r && !i;) await new Promise((e) => n = e), n = void 0;
74
+ if (t.length > 0) return {
55
75
  done: !1,
56
76
  value: t.shift()
57
77
  };
78
+ if (c(), i) throw i;
79
+ return {
80
+ done: !0,
81
+ value: void 0
82
+ };
58
83
  },
59
- return: async () => (r = !0, a(), n?.(), {
84
+ return: async () => (r = !0, c(), n?.(), {
60
85
  done: !0,
61
86
  value: void 0
62
87
  })
63
88
  };
64
- return o;
89
+ return l;
65
90
  }
66
91
  close() {
67
92
  this.closed = !0, this.handlers.clear(), this.anyHandlers.clear(), this.subscriberCount = 0, this.abortController?.abort(), this.clearWatchdog(), this.setStatus("closed");
@@ -83,37 +108,55 @@ var LoopstackStream = class {
83
108
  return this.config.workflowId && e.searchParams.set("workflowId", this.config.workflowId), e;
84
109
  }
85
110
  async runLoop() {
86
- this.running = !0;
87
- let t = this.config.fetch ?? fetch, i = 0;
111
+ this.running = !0, this.fatalError = void 0;
112
+ let t = this.config.fetch ?? fetch, a = 0;
88
113
  for (; !this.closed && this.subscriberCount > 0;) {
89
114
  this.abortController = new AbortController();
90
- let { signal: a } = this.abortController;
115
+ let { signal: o } = this.abortController;
91
116
  try {
92
117
  this.setStatus("connecting");
93
118
  let r = { Accept: "text/event-stream" };
94
119
  this.config.token && (r.Authorization = `Bearer ${this.config.token}`), this.lastEventId !== void 0 && (r["Last-Event-ID"] = this.lastEventId);
95
- let o = await t(this.buildUrl(), {
120
+ let s = await t(this.buildUrl(), {
96
121
  headers: r,
97
122
  credentials: this.config.credentials ?? "include",
98
- signal: a
123
+ signal: o
124
+ });
125
+ if (!s.ok) {
126
+ let t = new LoopstackApiError(s.status, void 0, `Event stream failed with status ${s.status}`);
127
+ if (isFatalStreamStatus(s.status)) {
128
+ this.failFatally(t);
129
+ break;
130
+ }
131
+ throw t;
132
+ }
133
+ if (!s.body) throw new LoopstackApiError(s.status, void 0, "Event stream response had no body");
134
+ this.setStatus("open");
135
+ let c = a > 0;
136
+ a = 0, this.resetWatchdog(), c && this.lastEventId === void 0 && this.dispatchMessage({
137
+ type: "stream.reset",
138
+ userId: null,
139
+ workerId: ""
99
140
  });
100
- if (!o.ok || !o.body) throw new LoopstackApiError(o.status, void 0, `Event stream failed with status ${o.status}`);
101
- this.setStatus("open"), i = 0, this.resetWatchdog();
102
- let s = createSseParser((e) => {
141
+ let l = createSseParser((e) => {
103
142
  this.resetWatchdog(), e.id !== void 0 && e.id !== "" && (this.lastEventId = e.id), e.event !== "ping" && e.data && this.dispatch(e.data);
104
- }), c = o.body.getReader(), l = new TextDecoder();
143
+ }), u = s.body.getReader(), d = new TextDecoder();
105
144
  for (;;) {
106
- let { done: e, value: t } = await c.read();
145
+ let { done: e, value: t } = await u.read();
107
146
  if (e) break;
108
- s.feed(l.decode(t, { stream: !0 }));
147
+ l.feed(d.decode(t, { stream: !0 }));
109
148
  }
110
149
  } catch {}
111
150
  if (this.clearWatchdog(), this.currentStatus === "open" && this.setStatus("connecting"), this.closed || this.subscriberCount === 0) break;
112
- i++;
113
- let o = Math.min(this.config.maxRetryDelayMs ?? 1e4, INITIAL_RETRY_DELAY_MS * 2 ** (i - 1)) * (.8 + Math.random() * .4);
114
- await new Promise((e) => setTimeout(e, o));
151
+ a++;
152
+ let s = Math.min(this.config.maxRetryDelayMs ?? 1e4, INITIAL_RETRY_DELAY_MS * 2 ** (a - 1)) * (.8 + Math.random() * .4);
153
+ await new Promise((e) => setTimeout(e, s));
115
154
  }
116
- this.running = !1, this.setStatus(this.closed ? "closed" : "idle");
155
+ this.running = !1, this.setStatus(this.closed || this.fatalError ? "closed" : "idle");
156
+ }
157
+ failFatally(e) {
158
+ this.fatalError = e, this.clearWatchdog();
159
+ for (let t of this.errorHandlers) t(e);
117
160
  }
118
161
  dispatch(e) {
119
162
  let n;
@@ -124,9 +167,7 @@ var LoopstackStream = class {
124
167
  }
125
168
  let r = ClientMessageSchema.safeParse(n);
126
169
  if (r.success) {
127
- let e = r.data;
128
- for (let t of this.handlers.get(e.type) ?? []) t(e);
129
- for (let t of this.anyHandlers) t(e);
170
+ this.dispatchMessage(r.data);
130
171
  return;
131
172
  }
132
173
  let i = n && typeof n == "object" && "type" in n && typeof n.type == "string" ? n.type : "unknown";
@@ -135,6 +176,10 @@ var LoopstackStream = class {
135
176
  raw: n
136
177
  });
137
178
  }
179
+ dispatchMessage(e) {
180
+ for (let t of this.handlers.get(e.type) ?? []) t(e);
181
+ for (let t of this.anyHandlers) t(e);
182
+ }
138
183
  resetWatchdog() {
139
184
  this.clearWatchdog(), this.watchdog = setTimeout(() => {
140
185
  this.abortController?.abort();
@@ -0,0 +1,2 @@
1
+ import "./park-view.types.js";
2
+ import { DISPLAY_WIDGETS, declaredTransitions, evaluateWorkflowPrompts, isAnswerableState, isAnswered, isDocumentActive, isDocumentVisible, pickPrompt, resolveSubmitTransition, toParkView, widgetState } from "./park-view.rules.js";
@@ -0,0 +1,102 @@
1
+ import { WorkflowState } from "../enums/workflow-state.enum.js";
2
+ const DISPLAY_WIDGETS = new Set([
3
+ "message",
4
+ "llm-message",
5
+ "ai-message",
6
+ "markdown",
7
+ "plain",
8
+ "error",
9
+ "link",
10
+ "debug"
11
+ ]);
12
+ function declaredTransitions(e) {
13
+ let n = [], r = e.options?.transition;
14
+ typeof r == "string" && n.push(r);
15
+ let i = e.options?.actions;
16
+ if (Array.isArray(i)) for (let e of i) typeof e.transition == "string" && n.push(e.transition);
17
+ return n;
18
+ }
19
+ function resolveSubmitTransition(e, n) {
20
+ let i = declaredTransitions(e);
21
+ return i.length > 0 ? i.find((e) => n.includes(e)) : n.length === 1 ? n[0] : void 0;
22
+ }
23
+ function widgetState(e, n, r, a = !0) {
24
+ return e.showWhen && !e.showWhen.includes(n ?? "") ? "hidden" : e.enabledWhen && !e.enabledWhen.includes(n ?? "") || !a || resolveSubmitTransition(e, r) === void 0 ? "disabled" : "active";
25
+ }
26
+ function isDocumentVisible(e, n, r) {
27
+ return !(n.internal || e.tags?.includes("internal") || n.hideAtPlaces?.includes(r ?? ""));
28
+ }
29
+ function isDocumentActive(e, n, r) {
30
+ return e.place === r || !!n.enableAtPlaces?.includes(r ?? "");
31
+ }
32
+ function isAnswered(e) {
33
+ return e?.answer !== void 0;
34
+ }
35
+ function isAnswerableState(n, r) {
36
+ return r.length === 0 ? !1 : n === WorkflowState.Waiting || n === WorkflowState.Paused || n === WorkflowState.Failed;
37
+ }
38
+ function evaluateWorkflowPrompts(e, l, u, d = []) {
39
+ if (!isAnswerableState(e.status, e.availableTransitions)) return [];
40
+ let f = [], p = e.availableTransitions;
41
+ for (let c of l) {
42
+ let l = u.get(c.documentName);
43
+ l && (DISPLAY_WIDGETS.has(l.widget) || isDocumentVisible(c, l, e.place) && isDocumentActive(c, l, e.place) && (isAnswered(c.content) || f.push({
44
+ kind: "document",
45
+ workflow: e,
46
+ document: c,
47
+ widget: l,
48
+ state: widgetState(l, e.place, p),
49
+ submitTransition: resolveSubmitTransition(l, p),
50
+ submitDeclared: declaredTransitions(l).length > 0
51
+ })));
52
+ }
53
+ for (let n of d) {
54
+ let o = widgetState(n, e.place, p);
55
+ o !== "hidden" && f.push({
56
+ kind: "workflow",
57
+ workflow: e,
58
+ widget: n,
59
+ state: o,
60
+ submitTransition: resolveSubmitTransition(n, p),
61
+ submitDeclared: declaredTransitions(n).length > 0
62
+ });
63
+ }
64
+ return f.push({
65
+ kind: "bare",
66
+ workflow: e,
67
+ state: "active",
68
+ ...p.length === 1 && { submitTransition: p[0] }
69
+ }), f;
70
+ }
71
+ function pickPrompt(e, n = () => !0) {
72
+ let r = {};
73
+ for (let i of e) {
74
+ if (i.kind === "bare") {
75
+ r.fallback ??= i;
76
+ continue;
77
+ }
78
+ if (i.state === "active") {
79
+ if (n(i)) return r.prompt = i, r;
80
+ i.submitDeclared && (r.blocked ??= i);
81
+ }
82
+ }
83
+ return r;
84
+ }
85
+ function toParkView(e) {
86
+ let { workflow: n, document: r, widget: i } = e, a = n.availableTransitions, o, s = i?.options?.actions;
87
+ return Array.isArray(s) && (o = s.filter((e) => typeof e.transition != "string" || a.includes(e.transition)).map((e) => typeof e.label == "string" ? e.label : String(e.transition ?? "")).filter((e) => e.length > 0)), {
88
+ workflowId: n.id,
89
+ workflowName: n.workflowName,
90
+ place: n.place,
91
+ status: n.status,
92
+ ...i && { widget: i.widget },
93
+ ...r && { documentName: r.documentName },
94
+ ...r?.content && { content: r.content },
95
+ ...i?.schema && { schema: i.schema },
96
+ ...i?.options && { options: i.options },
97
+ transitions: a,
98
+ ...e.submitTransition && { defaultTransition: e.submitTransition },
99
+ ...o?.length && { actions: o }
100
+ };
101
+ }
102
+ export { DISPLAY_WIDGETS, declaredTransitions, evaluateWorkflowPrompts, isAnswerableState, isAnswered, isDocumentActive, isDocumentVisible, pickPrompt, resolveSubmitTransition, toParkView, widgetState };
@@ -0,0 +1,15 @@
1
+ import { z } from "zod";
2
+ z.object({
3
+ workflowId: z.string(),
4
+ workflowName: z.string(),
5
+ place: z.string().nullable(),
6
+ status: z.string(),
7
+ widget: z.string().optional(),
8
+ documentName: z.string().optional(),
9
+ content: z.record(z.string(), z.unknown()).optional(),
10
+ schema: z.record(z.string(), z.unknown()).optional(),
11
+ options: z.record(z.string(), z.unknown()).optional(),
12
+ transitions: z.array(z.string()),
13
+ defaultTransition: z.string().optional(),
14
+ actions: z.array(z.string()).optional()
15
+ });
@@ -31,9 +31,22 @@ const UITextBlockSchema = z.object({
31
31
  UIServerToolUseBlockSchema,
32
32
  UIServerToolResultBlockSchema,
33
33
  UIToolResultBlockSchema
34
- ]), UIMessageSchema = z.object({
34
+ ]), UIUsageSchema = z.object({
35
+ inputTokens: z.number(),
36
+ outputTokens: z.number(),
37
+ cacheCreationInputTokens: z.number().optional(),
38
+ cacheReadInputTokens: z.number().optional(),
39
+ reasoningTokens: z.number().optional()
40
+ }), UIMessageMetaSchema = z.object({
41
+ model: z.string().optional(),
42
+ usage: UIUsageSchema.optional(),
43
+ costUsd: z.number().optional(),
44
+ numTurns: z.number().optional(),
45
+ durationMs: z.number().optional()
46
+ }), UIMessageSchema = z.object({
35
47
  role: z.enum(["user", "assistant"]),
36
48
  text: z.string().optional(),
37
- blocks: z.array(UIContentBlockSchema).optional()
49
+ blocks: z.array(UIContentBlockSchema).optional(),
50
+ meta: UIMessageMetaSchema.optional()
38
51
  });
39
52
  export { UIMessageSchema };
@@ -9,10 +9,10 @@ import ErrorSnackbar_default from "../components/feedback/ErrorSnackbar.js";
9
9
  import { ReactFlowProvider } from "../node_modules/@xyflow/react/dist/esm/index.js";
10
10
  import WorkflowFlowViewer_default from "../features/debug/components/WorkflowFlowViewer.js";
11
11
  import "../features/debug/index.js";
12
+ import config_default from "../config.js";
12
13
  import WorkflowItem_default from "../features/workbench/WorkflowItem.js";
13
14
  import WorkflowHistoryList_default from "../features/workbench/components/WorkflowHistoryList.js";
14
15
  import { NewRunDialog } from "../features/workbench/components/NewRunDialog.js";
15
- import config_default from "../config.js";
16
16
  import "../features/workbench/index.js";
17
17
  import { c } from "react/compiler-runtime";
18
18
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
@@ -46,27 +46,27 @@ function PreviewWorkbenchPage() {
46
46
  }), e[3] = t, e[4] = o), o;
47
47
  }
48
48
  function PreviewWorkbenchContent(n) {
49
- let i = c(49), { workflowId: m, onNewRunSuccess: h } = n, _ = useRef(null), [y, b] = useState("output"), [x, S] = useState(!1), w = useWorkflow(m), T = useRef(!1), O = useWorkflowConfigByName(w.data?.workflowName), A, j;
50
- i[0] !== w.data || i[1] !== m ? (A = () => {
49
+ let i = c(49), { workflowId: d, onNewRunSuccess: h } = n, _ = useRef(null), [y, b] = useState("output"), [x, S] = useState(!1), w = useWorkflow(d), T = useRef(!1), O = useWorkflowConfigByName(w.data?.workflowName), A, j;
50
+ i[0] !== w.data || i[1] !== d ? (A = () => {
51
51
  !w.data || T.current || w.data.status === WorkflowState.Completed && window.parent !== window && (T.current = !0, window.parent.postMessage({
52
52
  type: EMBED_MESSAGE_TYPE,
53
- workflowId: m
53
+ workflowId: d
54
54
  }, window.location.origin));
55
- }, j = [w.data, m], i[0] = w.data, i[1] = m, i[2] = A, i[3] = j) : (A = i[2], j = i[3]), useEffect(A, j);
55
+ }, j = [w.data, d], i[0] = w.data, i[1] = d, i[2] = A, i[3] = j) : (A = i[2], j = i[3]), useEffect(A, j);
56
56
  let M, N;
57
- i[4] === m ? (M = i[5], N = i[6]) : (M = () => {
57
+ i[4] === d ? (M = i[5], N = i[6]) : (M = () => {
58
58
  if (window.parent === window || !_.current) return;
59
59
  let e = new ResizeObserver(() => {
60
60
  if (!_.current) return;
61
61
  let e = Math.max(document.body.scrollHeight, document.body.offsetHeight, document.documentElement.scrollHeight, document.documentElement.offsetHeight);
62
62
  window.parent.postMessage({
63
63
  type: EMBED_RESIZE_MESSAGE_TYPE,
64
- workflowId: m,
64
+ workflowId: d,
65
65
  height: e
66
66
  }, window.location.origin);
67
67
  });
68
68
  return e.observe(_.current), () => e.disconnect();
69
- }, N = [m], i[4] = m, i[5] = M, i[6] = N), useEffect(M, N);
69
+ }, N = [d], i[4] = d, i[5] = M, i[6] = N), useEffect(M, N);
70
70
  let P;
71
71
  i[7] === h ? P = i[8] : (P = (e) => {
72
72
  S(!1), h(e);
@@ -143,12 +143,12 @@ function PreviewWorkbenchContent(n) {
143
143
  })
144
144
  }), i[21] = y, i[22] = w.data, i[23] = w.isLoading, i[24] = q) : q = i[24];
145
145
  let J;
146
- i[25] !== y || i[26] !== w.data || i[27] !== w.isLoading || i[28] !== O || i[29] !== m ? (J = y === "graph" && /* @__PURE__ */ jsx("div", {
146
+ i[25] !== y || i[26] !== w.data || i[27] !== w.isLoading || i[28] !== O || i[29] !== d ? (J = y === "graph" && /* @__PURE__ */ jsx("div", {
147
147
  className: "h-full",
148
148
  children: /* @__PURE__ */ jsx(LoadingCentered_default, {
149
149
  loading: w.isLoading,
150
150
  children: w.data ? /* @__PURE__ */ jsx(ReactFlowProvider, { children: /* @__PURE__ */ jsx(WorkflowFlowViewer_default, {
151
- workflowId: m,
151
+ workflowId: d,
152
152
  workflows: [w.data],
153
153
  workflowConfig: O.data,
154
154
  direction: "TB"
@@ -160,7 +160,7 @@ function PreviewWorkbenchContent(n) {
160
160
  })
161
161
  })
162
162
  })
163
- }), i[25] = y, i[26] = w.data, i[27] = w.isLoading, i[28] = O, i[29] = m, i[30] = J) : J = i[30];
163
+ }), i[25] = y, i[26] = w.data, i[27] = w.isLoading, i[28] = O, i[29] = d, i[30] = J) : J = i[30];
164
164
  let Y;
165
165
  i[31] !== y || i[32] !== w.data ? (Y = y === "run-log" && /* @__PURE__ */ jsx("div", {
166
166
  className: "px-4",
@@ -206,9 +206,9 @@ var STATUS_DOT_COLORS = {
206
206
  pending: "bg-muted-foreground"
207
207
  };
208
208
  function PreviewEmptyState(e) {
209
- let t = c(24), { newRunDialogOpen: r, onNewRunDialogOpenChange: o, onNewRunSuccess: s } = e, l = useNavigate(), { router: u } = useStudio(), [d, f] = useState(3), m;
210
- t[0] === Symbol.for("react.memo_cache_sentinel") ? (m = { parentId: null }, t[0] = m) : m = t[0];
211
- let h = useFilterWorkflows(void 0, m, "createdAt", "DESC", 0, d), g;
209
+ let t = c(24), { newRunDialogOpen: r, onNewRunDialogOpenChange: o, onNewRunSuccess: s } = e, l = useNavigate(), { router: u } = useStudio(), [d, f] = useState(3), p;
210
+ t[0] === Symbol.for("react.memo_cache_sentinel") ? (p = { parentId: null }, t[0] = p) : p = t[0];
211
+ let h = useFilterWorkflows(void 0, p, "createdAt", "DESC", 0, d), g;
212
212
  t[1] === h.data?.data ? g = t[2] : (g = h.data?.data ?? [], t[1] = h.data?.data, t[2] = g);
213
213
  let _ = g, se = h.data?.total ?? 0, y = _.length < se, x;
214
214
  t[3] === o ? x = t[4] : (x = () => o(!0), t[3] = o, t[4] = x);
@@ -358,19 +358,19 @@ function EmbedLogsContent() {
358
358
  className: "text-xs font-medium text-muted-foreground",
359
359
  children: "Application Logs"
360
360
  }), e[6] = u) : u = e[6];
361
- let d;
362
- e[7] === i ? d = e[8] : (d = () => void i.refetch(), e[7] = i, e[8] = d);
363
- let f = `text-muted-foreground hover:text-foreground rounded-md p-1 transition-colors hover:cursor-pointer ${i.isFetching ? "animate-spin" : ""}`, p;
364
- e[9] === Symbol.for("react.memo_cache_sentinel") ? (p = /* @__PURE__ */ jsx(RefreshCw, { className: "h-3 w-3" }), e[9] = p) : p = e[9];
361
+ let f;
362
+ e[7] === i ? f = e[8] : (f = () => void i.refetch(), e[7] = i, e[8] = f);
363
+ let p = `text-muted-foreground hover:text-foreground rounded-md p-1 transition-colors hover:cursor-pointer ${i.isFetching ? "animate-spin" : ""}`, m;
364
+ e[9] === Symbol.for("react.memo_cache_sentinel") ? (m = /* @__PURE__ */ jsx(RefreshCw, { className: "h-3 w-3" }), e[9] = m) : m = e[9];
365
365
  let h;
366
- e[10] !== d || e[11] !== f ? (h = /* @__PURE__ */ jsxs("div", {
366
+ e[10] !== f || e[11] !== p ? (h = /* @__PURE__ */ jsxs("div", {
367
367
  className: "flex h-9 shrink-0 items-center justify-between border-b bg-muted/30 px-3",
368
368
  children: [u, /* @__PURE__ */ jsx("button", {
369
- onClick: d,
370
- className: f,
371
- children: p
369
+ onClick: f,
370
+ className: p,
371
+ children: m
372
372
  })]
373
- }), e[10] = d, e[11] = f, e[12] = h) : h = e[12];
373
+ }), e[10] = f, e[11] = p, e[12] = h) : h = e[12];
374
374
  let _;
375
375
  e[13] !== l || e[14] !== i.data || e[15] !== i.error || e[16] !== i.isLoading ? (_ = /* @__PURE__ */ jsx("div", {
376
376
  className: "flex-1 overflow-auto bg-background",
@@ -4,7 +4,8 @@ import { jsx } from "react/jsx-runtime";
4
4
  var STORAGE_KEY = "loopstack:studio-preferences", DEFAULTS = {
5
5
  leftSidebarOpen: !1,
6
6
  activePanel: null,
7
- panelSizes: {}
7
+ panelSizes: {},
8
+ workbenchView: "classic"
8
9
  }, StudioPreferencesContext = createContext(null);
9
10
  function readFromStorage() {
10
11
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@loopstack/loopstack-studio",
3
- "version": "0.35.1",
3
+ "version": "0.37.0",
4
4
  "repository": "loopstack-ai/loopstack-studio",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -26,9 +26,9 @@
26
26
  "dependencies": {
27
27
  "@fontsource/roboto": "^5.2.10",
28
28
  "@hookform/resolvers": "^5.2.2",
29
- "@loopstack/client": "^0.37.1",
30
- "@loopstack/contracts": "^0.37.0",
31
- "@loopstack/react": "^1.0.1",
29
+ "@loopstack/client": "^0.39.0",
30
+ "@loopstack/contracts": "^0.39.0",
31
+ "@loopstack/react": "^3.0.0",
32
32
  "@radix-ui/react-accordion": "^1.2.12",
33
33
  "@radix-ui/react-alert-dialog": "^1.1.15",
34
34
  "@radix-ui/react-avatar": "^1.1.11",