@lobb-js/studio 0.33.0 → 0.35.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 (119) hide show
  1. package/dist/applyUITheme.d.ts +2 -0
  2. package/dist/{applyStudioTheme.js → applyUITheme.js} +4 -4
  3. package/dist/components/LlmButton.svelte +4 -2
  4. package/dist/components/LlmButton.svelte.d.ts +1 -0
  5. package/dist/components/Studio.svelte +15 -7
  6. package/dist/components/codeEditor.svelte +1 -1
  7. package/dist/components/createManyButton.svelte +2 -2
  8. package/dist/components/dataTable/dataTable.svelte +1 -1
  9. package/dist/components/dataTable/dataTableTabs.svelte +1 -1
  10. package/dist/components/dataTable/filter.svelte +3 -2
  11. package/dist/components/dataTable/filterButton.svelte +1 -1
  12. package/dist/components/dataTable/footer.svelte +1 -1
  13. package/dist/components/dataTable/header.svelte +14 -21
  14. package/dist/components/dataTable/listViewChildren.svelte +1 -1
  15. package/dist/components/dataTable/sort.svelte +1 -1
  16. package/dist/components/dataTable/sortButton.svelte +1 -1
  17. package/dist/components/dataTable/table.svelte +8 -8
  18. package/dist/components/dataTable/utils.js +2 -1
  19. package/dist/components/dataTablePopup/dataTablePopup.svelte +1 -1
  20. package/dist/components/detailView/create/children.svelte +1 -1
  21. package/dist/components/detailView/create/createDetailView.svelte +2 -2
  22. package/dist/components/detailView/create/createDetailViewButton.svelte +2 -0
  23. package/dist/components/detailView/create/createDetailViewButton.svelte.d.ts +1 -0
  24. package/dist/components/detailView/create/createDetailViewChildren.svelte +4 -4
  25. package/dist/components/detailView/create/createManyView.svelte +4 -4
  26. package/dist/components/detailView/detailView.svelte +2 -1
  27. package/dist/components/detailView/fieldInput.svelte +10 -10
  28. package/dist/components/detailView/fieldInputReplacement.svelte +7 -7
  29. package/dist/components/detailView/passwordInput.svelte +1 -1
  30. package/dist/components/detailView/update/updateDetailView.svelte +2 -2
  31. package/dist/components/detailView/update/updateDetailViewButton.svelte +2 -0
  32. package/dist/components/detailView/update/updateDetailViewButton.svelte.d.ts +1 -0
  33. package/dist/components/detailView/update/updateDetailViewChildren.svelte +4 -4
  34. package/dist/components/diffViewer.svelte +1 -1
  35. package/dist/components/drawer.svelte +2 -2
  36. package/dist/components/foreingKeyInput.svelte +2 -2
  37. package/dist/components/horizontalNav.svelte +85 -0
  38. package/dist/components/horizontalNav.svelte.d.ts +3 -0
  39. package/dist/components/importButton.svelte +7 -7
  40. package/dist/components/mainNav.svelte +15 -0
  41. package/dist/components/mainNav.svelte.d.ts +6 -0
  42. package/dist/components/mainNavShared.d.ts +10 -0
  43. package/dist/components/mainNavShared.js +62 -0
  44. package/dist/components/polymorphicInput.svelte +1 -1
  45. package/dist/components/rangeCalendarButton.svelte +11 -12
  46. package/dist/components/richTextEditor.svelte +1 -1
  47. package/dist/components/routes/extensions/publicExtension.svelte +1 -1
  48. package/dist/components/routes/home.svelte +1 -1
  49. package/dist/components/routes/workflows/workflows.svelte +1 -1
  50. package/dist/components/setServerPage.svelte +1 -1
  51. package/dist/components/sidebar/sidebar.svelte +3 -3
  52. package/dist/components/sidebar/sidebarElements.svelte +4 -4
  53. package/dist/components/singletone.svelte +2 -2
  54. package/dist/components/ui/skeleton/skeleton.svelte +1 -1
  55. package/dist/components/ui/tooltip/tooltip-content.svelte +1 -1
  56. package/dist/components/verticalNav.svelte +174 -0
  57. package/dist/components/verticalNav.svelte.d.ts +3 -0
  58. package/dist/components/workflowEditor.svelte +2 -2
  59. package/dist/store.types.d.ts +4 -3
  60. package/dist/utils.d.ts +1 -0
  61. package/dist/utils.js +12 -0
  62. package/package.json +2 -2
  63. package/src/app.css +50 -76
  64. package/src/lib/{applyStudioTheme.ts → applyUITheme.ts} +5 -5
  65. package/src/lib/components/LlmButton.svelte +4 -2
  66. package/src/lib/components/Studio.svelte +15 -7
  67. package/src/lib/components/codeEditor.svelte +1 -1
  68. package/src/lib/components/createManyButton.svelte +2 -2
  69. package/src/lib/components/dataTable/dataTable.svelte +1 -1
  70. package/src/lib/components/dataTable/dataTableTabs.svelte +1 -1
  71. package/src/lib/components/dataTable/filter.svelte +3 -2
  72. package/src/lib/components/dataTable/filterButton.svelte +1 -1
  73. package/src/lib/components/dataTable/footer.svelte +1 -1
  74. package/src/lib/components/dataTable/header.svelte +14 -21
  75. package/src/lib/components/dataTable/listViewChildren.svelte +1 -1
  76. package/src/lib/components/dataTable/sort.svelte +1 -1
  77. package/src/lib/components/dataTable/sortButton.svelte +1 -1
  78. package/src/lib/components/dataTable/table.svelte +8 -8
  79. package/src/lib/components/dataTable/utils.ts +2 -1
  80. package/src/lib/components/dataTablePopup/dataTablePopup.svelte +1 -1
  81. package/src/lib/components/detailView/create/children.svelte +1 -1
  82. package/src/lib/components/detailView/create/createDetailView.svelte +2 -2
  83. package/src/lib/components/detailView/create/createDetailViewButton.svelte +2 -0
  84. package/src/lib/components/detailView/create/createDetailViewChildren.svelte +4 -4
  85. package/src/lib/components/detailView/create/createManyView.svelte +4 -4
  86. package/src/lib/components/detailView/detailView.svelte +2 -1
  87. package/src/lib/components/detailView/fieldInput.svelte +10 -10
  88. package/src/lib/components/detailView/fieldInputReplacement.svelte +7 -7
  89. package/src/lib/components/detailView/passwordInput.svelte +1 -1
  90. package/src/lib/components/detailView/update/updateDetailView.svelte +2 -2
  91. package/src/lib/components/detailView/update/updateDetailViewButton.svelte +2 -0
  92. package/src/lib/components/detailView/update/updateDetailViewChildren.svelte +4 -4
  93. package/src/lib/components/diffViewer.svelte +1 -1
  94. package/src/lib/components/drawer.svelte +2 -2
  95. package/src/lib/components/foreingKeyInput.svelte +2 -2
  96. package/src/lib/components/horizontalNav.svelte +85 -0
  97. package/src/lib/components/importButton.svelte +7 -7
  98. package/src/lib/components/mainNav.svelte +15 -0
  99. package/src/lib/components/mainNavShared.ts +67 -0
  100. package/src/lib/components/polymorphicInput.svelte +1 -1
  101. package/src/lib/components/rangeCalendarButton.svelte +11 -12
  102. package/src/lib/components/richTextEditor.svelte +1 -1
  103. package/src/lib/components/routes/extensions/publicExtension.svelte +1 -1
  104. package/src/lib/components/routes/home.svelte +1 -1
  105. package/src/lib/components/routes/workflows/workflows.svelte +1 -1
  106. package/src/lib/components/setServerPage.svelte +1 -1
  107. package/src/lib/components/sidebar/sidebar.svelte +3 -3
  108. package/src/lib/components/sidebar/sidebarElements.svelte +4 -4
  109. package/src/lib/components/singletone.svelte +2 -2
  110. package/src/lib/components/ui/skeleton/skeleton.svelte +1 -1
  111. package/src/lib/components/ui/tooltip/tooltip-content.svelte +1 -1
  112. package/src/lib/components/verticalNav.svelte +174 -0
  113. package/src/lib/components/workflowEditor.svelte +2 -2
  114. package/src/lib/store.types.ts +2 -2
  115. package/src/lib/utils.ts +12 -0
  116. package/dist/applyStudioTheme.d.ts +0 -2
  117. package/dist/components/miniSidebar.svelte +0 -300
  118. package/dist/components/miniSidebar.svelte.d.ts +0 -5
  119. package/src/lib/components/miniSidebar.svelte +0 -300
