@lobb-js/studio 0.7.2 → 0.8.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 (164) hide show
  1. package/package.json +2 -1
  2. package/src/App.svelte +5 -0
  3. package/src/app.css +124 -0
  4. package/src/lib/components/LlmButton.svelte +137 -0
  5. package/src/lib/components/Studio.svelte +129 -0
  6. package/src/lib/components/alertView.svelte +20 -0
  7. package/src/lib/components/breadCrumbs.svelte +60 -0
  8. package/src/lib/components/codeEditor.svelte +152 -0
  9. package/src/lib/components/combobox.svelte +92 -0
  10. package/src/lib/components/confirmationDialog/confirmationDialog.svelte +33 -0
  11. package/src/lib/components/confirmationDialog/store.svelte.ts +28 -0
  12. package/src/lib/components/createManyButton.svelte +109 -0
  13. package/src/lib/components/dataTable/childRecords.svelte +142 -0
  14. package/src/lib/components/dataTable/dataTable.svelte +225 -0
  15. package/src/lib/components/dataTable/fieldCell.svelte +77 -0
  16. package/src/lib/components/dataTable/filter.svelte +284 -0
  17. package/src/lib/components/dataTable/filterButton.svelte +39 -0
  18. package/src/lib/components/dataTable/footer.svelte +84 -0
  19. package/src/lib/components/dataTable/header.svelte +155 -0
  20. package/src/lib/components/dataTable/sort.svelte +173 -0
  21. package/src/lib/components/dataTable/sortButton.svelte +36 -0
  22. package/src/lib/components/dataTable/table.svelte +337 -0
  23. package/src/lib/components/dataTable/utils.ts +127 -0
  24. package/src/lib/components/detailView/create/children.svelte +70 -0
  25. package/src/lib/components/detailView/create/createDetailView.svelte +228 -0
  26. package/src/lib/components/detailView/create/createDetailViewButton.svelte +37 -0
  27. package/src/lib/components/detailView/create/createManyView.svelte +252 -0
  28. package/src/lib/components/detailView/create/subRecords.svelte +50 -0
  29. package/src/lib/components/detailView/detailViewForm.svelte +104 -0
  30. package/src/lib/components/detailView/fieldCustomInput.svelte +26 -0
  31. package/src/lib/components/detailView/fieldInput.svelte +258 -0
  32. package/src/lib/components/detailView/fieldInputReplacement.svelte +199 -0
  33. package/src/lib/components/detailView/store.svelte.ts +59 -0
  34. package/src/lib/components/detailView/update/children.svelte +96 -0
  35. package/src/lib/components/detailView/update/updateDetailView.svelte +176 -0
  36. package/src/lib/components/detailView/update/updateDetailViewButton.svelte +56 -0
  37. package/src/lib/components/detailView/utils.ts +176 -0
  38. package/src/lib/components/diffViewer.svelte +105 -0
  39. package/src/lib/components/drawer.svelte +28 -0
  40. package/src/lib/components/extensionsComponents.svelte +33 -0
  41. package/src/lib/components/foreingKeyInput.svelte +80 -0
  42. package/src/lib/components/header.svelte +45 -0
  43. package/src/lib/components/loadingTypesForMonacoEditor.ts +36 -0
  44. package/src/lib/components/miniSidebar.svelte +226 -0
  45. package/src/lib/components/rangeCalendarButton.svelte +257 -0
  46. package/src/lib/components/richTextEditor.svelte +284 -0
  47. package/src/lib/components/routes/collections/collection.svelte +57 -0
  48. package/src/lib/components/routes/collections/collections.svelte +45 -0
  49. package/src/lib/components/routes/data_model/dataModel.svelte +40 -0
  50. package/src/lib/components/routes/data_model/flow.css +22 -0
  51. package/src/lib/components/routes/data_model/flow.svelte +84 -0
  52. package/src/lib/components/routes/data_model/syncManager.svelte +94 -0
  53. package/src/lib/components/routes/data_model/utils.ts +35 -0
  54. package/src/lib/components/routes/extensions/extension.svelte +19 -0
  55. package/src/lib/components/routes/home.svelte +40 -0
  56. package/src/lib/components/routes/workflows/workflows.svelte +136 -0
  57. package/src/lib/components/selectRecord.svelte +130 -0
  58. package/src/lib/components/setServerPage.svelte +50 -0
  59. package/src/lib/components/sidebar/index.ts +4 -0
  60. package/src/lib/components/sidebar/sidebar.svelte +149 -0
  61. package/src/lib/components/sidebar/sidebarElements.svelte +144 -0
  62. package/src/lib/components/sidebar/sidebarTrigger.svelte +33 -0
  63. package/src/lib/components/singletone.svelte +71 -0
  64. package/src/lib/components/ui/accordion/accordion-content.svelte +22 -0
  65. package/src/lib/components/ui/accordion/accordion-item.svelte +12 -0
  66. package/src/lib/components/ui/accordion/accordion-trigger.svelte +31 -0
  67. package/src/lib/components/ui/accordion/index.ts +17 -0
  68. package/src/lib/components/ui/alert/alert-description.svelte +16 -0
  69. package/src/lib/components/ui/alert/alert-title.svelte +24 -0
  70. package/src/lib/components/ui/alert/alert.svelte +39 -0
  71. package/src/lib/components/ui/alert/index.ts +14 -0
  72. package/src/lib/components/ui/alert-dialog/alert-dialog-action.svelte +13 -0
  73. package/src/lib/components/ui/alert-dialog/alert-dialog-cancel.svelte +17 -0
  74. package/src/lib/components/ui/alert-dialog/alert-dialog-content.svelte +26 -0
  75. package/src/lib/components/ui/alert-dialog/alert-dialog-description.svelte +16 -0
  76. package/src/lib/components/ui/alert-dialog/alert-dialog-footer.svelte +20 -0
  77. package/src/lib/components/ui/alert-dialog/alert-dialog-header.svelte +20 -0
  78. package/src/lib/components/ui/alert-dialog/alert-dialog-overlay.svelte +19 -0
  79. package/src/lib/components/ui/alert-dialog/alert-dialog-title.svelte +18 -0
  80. package/src/lib/components/ui/alert-dialog/index.ts +40 -0
  81. package/src/lib/components/ui/breadcrumb/breadcrumb-ellipsis.svelte +23 -0
  82. package/src/lib/components/ui/breadcrumb/breadcrumb-item.svelte +16 -0
  83. package/src/lib/components/ui/breadcrumb/breadcrumb-link.svelte +31 -0
  84. package/src/lib/components/ui/breadcrumb/breadcrumb-list.svelte +23 -0
  85. package/src/lib/components/ui/breadcrumb/breadcrumb-page.svelte +23 -0
  86. package/src/lib/components/ui/breadcrumb/breadcrumb-separator.svelte +27 -0
  87. package/src/lib/components/ui/breadcrumb/breadcrumb.svelte +15 -0
  88. package/src/lib/components/ui/breadcrumb/index.ts +25 -0
  89. package/src/lib/components/ui/button/button.svelte +110 -0
  90. package/src/lib/components/ui/button/index.ts +17 -0
  91. package/src/lib/components/ui/checkbox/checkbox.svelte +35 -0
  92. package/src/lib/components/ui/checkbox/index.ts +6 -0
  93. package/src/lib/components/ui/command/command-dialog.svelte +35 -0
  94. package/src/lib/components/ui/command/command-empty.svelte +12 -0
  95. package/src/lib/components/ui/command/command-group.svelte +31 -0
  96. package/src/lib/components/ui/command/command-input.svelte +25 -0
  97. package/src/lib/components/ui/command/command-item.svelte +19 -0
  98. package/src/lib/components/ui/command/command-link-item.svelte +19 -0
  99. package/src/lib/components/ui/command/command-list.svelte +16 -0
  100. package/src/lib/components/ui/command/command-separator.svelte +12 -0
  101. package/src/lib/components/ui/command/command-shortcut.svelte +20 -0
  102. package/src/lib/components/ui/command/command.svelte +21 -0
  103. package/src/lib/components/ui/command/index.ts +40 -0
  104. package/src/lib/components/ui/dialog/dialog-content.svelte +38 -0
  105. package/src/lib/components/ui/dialog/dialog-description.svelte +16 -0
  106. package/src/lib/components/ui/dialog/dialog-footer.svelte +20 -0
  107. package/src/lib/components/ui/dialog/dialog-header.svelte +20 -0
  108. package/src/lib/components/ui/dialog/dialog-overlay.svelte +19 -0
  109. package/src/lib/components/ui/dialog/dialog-title.svelte +16 -0
  110. package/src/lib/components/ui/dialog/index.ts +37 -0
  111. package/src/lib/components/ui/input/index.ts +7 -0
  112. package/src/lib/components/ui/input/input.svelte +46 -0
  113. package/src/lib/components/ui/label/index.ts +7 -0
  114. package/src/lib/components/ui/label/label.svelte +19 -0
  115. package/src/lib/components/ui/popover/index.ts +17 -0
  116. package/src/lib/components/ui/popover/popover-content.svelte +28 -0
  117. package/src/lib/components/ui/range-calendar/index.ts +30 -0
  118. package/src/lib/components/ui/range-calendar/range-calendar-cell.svelte +19 -0
  119. package/src/lib/components/ui/range-calendar/range-calendar-day.svelte +35 -0
  120. package/src/lib/components/ui/range-calendar/range-calendar-grid-body.svelte +12 -0
  121. package/src/lib/components/ui/range-calendar/range-calendar-grid-head.svelte +12 -0
  122. package/src/lib/components/ui/range-calendar/range-calendar-grid-row.svelte +12 -0
  123. package/src/lib/components/ui/range-calendar/range-calendar-grid.svelte +16 -0
  124. package/src/lib/components/ui/range-calendar/range-calendar-head-cell.svelte +16 -0
  125. package/src/lib/components/ui/range-calendar/range-calendar-header.svelte +16 -0
  126. package/src/lib/components/ui/range-calendar/range-calendar-heading.svelte +16 -0
  127. package/src/lib/components/ui/range-calendar/range-calendar-months.svelte +20 -0
  128. package/src/lib/components/ui/range-calendar/range-calendar-next-button.svelte +27 -0
  129. package/src/lib/components/ui/range-calendar/range-calendar-prev-button.svelte +27 -0
  130. package/src/lib/components/ui/range-calendar/range-calendar.svelte +57 -0
  131. package/src/lib/components/ui/select/index.ts +34 -0
  132. package/src/lib/components/ui/select/select-content.svelte +38 -0
  133. package/src/lib/components/ui/select/select-group-heading.svelte +16 -0
  134. package/src/lib/components/ui/select/select-item.svelte +37 -0
  135. package/src/lib/components/ui/select/select-scroll-down-button.svelte +19 -0
  136. package/src/lib/components/ui/select/select-scroll-up-button.svelte +19 -0
  137. package/src/lib/components/ui/select/select-separator.svelte +13 -0
  138. package/src/lib/components/ui/select/select-trigger.svelte +24 -0
  139. package/src/lib/components/ui/separator/index.ts +7 -0
  140. package/src/lib/components/ui/separator/separator.svelte +22 -0
  141. package/src/lib/components/ui/skeleton/index.ts +7 -0
  142. package/src/lib/components/ui/skeleton/skeleton.svelte +22 -0
  143. package/src/lib/components/ui/sonner/index.ts +1 -0
  144. package/src/lib/components/ui/sonner/sonner.svelte +20 -0
  145. package/src/lib/components/ui/switch/index.ts +7 -0
  146. package/src/lib/components/ui/switch/switch.svelte +27 -0
  147. package/src/lib/components/ui/textarea/index.ts +7 -0
  148. package/src/lib/components/ui/textarea/textarea.svelte +22 -0
  149. package/src/lib/components/ui/tooltip/index.ts +18 -0
  150. package/src/lib/components/ui/tooltip/tooltip-content.svelte +21 -0
  151. package/src/lib/components/workflowEditor.svelte +188 -0
  152. package/src/lib/context.ts +22 -0
  153. package/src/lib/eventSystem.ts +40 -0
  154. package/src/lib/extensions/extension.types.ts +92 -0
  155. package/src/lib/extensions/extensionUtils.ts +156 -0
  156. package/src/lib/index.ts +24 -0
  157. package/src/lib/store.svelte.ts +13 -0
  158. package/src/lib/store.types.ts +28 -0
  159. package/src/lib/utils.ts +68 -0
  160. package/src/main.ts +18 -0
  161. package/src/stories/detailView/detailViewForm.stories.svelte +79 -0
  162. package/src/vite-env.d.ts +2 -0
  163. package/vite-plugins/index.js +2 -0
  164. package/vite-plugins/lobb-proxy.js +36 -0
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "@lobb-js/studio",
3
- "version": "0.7.2",
3
+ "version": "0.8.0",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
8
8
  "files": [
9
+ "src",
9
10
  "dist",
10
11
  "vite-plugins"
11
12
  ],