@@ -0,0 +1,85 @@
1
+ <script lang="ts">
2
+ import { onMount } from "svelte";
3
+ import { page } from "$app/state";
4
+ import Button from "./ui/button/button.svelte";
5
+ import Separator from "./ui/separator/separator.svelte";
6
+ import * as Popover from "./ui/popover";
7
+ import { getStudioContext } from "../context";
8
+ import { buildNavSections, isItemActive, type NavItem } from "./mainNavShared";
9
+
10
+ const { lobb, ctx } = getStudioContext();
11
+
12
+ let sections: NavItem[][] = $state([[], [], []]);
13
+ onMount(async () => {
14
+ sections = await buildNavSections(lobb, ctx);
15
+ });
16
+
17
+ const currentPath = $derived(page.url.pathname.replace(/\/$/, "") || "/");
18
+ </script>
19
+
20
+ {#snippet section(section: NavItem[])}
21
+ <div class="flex flex-row items-center gap-1">
22
+ {#each section as item}
23
+ {@const active = isItemActive(currentPath, item)}
24
+ {#if !item.navs}
25
+ <Button
26
+ onclick={() => { if (item.onclick) item.onclick(); }}
27
+ href={item.href}
28
+ class="h-8 px-2.5 text-xs font-normal {active
29
+ ? 'bg-accent text-foreground'
30
+ : 'text-foreground/70 hover:bg-accent/50 hover:text-foreground'}"
31
+ variant="ghost"
32
+ Icon={item.icon}
33
+ >
34
+ {item.label}
35
+ </Button>
36
+ {:else}
37
+ <Popover.Root>
38
+ <Popover.Trigger>
39
+ <Button
40
+ class="h-8 px-2.5 text-xs font-normal {active
41
+ ? 'bg-accent text-accent-foreground'
42
+ : 'text-muted-foreground'}"
43
+ variant="ghost"
44
+ Icon={item.icon}
45
+ >
46
+ {item.label}
47
+ </Button>
48
+ </Popover.Trigger>
49
+ <Popover.Content sideOffset={6} side="bottom" align="start" class="w-60 p-0">
50
+ <div class="py-1">
51
+ {#each item.navs as childItem}
52
+ {@const childActive = isItemActive(currentPath, childItem)}
53
+ <div class="px-1 text-xs text-muted-foreground">
54
+ <Button
55
+ variant="ghost"
56
+ class="flex h-7 w-full justify-start p-2 text-xs font-normal {childActive
57
+ ? 'bg-accent text-accent-foreground'
58
+ : 'text-muted-foreground'}"
59
+ Icon={childItem.icon}
60
+ onclick={() => { if (childItem.onclick) childItem.onclick(); }}
61
+ href={childItem.href}
62
+ >
63
+ {childItem.label}
64
+ </Button>
65
+ </div>
66
+ {/each}
67
+ </div>
68
+ </Popover.Content>
69
+ </Popover.Root>
70
+ {/if}
71
+ {/each}
72
+ </div>
73
+ {/snippet}
74
+
75
+ <div class="relative border-b border-border bg-card text-foreground h-12 w-full px-3 flex items-center justify-between gap-2">
76
+ <div class="flex flex-row items-center gap-2">
77
+ {@render section(sections[0])}
78
+ {#if sections[1].length > 0}
79
+ <Separator orientation="vertical" class="h-6 bg-border" />
80
+ {@render section(sections[1])}
81
+ {/if}
82
+ </div>
83
+
84
+ {@render section(sections[2])}
85
+ </div>
@@ -170,7 +170,7 @@
170
170
  }
171
171
  </script>
172
172
 
173
- <Button variant={rest.variant} class={rest.class} onclick={showDrawer}>
173
+ <Button variant={rest.variant} size={rest.size} class={rest.class} onclick={showDrawer}>
174
174
  <ExtensionsComponents
175
175
  name="collections.import.button.content"
176
176
  utils={getExtensionUtils(lobb, ctx)}
@@ -249,7 +249,7 @@
249
249
  />
250
250
  {:else}
251
251
  <textarea
252
- class="block h-56 w-full resize-none rounded-md border bg-muted-soft p-3 font-mono text-sm focus:outline-none focus:ring-1 focus:ring-ring"
252
+ class="block h-56 w-full resize-none rounded-md border bg-muted p-3 font-mono text-sm focus:outline-none focus:ring-1 focus:ring-ring"
253
253
  placeholder="Paste CSV or JSON here..."
254
254
  bind:value={pasteContent}
255
255
  ></textarea>
@@ -264,11 +264,11 @@
264
264
  </div>
265
265
 
266
266
  <div class="flex h-12 shrink-0 items-center justify-end gap-2 border-t px-4">
267
- <Button variant="outline" onclick={hideDrawer} class="h-7 px-3 text-xs font-normal" Icon={X}>
267
+ <Button variant="outline" onclick={hideDrawer} size="sm" Icon={X}>
268
268
  Cancel
269
269
  </Button>
270
270
  {#if activeTab === "paste"}
271
- <Button onclick={() => processContent(pasteContent)} class="h-7 px-3 text-xs font-normal" Icon={FileText}>
271
+ <Button onclick={() => processContent(pasteContent)} size="sm" Icon={FileText}>
272
272
  Parse
273
273
  </Button>
274
274
  {/if}
@@ -289,10 +289,10 @@
289
289
  </div>
290
290
 
291
291
  <div class="flex h-12 shrink-0 items-center justify-end gap-2 border-t px-4">
292
- <Button variant="outline" onclick={() => (step = "input")} class="h-7 px-3 text-xs font-normal" Icon={X}>
292
+ <Button variant="outline" onclick={() => (step = "input")} size="sm" Icon={X}>
293
293
  Cancel
294
294
  </Button>
295
- <Button onclick={handleImport} class="h-7 px-3 text-xs font-normal" Icon={Check}>
295
+ <Button onclick={handleImport} size="sm" Icon={Check}>
296
296
  Import {transformedRows.length} records
297
297
  </Button>
298
298
  </div>
@@ -345,7 +345,7 @@
345
345
  </div>
346
346
  {/if}
347
347
  <div class="flex h-12 shrink-0 items-center justify-end gap-2 border-t px-4">
348
- <Button onclick={hideDrawer} class="h-7 px-3 text-xs font-normal" Icon={Check}>
348
+ <Button onclick={hideDrawer} size="sm" Icon={Check}>
349
349
  Done
350
350
  </Button>
351
351
  </div>
@@ -0,0 +1,15 @@
1
+ <script lang="ts">
2
+ import VerticalNav from "./verticalNav.svelte";
3
+ import HorizontalNav from "./horizontalNav.svelte";
4
+
5
+ interface Props {
6
+ orientation?: "vertical" | "horizontal";
7
+ }
8
+ const { orientation = "vertical" }: Props = $props();
9
+ </script>
10
+
11
+ {#if orientation === "horizontal"}
12
+ <HorizontalNav />
13
+ {:else}
14
+ <VerticalNav />
15
+ {/if}
@@ -0,0 +1,67 @@
1
+ import { House, Layers, Library, Workflow } from "lucide-svelte";
2
+ import { emitEvent } from "../eventSystem";
3
+ import { getDashboardNavs } from "../extensions/extensionUtils";
4
+
5
+ export type NavItem = {
6
+ label: string;
7
+ href?: string;
8
+ icon: any;
9
+ represents?: string;
10
+ onclick?: () => void;
11
+ navs?: NavItem[];
12
+ };
13
+
14
+ async function isItemVisible(lobb: any, ctx: any, item: NavItem): Promise<boolean> {
15
+ if (!item.represents) return true;
16
+ const res = await emitEvent({ lobb, ctx }, "auth.canAccess", {
17
+ collection: item.represents,
18
+ action: "read",
19
+ });
20
+ return res === true;
21
+ }
22
+
23
+ export async function buildNavSections(lobb: any, ctx: any): Promise<NavItem[][]> {
24
+ const rawSections: NavItem[][] = [
25
+ [
26
+ { label: "Home", href: "/studio", icon: House },
27
+ { label: "Collections", href: "/studio/collections", icon: Library },
28
+ { label: "Data Model", href: "/studio/datamodel", icon: Layers, represents: "core_data_model" },
29
+ { label: "Workflows", href: "/studio/workflows", icon: Workflow, represents: "core_workflows" },
30
+ ],
31
+ [],
32
+ [],
33
+ ];
34
+
35
+ const navs = getDashboardNavs(ctx);
36
+ if (navs.top) rawSections[0].push(...(navs.top as NavItem[]));
37
+ if (navs.middle) rawSections[1].push(...(navs.middle as NavItem[]));
38
+ if (navs.bottom) rawSections[2].push(...(navs.bottom as NavItem[]));
39
+
40
+ const result: NavItem[][] = [[], [], []];
41
+ for (let i = 0; i < rawSections.length; i++) {
42
+ for (const item of rawSections[i]) {
43
+ if (item.navs) {
44
+ const visibleChildren: NavItem[] = [];
45
+ for (const child of item.navs) {
46
+ if (await isItemVisible(lobb, ctx, child)) visibleChildren.push(child);
47
+ }
48
+ if (visibleChildren.length && (await isItemVisible(lobb, ctx, item))) {
49
+ result[i].push({ ...item, navs: visibleChildren });
50
+ }
51
+ } else if (await isItemVisible(lobb, ctx, item)) {
52
+ result[i].push(item);
53
+ }
54
+ }
55
+ }
56
+ return result;
57
+ }
58
+
59
+ export function isItemActive(currentPath: string, item: NavItem): boolean {
60
+ if (item.navs) return item.navs.some((c) => isItemActive(currentPath, c));
61
+ if (!item.href) return false;
62
+ const itemHref = item.href.replace(/\/$/, "") || "/";
63
+ // "/studio" needs an exact match — it's a prefix of every other
64
+ // route, so startsWith would always light it up.
65
+ if (itemHref === "/studio") return currentPath === "/studio";
66
+ return currentPath === itemHref || currentPath.startsWith(itemHref + "/");
67
+ }
@@ -70,7 +70,7 @@
70
70
  }
71
71
  </script>
72
72
 
73
- <div class="flex h-9 w-full items-center gap-1.5 rounded-md border pl-1.5 pr-9 text-xs bg-muted-soft {destructive ? 'border-destructive bg-destructive/10' : ''}">
73
+ <div class="flex h-9 w-full items-center gap-1.5 rounded-md border pl-1.5 pr-9 text-xs bg-muted {destructive ? 'border-destructive bg-destructive/10' : ''}">
74
74
  <!-- Collection picker -->
75
75
  <Popover.Root bind:open={collectionPopoverOpen}>
76
76
  <Popover.Trigger>
@@ -33,9 +33,8 @@
33
33
  <Popover.Root>
34
34
  <Popover.Trigger
35
35
  class={cn(
36
- buttonVariants({ variant: "outline" }),
36
+ buttonVariants({ variant: "outline", size: "sm" }),
37
37
  !value && "text-muted-foreground",
38
- "h-7 px-3 text-xs font-normal",
39
38
  )}
40
39
  >
41
40
  <CalendarIcon class="mr-2 size-4" />
@@ -59,7 +58,7 @@
59
58
  class="flex flex-col overflow-hidden text-muted-foreground"
60
59
  >
61
60
  <button
62
- class="text-start text-sm py-2 px-2 hover:bg-muted-soft hover:text-primary"
61
+ class="text-start text-sm py-2 px-2 hover:bg-muted hover:text-primary"
63
62
  onclick={() => {
64
63
  const currentDate = today(getLocalTimeZone());
65
64
  value = {
@@ -71,7 +70,7 @@
71
70
  Today
72
71
  </button>
73
72
  <button
74
- class="text-start text-sm py-2 px-2 hover:bg-muted-soft hover:text-primary"
73
+ class="text-start text-sm py-2 px-2 hover:bg-muted hover:text-primary"
75
74
  onclick={() => {
76
75
  const currentDate = today(getLocalTimeZone());
77
76
  value = {
@@ -83,7 +82,7 @@
83
82
  Yesterday
84
83
  </button>
85
84
  <button
86
- class="text-start text-sm py-2 px-2 hover:bg-muted-soft hover:text-primary"
85
+ class="text-start text-sm py-2 px-2 hover:bg-muted hover:text-primary"
87
86
  onclick={() => {
88
87
  const currentDate = today(getLocalTimeZone());
89
88
  const weekStart = startOfWeek(currentDate, "en-US");
@@ -96,7 +95,7 @@
96
95
  This week (Sun - Today)
97
96
  </button>
98
97
  <button
99
- class="text-start text-sm py-2 px-2 hover:bg-muted-soft hover:text-primary"
98
+ class="text-start text-sm py-2 px-2 hover:bg-muted hover:text-primary"
100
99
  onclick={() => {
101
100
  const currentDate = today(getLocalTimeZone());
102
101
  const thisWeekStart = startOfWeek(
@@ -119,7 +118,7 @@
119
118
  Last week (Sun - Sat)
120
119
  </button>
121
120
  <button
122
- class="text-start text-sm py-2 px-2 hover:bg-muted-soft hover:text-primary"
121
+ class="text-start text-sm py-2 px-2 hover:bg-muted hover:text-primary"
123
122
  onclick={() => {
124
123
  const currentDate = today(getLocalTimeZone());
125
124
  value = {
@@ -131,7 +130,7 @@
131
130
  Last 7 days
132
131
  </button>
133
132
  <button
134
- class="text-start text-sm py-2 px-2 hover:bg-muted-soft hover:text-primary"
133
+ class="text-start text-sm py-2 px-2 hover:bg-muted hover:text-primary"
135
134
  onclick={() => {
136
135
  const currentDate = today(getLocalTimeZone());
137
136
  value = {
@@ -143,7 +142,7 @@
143
142
  Last 30 days
144
143
  </button>
145
144
  <button
146
- class="text-start text-sm py-2 px-2 hover:bg-muted-soft hover:text-primary"
145
+ class="text-start text-sm py-2 px-2 hover:bg-muted hover:text-primary"
147
146
  onclick={() => {
148
147
  const currentDate = today(getLocalTimeZone());
149
148
  value = {
@@ -155,7 +154,7 @@
155
154
  Last 90 days
156
155
  </button>
157
156
  <button
158
- class="text-start text-sm py-2 px-2 hover:bg-muted-soft hover:text-primary"
157
+ class="text-start text-sm py-2 px-2 hover:bg-muted hover:text-primary"
159
158
  onclick={() => {
160
159
  const currentDate = today(getLocalTimeZone());
161
160
  value = {
@@ -167,7 +166,7 @@
167
166
  Last 12 months
168
167
  </button>
169
168
  <button
170
- class="text-start text-sm py-2 px-2 hover:bg-muted-soft hover:text-primary"
169
+ class="text-start text-sm py-2 px-2 hover:bg-muted hover:text-primary"
171
170
  onclick={() => {
172
171
  const currentDate = today(getLocalTimeZone());
173
172
  const lastYearStart = currentDate
@@ -185,7 +184,7 @@
185
184
  Last Calendar year
186
185
  </button>
187
186
  <button
188
- class="text-start text-sm py-2 px-2 hover:bg-muted-soft hover:text-primary"
187
+ class="text-start text-sm py-2 px-2 hover:bg-muted hover:text-primary"
189
188
  onclick={() => {
190
189
  const currentDate = today(getLocalTimeZone());
191
190
  const yearStart = currentDate.set({
@@ -122,7 +122,7 @@
122
122
  }
123
123
  </script>
124
124
 
125
- <div class="flex flex-col rounded-md border bg-muted-soft">
125
+ <div class="flex flex-col rounded-md border bg-muted">
126
126
  <div class="flex flex-wrap items-center gap-0.5 border-b p-1.5">
127
127
  {#if editor}
128
128
  <button
@@ -8,7 +8,7 @@
8
8
  const { lobb, ctx } = getStudioContext();
9
9
  </script>
10
10
 
11
- <div class="grid h-full w-full overflow-auto bg-background">
11
+ <div class="grid h-full w-full overflow-hidden bg-background">
12
12
  {#key extension && page}
13
13
  <ExtensionsComponents
14
14
  name="publicPages.{page}"
@@ -33,7 +33,7 @@
33
33
  <Button
34
34
  Icon={ArrowRight}
35
35
  variant="outline"
36
- class="h-7 px-3 text-xs font-normal"
36
+ size="sm"
37
37
  onclick={() => goto("/studio/collections")}
38
38
  >
39
39
  Go to collections
@@ -103,7 +103,7 @@
103
103
  {#snippet belowSearch()}
104
104
  <div class="pb-4 px-2">
105
105
  <Button
106
- class="h-7 px-3 text-xs font-normal w-full"
106
+ size="sm" class="w-full"
107
107
  variant="outline"
108
108
  onclick={() => goto("/studio/workflows/new")}
109
109
  Icon={Plus}
@@ -34,7 +34,7 @@
34
34
  <div class="text-4xl">Welcome back</div>
35
35
  <div>Sign in to your account</div>
36
36
  </div>
37
- <div class="flex flex-col gap-6 rounded-md border bg-white p-6">
37
+ <div class="flex flex-col gap-6 rounded-md border bg-card p-6">
38
38
  <div class="flex flex-col gap-2">
39
39
  <div>
40
40
  <div class="mb-1 text-sm font-medium">Server</div>
@@ -90,7 +90,7 @@
90
90
  >
91
91
  <div
92
92
  class="
93
- bg-background border-r overflow-hidden
93
+ bg-card border-r overflow-hidden
94
94
  "
95
95
  style="
96
96
  {sidebarProperties.collapsed ? 'border-right-width: 0px; padding: 0px;' : ''}
@@ -107,7 +107,7 @@
107
107
  {#if showSearch}
108
108
  <div class="p-2">
109
109
  <div
110
- class="flex items-center px-4 py-1 text-muted-foreground bg-muted-soft border rounded-md"
110
+ class="flex items-center px-4 py-1 text-muted-foreground bg-muted border rounded-md"
111
111
  >
112
112
  <input
113
113
  type="text"
@@ -123,7 +123,7 @@
123
123
  </div>
124
124
  <div
125
125
  class="
126
- text-primary p-2 overflow-y-auto overflow-x-clip
126
+ text-foreground p-2 overflow-y-auto overflow-x-clip
127
127
  "
128
128
  style="max-height: {sidebarBodyHeight}px;"
129
129
  >
@@ -58,7 +58,7 @@
58
58
  {#each data as node, index}
59
59
  {#if node.type === "directory"}
60
60
  <button
61
- class="flex items-center justify-between p-2 gap-2 text-muted-foreground rounded-md hover:bg-muted-soft cursor-pointer"
61
+ class="flex items-center justify-between p-2 gap-2 text-muted-foreground rounded-md hover:bg-muted cursor-pointer"
62
62
  onclick={() => toggleDir(index)}
63
63
  >
64
64
  <div class="flex items-center gap-2">
@@ -89,8 +89,8 @@
89
89
  href={node.href}
90
90
  variant="ghost"
91
91
  class="
92
- flex items-center justify-between p-2 gap-2 hover:bg-muted-soft text-muted-foreground
93
- rounded-md {isselected ? 'bg-muted' : ''}
92
+ flex items-center justify-between p-2 gap-2 text-muted-foreground
93
+ rounded-md {isselected ? 'bg-accent' : 'hover:bg-muted'}
94
94
  "
95
95
  title={node.name}
96
96
  >
@@ -98,7 +98,7 @@
98
98
  {#if node.icon}
99
99
  <node.icon size="17.5" />
100
100
  {/if}
101
- <div class="text-xs {isselected ? 'text-primary font-medium' : ''}">
101
+ <div class="text-xs {isselected ? 'text-foreground font-medium' : ''}">
102
102
  {node.name}
103
103
  </div>
104
104
  </div>
@@ -39,12 +39,12 @@
39
39
  </script>
40
40
 
41
41
  <div class="flex flex-col h-full bg-background">
42
- <div class="flex justify-between items-center gap-2 p-2 border-b h-10 shrink-0">
42
+ <div class="flex justify-between items-center gap-2 p-2 border-b h-12 shrink-0">
43
43
  <div class="flex items-center gap-1">
44
44
  <SidebarTrigger />
45
45
  </div>
46
46
  <div>
47
- <Button class="h-7 px-2 font-normal text-xs" Icon={Save} onclick={handleSave}>
47
+ <Button size="sm" Icon={Save} onclick={handleSave}>
48
48
  Save
49
49
  </Button>
50
50
  </div>
@@ -15,7 +15,7 @@
15
15
  <div
16
16
  bind:this={ref}
17
17
  class={cn(
18
- "bg-primary/10 animate-pulse rounded-md",
18
+ "bg-muted animate-pulse rounded-md",
19
19
  className,
20
20
  )}
21
21
  {...restProps}
@@ -14,7 +14,7 @@
14
14
  bind:ref
15
15
  {sideOffset}
16
16
  class={cn(
17
- "bg-primary text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 overflow-hidden rounded-md px-3 py-1.5 text-xs",
17
+ "bg-popover text-popover-foreground border shadow-sm animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 overflow-hidden rounded-md px-3 py-1.5 text-xs",
18
18
  className
19
19
  )}
20
20
  {...restProps}
@@ -0,0 +1,174 @@
1
+ <script lang="ts">
2
+ import { X } from "lucide-svelte";
3
+ import { onMount } from "svelte";
4
+ import { page } from "$app/state";
5
+ import { goto } from "$app/navigation";
6
+ import Button from "./ui/button/button.svelte";
7
+ import Separator from "./ui/separator/separator.svelte";
8
+ import * as Tooltip from "./ui/tooltip";
9
+ import * as Accordion from "./ui/accordion/index.js";
10
+ import * as Popover from "./ui/popover";
11
+ import { getStudioContext } from "../context";
12
+ import { mediaQueries } from "../utils";
13
+ import { buildNavSections, isItemActive, type NavItem } from "./mainNavShared";
14
+
15
+ const { lobb, ctx } = getStudioContext();
16
+
17
+ let isSmallScreen = $derived(!mediaQueries.sm.current);
18
+ let isCollapsed = $state(true);
19
+ $effect(() => {
20
+ isCollapsed = isSmallScreen;
21
+ });
22
+
23
+ let sections: NavItem[][] = $state([[], [], []]);
24
+ onMount(async () => {
25
+ sections = await buildNavSections(lobb, ctx);
26
+ });
27
+
28
+ const currentPath = $derived(page.url.pathname.replace(/\/$/, "") || "/");
29
+ </script>
30
+
31
+ {#snippet section(section: NavItem[])}
32
+ <div class="flex flex-col {isSmallScreen ? 'gap-0' : 'gap-2'}">
33
+ {#each section as item}
34
+ {@const active = isItemActive(currentPath, item)}
35
+ {#if isSmallScreen}
36
+ {#if !item.navs}
37
+ <Button
38
+ onclick={() => {
39
+ if (item.onclick) item.onclick();
40
+ else if (item.href) goto(item.href);
41
+ isCollapsed = true;
42
+ }}
43
+ class="flex items-center justify-start flex-nowrap text-nowrap h-10 w-full {active
44
+ ? 'bg-accent text-foreground'
45
+ : 'text-foreground/70 hover:bg-accent/50 hover:text-foreground'}"
46
+ variant="ghost"
47
+ size="icon"
48
+ Icon={item.icon}
49
+ >
50
+ {item.label}
51
+ </Button>
52
+ {:else}
53
+ <Accordion.Root type="single">
54
+ <Accordion.Item class="border-b-0">
55
+ <Accordion.Trigger class="justify-between p-0 h-10">
56
+ <div
57
+ class="flex items-center gap-2 {active
58
+ ? 'text-accent-foreground'
59
+ : 'text-foreground/70'}"
60
+ >
61
+ <item.icon size="18" />
62
+ <div class="text-nowrap">{item.label}</div>
63
+ </div>
64
+ </Accordion.Trigger>
65
+ <Accordion.Content class="pl-2 border-l border-border">
66
+ {#each item.navs as childItem}
67
+ {@const childActive = isItemActive(currentPath, childItem)}
68
+ <Button
69
+ onclick={() => {
70
+ if (childItem.onclick) childItem.onclick();
71
+ else if (childItem.href) goto(childItem.href);
72
+ isCollapsed = true;
73
+ }}
74
+ class="flex items-center justify-start flex-nowrap text-nowrap h-8 w-full {childActive
75
+ ? 'bg-accent text-foreground'
76
+ : 'text-foreground/70 hover:bg-accent/50 hover:text-foreground'}"
77
+ variant="ghost"
78
+ size="icon"
79
+ Icon={childItem.icon}
80
+ >
81
+ {childItem.label}
82
+ </Button>
83
+ {/each}
84
+ </Accordion.Content>
85
+ </Accordion.Item>
86
+ </Accordion.Root>
87
+ {/if}
88
+ {:else}
89
+ <Tooltip.Root>
90
+ <Tooltip.Trigger>
91
+ {#if !item.navs}
92
+ <Button
93
+ onclick={() => { if (item.onclick) item.onclick(); }}
94
+ href={item.href}
95
+ class={active
96
+ ? 'bg-accent text-foreground'
97
+ : 'text-foreground/70 hover:bg-accent/50 hover:text-foreground'}
98
+ variant="ghost"
99
+ size="icon"
100
+ Icon={item.icon}
101
+ ></Button>
102
+ {:else}
103
+ <Popover.Root>
104
+ <Popover.Trigger>
105
+ <Button
106
+ class={active
107
+ ? 'bg-accent text-foreground'
108
+ : 'text-foreground/70 hover:bg-accent/50 hover:text-foreground'}
109
+ variant="ghost"
110
+ size="icon"
111
+ Icon={item.icon}
112
+ ></Button>
113
+ </Popover.Trigger>
114
+ <Popover.Content sideOffset={17.5} side="right" class="popover_content w-60 mb-4 p-0">
115
+ <div class="py-1">
116
+ {#each item.navs as childItem}
117
+ {@const childActive = isItemActive(currentPath, childItem)}
118
+ <div class="px-1 text-xs text-muted-foreground">
119
+ <Button
120
+ variant="ghost"
121
+ class="flex h-7 w-full justify-start p-2 text-xs font-normal {childActive
122
+ ? 'bg-accent text-accent-foreground'
123
+ : 'text-muted-foreground'}"
124
+ Icon={childItem.icon}
125
+ onclick={() => { if (childItem.onclick) childItem.onclick(); }}
126
+ href={childItem.href}
127
+ >
128
+ {childItem.label}
129
+ </Button>
130
+ </div>
131
+ {/each}
132
+ </div>
133
+ </Popover.Content>
134
+ </Popover.Root>
135
+ {/if}
136
+ </Tooltip.Trigger>
137
+ <Tooltip.Content side="right" sideOffset={15}>
138
+ {item.label}
139
+ </Tooltip.Content>
140
+ </Tooltip.Root>
141
+ {/if}
142
+ {/each}
143
+ </div>
144
+ {/snippet}
145
+
146
+ <div
147
+ class="
148
+ {isSmallScreen ? 'fixed top-0 left-0 h-full z-50' : 'relative'}
149
+ border-r border-border bg-card text-foreground w-screen
150
+ {isCollapsed
151
+ ? 'max-w-0 p-0'
152
+ : `max-w-14 ${isSmallScreen ? 'px-3 pb-3' : 'p-2'}`}"
153
+ style="transition: max-width 150ms, padding 150ms; {isSmallScreen && !isCollapsed ? 'max-width: 100vw' : ''}"
154
+ >
155
+ {#if isSmallScreen && !isCollapsed}
156
+ <X
157
+ class="absolute h-10 text-foreground/70 hover:bg-transparent cursor-pointer hover:text-foreground"
158
+ style="left: 0.6rem; top: 0rem;"
159
+ size="18"
160
+ onclick={() => (isCollapsed = !isCollapsed)}
161
+ />
162
+ {/if}
163
+ <div class="flex h-full flex-col justify-between gap-2 w-full overflow-hidden">
164
+ <div class="flex flex-col gap-2 {isSmallScreen ? 'pt-8' : ''}">
165
+ {@render section(sections[0])}
166
+ <Separator class="bg-border" />
167
+ {@render section(sections[1])}
168
+ </div>
169
+ <div class="flex flex-col gap-2">
170
+ <Separator class="bg-border" />
171
+ {@render section(sections[2])}
172
+ </div>
173
+ </div>
174
+ </div>
@@ -157,7 +157,7 @@
157
157
  <div>
158
158
  {#if workflow.id}
159
159
  <Button
160
- class="h-7 px-3 text-xs font-normal w-full"
160
+ size="sm" class="w-full"
161
161
  variant="default"
162
162
  onclick={handleUpdateWorkflow}
163
163
  Icon={Edit}
@@ -167,7 +167,7 @@
167
167
  </Button>
168
168
  {:else}
169
169
  <Button
170
- class="h-7 px-3 text-xs font-normal w-full"
170
+ size="sm" class="w-full"
171
171
  variant="default"
172
172
  onclick={handleCreateWorkflow}
173
173
  Icon={Plus}