package/src/App.svelte ADDED
@@ -0,0 +1,5 @@
1
+ <script lang="ts">
2
+ import Studio from "./lib/components/Studio.svelte";
3
+ </script>
4
+
5
+ <Studio />
package/src/app.css ADDED
@@ -0,0 +1,124 @@
1
+ @import "tailwindcss";
2
+
3
+ @import "tw-animate-css";
4
+
5
+ @source "../../../packages";
6
+ @source "../node_modules/@lobb-js";
7
+
8
+ @custom-variant dark (&:is(.dark *));
9
+
10
+ :root {
11
+ --radius: 0.625rem;
12
+ --background: oklch(1 0 0);
13
+ --foreground: oklch(0.129 0.042 264.695);
14
+ --card: oklch(1 0 0);
15
+ --card-foreground: oklch(0.129 0.042 264.695);
16
+ --popover: oklch(1 0 0);
17
+ --popover-foreground: oklch(0.129 0.042 264.695);
18
+ --primary: oklch(0.208 0.042 265.755);
19
+ --primary-foreground: oklch(0.984 0.003 247.858);
20
+ --secondary: oklch(0.968 0.007 247.896);
21
+ --secondary-foreground: oklch(0.208 0.042 265.755);
22
+ --muted: oklch(0.968 0.007 247.896);
23
+ --muted-foreground: oklch(0.554 0.046 257.417);
24
+ --accent: oklch(0.968 0.007 247.896);
25
+ --accent-foreground: oklch(0.208 0.042 265.755);
26
+ --destructive: oklch(0.577 0.245 27.325);
27
+ --border: oklch(0.929 0.013 255.508);
28
+ --input: oklch(0.929 0.013 255.508);
29
+ --ring: oklch(0.704 0.04 256.788);
30
+ --chart-1: oklch(0.646 0.222 41.116);
31
+ --chart-2: oklch(0.6 0.118 184.704);
32
+ --chart-3: oklch(0.398 0.07 227.392);
33
+ --chart-4: oklch(0.828 0.189 84.429);
34
+ --chart-5: oklch(0.769 0.188 70.08);
35
+ --sidebar: oklch(0.984 0.003 247.858);
36
+ --sidebar-foreground: oklch(0.129 0.042 264.695);
37
+ --sidebar-primary: oklch(0.208 0.042 265.755);
38
+ --sidebar-primary-foreground: oklch(0.984 0.003 247.858);
39
+ --sidebar-accent: oklch(0.968 0.007 247.896);
40
+ --sidebar-accent-foreground: oklch(0.208 0.042 265.755);
41
+ --sidebar-border: oklch(0.929 0.013 255.508);
42
+ --sidebar-ring: oklch(0.704 0.04 256.788);
43
+ }
44
+
45
+ .dark {
46
+ --background: oklch(0.129 0.042 264.695);
47
+ --foreground: oklch(0.984 0.003 247.858);
48
+ --card: oklch(0.208 0.042 265.755);
49
+ --card-foreground: oklch(0.984 0.003 247.858);
50
+ --popover: oklch(0.208 0.042 265.755);
51
+ --popover-foreground: oklch(0.984 0.003 247.858);
52
+ --primary: oklch(0.929 0.013 255.508);
53
+ --primary-foreground: oklch(0.208 0.042 265.755);
54
+ --secondary: oklch(0.279 0.041 260.031);
55
+ --secondary-foreground: oklch(0.984 0.003 247.858);
56
+ --muted: oklch(0.279 0.041 260.031);
57
+ --muted-foreground: oklch(0.704 0.04 256.788);
58
+ --accent: oklch(0.279 0.041 260.031);
59
+ --accent-foreground: oklch(0.984 0.003 247.858);
60
+ --destructive: oklch(0.704 0.191 22.216);
61
+ --border: oklch(1 0 0 / 10%);
62
+ --input: oklch(1 0 0 / 15%);
63
+ --ring: oklch(0.551 0.027 264.364);
64
+ --chart-1: oklch(0.488 0.243 264.376);
65
+ --chart-2: oklch(0.696 0.17 162.48);
66
+ --chart-3: oklch(0.769 0.188 70.08);
67
+ --chart-4: oklch(0.627 0.265 303.9);
68
+ --chart-5: oklch(0.645 0.246 16.439);
69
+ --sidebar: oklch(0.208 0.042 265.755);
70
+ --sidebar-foreground: oklch(0.984 0.003 247.858);
71
+ --sidebar-primary: oklch(0.488 0.243 264.376);
72
+ --sidebar-primary-foreground: oklch(0.984 0.003 247.858);
73
+ --sidebar-accent: oklch(0.279 0.041 260.031);
74
+ --sidebar-accent-foreground: oklch(0.984 0.003 247.858);
75
+ --sidebar-border: oklch(1 0 0 / 10%);
76
+ --sidebar-ring: oklch(0.551 0.027 264.364);
77
+ }
78
+
79
+ @theme inline {
80
+ --radius-sm: calc(var(--radius) - 4px);
81
+ --radius-md: calc(var(--radius) - 2px);
82
+ --radius-lg: var(--radius);
83
+ --radius-xl: calc(var(--radius) + 4px);
84
+ --color-background: var(--background);
85
+ --color-foreground: var(--foreground);
86
+ --color-card: var(--card);
87
+ --color-card-foreground: var(--card-foreground);
88
+ --color-popover: var(--popover);
89
+ --color-popover-foreground: var(--popover-foreground);
90
+ --color-primary: var(--primary);
91
+ --color-primary-foreground: var(--primary-foreground);
92
+ --color-secondary: var(--secondary);
93
+ --color-secondary-foreground: var(--secondary-foreground);
94
+ --color-muted: var(--muted);
95
+ --color-muted-foreground: var(--muted-foreground);
96
+ --color-accent: var(--accent);
97
+ --color-accent-foreground: var(--accent-foreground);
98
+ --color-destructive: var(--destructive);
99
+ --color-border: var(--border);
100
+ --color-input: var(--input);
101
+ --color-ring: var(--ring);
102
+ --color-chart-1: var(--chart-1);
103
+ --color-chart-2: var(--chart-2);
104
+ --color-chart-3: var(--chart-3);
105
+ --color-chart-4: var(--chart-4);
106
+ --color-chart-5: var(--chart-5);
107
+ --color-sidebar: var(--sidebar);
108
+ --color-sidebar-foreground: var(--sidebar-foreground);
109
+ --color-sidebar-primary: var(--sidebar-primary);
110
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
111
+ --color-sidebar-accent: var(--sidebar-accent);
112
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
113
+ --color-sidebar-border: var(--sidebar-border);
114
+ --color-sidebar-ring: var(--sidebar-ring);
115
+ }
116
+
117
+ @layer base {
118
+ * {
119
+ @apply border-border outline-ring/50;
120
+ }
121
+ body {
122
+ @apply bg-background text-foreground;
123
+ }
124
+ }
@@ -0,0 +1,137 @@
1
+ <script lang="ts">
2
+ import { getStudioContext } from "../context";
3
+ import { Brain, LoaderIcon, Send } from "lucide-svelte";
4
+
5
+ const { lobb, ctx } = getStudioContext();
6
+ import Button, { type ButtonProps } from "./ui/button/button.svelte";
7
+ import * as Popover from "./ui/popover";
8
+ import Textarea from "./ui/textarea/textarea.svelte";
9
+ import { toast } from "svelte-sonner";
10
+
11
+ interface LocalProp {
12
+ value?: any;
13
+ title: string;
14
+ description?: string;
15
+ placeholder?: string;
16
+ format?: any;
17
+ messages?: any[];
18
+ variant?: ButtonProps["variant"];
19
+ class?: ButtonProps["class"];
20
+ Icon?: ButtonProps["Icon"];
21
+ children?: ButtonProps["children"];
22
+ onApiResponseComplete?: (res: any) => Promise<void>;
23
+ }
24
+
25
+ let {
26
+ value = $bindable(),
27
+ title,
28
+ description,
29
+ placeholder = "write prompt description",
30
+ variant = "default",
31
+ Icon = Brain,
32
+ onApiResponseComplete,
33
+ messages,
34
+ format = { type: "text" },
35
+ ...props
36
+ }: LocalProp = $props();
37
+
38
+ let loading = $state(false);
39
+ let popoverOpen = $state(false);
40
+ let prompt = $state("");
41
+
42
+ let localMessages: any[] = [];
43
+ if (messages) {
44
+ localMessages.push(...messages);
45
+ }
46
+
47
+ async function handleSubmit(e: Event) {
48
+ e.preventDefault();
49
+ loading = true;
50
+ popoverOpen = false;
51
+ if (prompt) {
52
+ const response = await fetch(
53
+ `${lobb.lobbUrl}/api/collections/llm_chat`,
54
+ {
55
+ method: "POST",
56
+ headers: lobb.getHeaders(),
57
+ body: JSON.stringify({
58
+ stream: true,
59
+ format: format,
60
+ messages: [
61
+ ...localMessages,
62
+ {
63
+ role: "user",
64
+ content: prompt,
65
+ },
66
+ ],
67
+ }),
68
+ },
69
+ );
70
+ if (response.ok && response.body) {
71
+ value = "";
72
+ const reader = response.body.getReader();
73
+ const textDecoder = new TextDecoder();
74
+ let done = false;
75
+ while (!done) {
76
+ const { done: readerDone, ...stream } = await reader.read();
77
+ const llmValue = stream.value;
78
+ done = readerDone;
79
+ const chunk = textDecoder.decode(llmValue, {
80
+ stream: true,
81
+ });
82
+ value += chunk;
83
+ }
84
+ if (format.type === "json_object") {
85
+ value = JSON.stringify(JSON.parse(value), null, 2);
86
+ }
87
+ } else {
88
+ toast.error("Failed to fetch stream");
89
+ }
90
+ }
91
+ if (onApiResponseComplete) {
92
+ await onApiResponseComplete(value);
93
+ }
94
+ loading = false;
95
+ }
96
+ </script>
97
+
98
+ {#if ctx.meta.extensions.llm && ctx.meta.collections.llm_chat}
99
+ <Popover.Root bind:open={popoverOpen}>
100
+ <Popover.Trigger>
101
+ <Button {variant} class={props.class}>
102
+ {#if loading}
103
+ <LoaderIcon class="animate-spin" />
104
+ {:else}
105
+ <Icon />
106
+ {/if}
107
+ {#if props.children}
108
+ {@render props.children()}
109
+ {/if}
110
+ </Button>
111
+ </Popover.Trigger>
112
+ <Popover.Content collisionPadding={20} class="mt-2 w-screen max-w-[20rem]">
113
+ <form
114
+ onsubmit={handleSubmit}
115
+ class="flex flex-col items-start gap-4"
116
+ >
117
+ <div>
118
+ <div class="text-sm font-semibold">{title}</div>
119
+ <div class="text-xs text-muted-foreground">
120
+ {description}
121
+ </div>
122
+ </div>
123
+ <Textarea
124
+ bind:value={prompt}
125
+ placeholder="Type here..."
126
+ rows={5}
127
+ class="bg-muted text-xs"
128
+ />
129
+ <Button
130
+ type="submit"
131
+ Icon={Send}
132
+ class="h-7 px-3 text-xs font-normal">Submit</Button
133
+ >
134
+ </form>
135
+ </Popover.Content>
136
+ </Popover.Root>
137
+ {/if}
@@ -0,0 +1,129 @@
1
+ <script lang="ts">
2
+ import { Toaster } from "../components/ui/sonner";
3
+ import { onMount, onDestroy } from "svelte";
4
+ import { ModeWatcher } from "mode-watcher";
5
+ import { createLobb } from "../store.svelte";
6
+ import { setStudioContext } from "../context";
7
+ import Header from "../components/header.svelte";
8
+ import { LoaderCircle, ServerOff } from "lucide-svelte";
9
+ import MiniSidebar from "../components/miniSidebar.svelte";
10
+ import * as Tooltip from "../components/ui/tooltip";
11
+ import { Router, Route, Fallback, init as initRouter } from "@wjfe/n-savant";
12
+ import {
13
+ executeExtensionsOnStartup,
14
+ loadExtensions,
15
+ } from "../extensions/extensionUtils";
16
+ import extensionMap from 'virtual:lobb-studio-extensions';
17
+ import { mediaQueries } from "../utils";
18
+ import Home from "./routes/home.svelte";
19
+ import DataModel from "./routes/data_model/dataModel.svelte";
20
+ import Collections from "./routes/collections/collections.svelte";
21
+ import Workflows from "./routes/workflows/workflows.svelte";
22
+ import Extension from "./routes/extensions/extension.svelte";
23
+
24
+ interface StudioProps {
25
+ lobbUrl?: string;
26
+ }
27
+
28
+ const { lobbUrl }: StudioProps = $props();
29
+
30
+ const ctx = $state({
31
+ lobbUrl: lobbUrl ?? "",
32
+ extensions: {},
33
+ meta: null,
34
+ currentUrl: new URL(window.location.href),
35
+ });
36
+
37
+ const lobb = createLobb(lobbUrl ?? "");
38
+
39
+ setStudioContext({ ctx, lobb });
40
+
41
+ let status: "loading" | "error" | "ready" = $state("loading");
42
+ let isSmallScreen = $derived(!mediaQueries.sm.current);
43
+ let cleanupRouter: (() => void) | undefined;
44
+
45
+ onMount(async () => {
46
+ cleanupRouter = initRouter();
47
+ try {
48
+ ctx.meta = await lobb.getMeta();
49
+ ctx.extensions = await loadExtensions(lobb, ctx, extensionMap);
50
+ await executeExtensionsOnStartup(lobb, ctx);
51
+ status = "ready";
52
+ } catch (err) {
53
+ console.error(err);
54
+ status = "error";
55
+ }
56
+ });
57
+
58
+ onDestroy(() => {
59
+ if (cleanupRouter) cleanupRouter();
60
+ });
61
+ </script>
62
+
63
+ <ModeWatcher defaultMode="light" />
64
+ <Toaster position="top-right" class="z-50" />
65
+
66
+ {#if status === "loading"}
67
+ <div class="flex h-screen w-full flex-col items-center justify-center gap-4 text-muted-foreground">
68
+ <LoaderCircle class="animate-spin opacity-50" size="50" />
69
+ <div class="flex flex-col items-center justify-center">
70
+ <div>Loading the dashboard, please wait...</div>
71
+ <div class="text-xs">Loading and importing all necessary data and components from the lobb server.</div>
72
+ </div>
73
+ </div>
74
+ {:else if status === "error"}
75
+ <div class="flex h-screen w-full flex-col items-center justify-center gap-4 text-muted-foreground">
76
+ <ServerOff class="opacity-50" size="50" />
77
+ <div class="flex flex-col items-center justify-center">
78
+ <div>Could not connect to the server</div>
79
+ <div class="text-xs">Could not connect to the lobb server at this endpoint ({ctx.lobbUrl})</div>
80
+ </div>
81
+ </div>
82
+ {:else}
83
+ <Tooltip.Provider delayDuration={0} disableHoverableContent={true}>
84
+ <main
85
+ class="bg-muted h-screen w-screen"
86
+ style="display: grid; grid-template-columns: {isSmallScreen ? '1fr' : '3.5rem 1fr'};"
87
+ >
88
+ <MiniSidebar />
89
+ <div class="second_grid">
90
+ <Header />
91
+ <Router id="root-router" basePath="/studio">
92
+ <Route key="home" path="/">
93
+ {#snippet children(params)}
94
+ <Home />
95
+ {/snippet}
96
+ </Route>
97
+ <Route key="collections" path="/collections/:collection?">
98
+ {#snippet children(params)}
99
+ <Collections collectionName={params?.collection} />
100
+ {/snippet}
101
+ </Route>
102
+ <Route key="datamodel" path="/datamodel/*">
103
+ {#snippet children(params)}
104
+ <DataModel />
105
+ {/snippet}
106
+ </Route>
107
+ <Route key="workflows" path="/workflows/:workflow?">
108
+ {#snippet children(params)}
109
+ <Workflows workflowName={params?.workflow} />
110
+ {/snippet}
111
+ </Route>
112
+ <Route key="extensions" path="/extensions/:extension?/:page?/*">
113
+ {#snippet children(params)}
114
+ <Extension extension={params?.extension} page={params?.page} />
115
+ {/snippet}
116
+ </Route>
117
+ <Fallback>Not Found</Fallback>
118
+ </Router>
119
+ </div>
120
+ </main>
121
+ </Tooltip.Provider>
122
+ {/if}
123
+
124
+ <style>
125
+ .second_grid {
126
+ display: grid;
127
+ grid-template-rows: 2.5rem 1fr;
128
+ }
129
+ </style>
@@ -0,0 +1,20 @@
1
+ <script lang="ts">
2
+ import * as Alert from "./ui/alert/index.js";
3
+ import { AlertCircleIcon } from "lucide-svelte";
4
+ import type { Snippet } from "svelte";
5
+
6
+ interface Props {
7
+ title: string;
8
+ children: Snippet<[]>;
9
+ }
10
+
11
+ const { title, children }: Props = $props();
12
+ </script>
13
+
14
+ <Alert.Root variant="destructive">
15
+ <AlertCircleIcon />
16
+ <Alert.Title>{title}</Alert.Title>
17
+ <Alert.Description>
18
+ {@render children?.()}
19
+ </Alert.Description>
20
+ </Alert.Root>
@@ -0,0 +1,60 @@
1
+ <script lang="ts">
2
+ import * as Breadcrumb from "./ui/breadcrumb";
3
+ import { mediaQueries } from "../utils";
4
+ import { location } from "@wjfe/n-savant";
5
+
6
+ const isSmall = $derived(!mediaQueries.sm.current);
7
+ const pathNames = $derived(
8
+ location.url.pathname
9
+ .replace("/studio", "")
10
+ .split("/")
11
+ .filter((el: any) => el !== "")
12
+ .slice(0, 3),
13
+ );
14
+ </script>
15
+
16
+ {#if isSmall}
17
+ <div class="text-muted-foreground text-sm">
18
+ {pathNames[pathNames.length - 1] || "Home"}
19
+ </div>
20
+ {:else}
21
+ <Breadcrumb.Root>
22
+ <Breadcrumb.List class="flex-nowrap">
23
+ <Breadcrumb.Item>
24
+ {#if pathNames.length === 0}
25
+ <Breadcrumb.Page>Home</Breadcrumb.Page>
26
+ {:else}
27
+ <Breadcrumb.Link
28
+ class="cursor-pointer"
29
+ onclick={() => location.navigate("/studio")}
30
+ >
31
+ Home
32
+ </Breadcrumb.Link>
33
+ <Breadcrumb.Separator />
34
+ {/if}
35
+ </Breadcrumb.Item>
36
+ {#each pathNames as path, index}
37
+ {@const isLastElement = pathNames.length - 1 === index}
38
+ {@const currentFullPaths = pathNames
39
+ .slice(0, index + 1)
40
+ .join("/")}
41
+ <Breadcrumb.Item>
42
+ {#if isLastElement}
43
+ <Breadcrumb.Page>{path}</Breadcrumb.Page>
44
+ {:else}
45
+ <Breadcrumb.Link
46
+ class="cursor-pointer"
47
+ onclick={() =>
48
+ location.navigate(`/studio/${currentFullPaths}`)}
49
+ >
50
+ {path}
51
+ </Breadcrumb.Link>
52
+ {/if}
53
+ </Breadcrumb.Item>
54
+ {#if !isLastElement}
55
+ <Breadcrumb.Separator />
56
+ {/if}
57
+ {/each}
58
+ </Breadcrumb.List>
59
+ </Breadcrumb.Root>
60
+ {/if}
@@ -0,0 +1,152 @@
1
+ <script lang="ts">
2
+ import { onMount, onDestroy } from 'svelte';
3
+ import { keymap } from '@codemirror/view';
4
+ import { EditorState } from '@codemirror/state';
5
+ import { basicSetup, EditorView } from 'codemirror';
6
+ import { javascript } from '@codemirror/lang-javascript';
7
+ import { sql } from '@codemirror/lang-sql';
8
+ import { oneDark } from '@codemirror/theme-one-dark';
9
+ import { cn } from '../utils.js';
10
+
11
+ interface Props {
12
+ type: 'javascript' | 'typescript' | 'json' | 'sql';
13
+ name: string;
14
+ value?: string;
15
+ default?: string;
16
+ types?: string;
17
+ class?: string;
18
+ handleCtrlSave?: () => void;
19
+ onChange?: (value: string) => void;
20
+ }
21
+
22
+ let {
23
+ type,
24
+ name,
25
+ value = $bindable(''),
26
+ class: className,
27
+ types,
28
+ handleCtrlSave,
29
+ onChange,
30
+ ...props
31
+ }: Props = $props();
32
+
33
+ if (props.default) {
34
+ value = props.default;
35
+ }
36
+
37
+ let editorContainer: HTMLDivElement;
38
+ let editorView: EditorView | null = null;
39
+
40
+ const getLanguageExtension = () => {
41
+ switch (type) {
42
+ case 'javascript':
43
+ case 'typescript':
44
+ case 'json':
45
+ return javascript();
46
+ case 'sql':
47
+ return sql();
48
+ default:
49
+ return javascript();
50
+ }
51
+ };
52
+
53
+ const createEditor = () => {
54
+ if (!editorContainer) return;
55
+
56
+ const extensions = [
57
+ basicSetup,
58
+ getLanguageExtension(),
59
+ EditorView.updateListener.of((update) => {
60
+ if (update.docChanged) {
61
+ const newValue = update.state.doc.toString();
62
+ value = newValue;
63
+ onChange?.(newValue);
64
+ }
65
+ }),
66
+ EditorView.theme({
67
+ '&': {
68
+ backgroundColor: 'transparent',
69
+ },
70
+ '.cm-gutters': {
71
+ backgroundColor: 'transparent',
72
+ border: 'none',
73
+ },
74
+ '.cm-content': {
75
+ paddingTop: '10px',
76
+ paddingBottom: '1px',
77
+ }
78
+ })
79
+ ];
80
+
81
+ // Add Ctrl+S / Cmd+S handler if provided
82
+ if (handleCtrlSave) {
83
+ extensions.push(
84
+ keymap.of([
85
+ {
86
+ key: 'Mod-s',
87
+ run: () => {
88
+ handleCtrlSave();
89
+ return true;
90
+ }
91
+ }
92
+ ])
93
+ );
94
+ }
95
+
96
+ const state = EditorState.create({
97
+ doc: value,
98
+ extensions
99
+ });
100
+
101
+ editorView = new EditorView({
102
+ state,
103
+ parent: editorContainer
104
+ });
105
+ };
106
+
107
+ const updateEditor = () => {
108
+ if (!editorView) return;
109
+
110
+ const currentValue = editorView.state.doc.toString();
111
+ if (currentValue !== value) {
112
+ editorView.dispatch({
113
+ changes: {
114
+ from: 0,
115
+ to: currentValue.length,
116
+ insert: value
117
+ }
118
+ });
119
+ }
120
+ };
121
+
122
+ onMount(() => {
123
+ createEditor();
124
+ });
125
+
126
+ onDestroy(() => {
127
+ editorView?.destroy();
128
+ });
129
+
130
+ $effect(() => {
131
+ updateEditor();
132
+ });
133
+ </script>
134
+
135
+ <div class={cn('resize-y rounded-md border bg-muted/30 h-60', className)}>
136
+ <div bind:this={editorContainer} class="h-full w-full pl-2" />
137
+ </div>
138
+
139
+ <style>
140
+ :global(.cm-editor) {
141
+ height: 100%;
142
+ font-size: 14px;
143
+ }
144
+
145
+ :global(.cm-scroller) {
146
+ overflow: auto;
147
+ }
148
+
149
+ :global(.cm-focused) {
150
+ outline: none !important;
151
+ }
152
+ </style>