@lobb-js/studio 0.7.2 → 0.7.3

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 (162) 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
@@ -0,0 +1,284 @@
1
+ <script lang="ts">
2
+ import { onMount, onDestroy } from 'svelte';
3
+ import { Editor } from '@tiptap/core';
4
+ import StarterKit from '@tiptap/starter-kit';
5
+ import UnderlineExt from '@tiptap/extension-underline';
6
+ import LinkExt from '@tiptap/extension-link';
7
+ import {
8
+ Bold,
9
+ Italic,
10
+ Underline,
11
+ Strikethrough,
12
+ Heading1,
13
+ Heading2,
14
+ Heading3,
15
+ List,
16
+ ListOrdered,
17
+ Quote,
18
+ Minus,
19
+ Undo2,
20
+ Redo2,
21
+ Link
22
+ } from 'lucide-svelte';
23
+ import { cn } from '../utils.js';
24
+
25
+ interface Props {
26
+ value?: string;
27
+ name?: string;
28
+ }
29
+
30
+ let { value = $bindable(''), name }: Props = $props();
31
+
32
+ let editorElement = $state<HTMLDivElement | undefined>(undefined);
33
+ let editor = $state<Editor | null>(null);
34
+
35
+ // Individual states for toolbar buttons.
36
+ // Updated in onUpdate/onSelectionUpdate — these callbacks never fire during
37
+ // the Editor constructor, so no $state writes happen mid-construction and
38
+ // the onMount effect cannot be re-queued by Svelte's scheduler.
39
+ let isBold = $state(false);
40
+ let isItalic = $state(false);
41
+ let isUnderline = $state(false);
42
+ let isStrike = $state(false);
43
+ let isH1 = $state(false);
44
+ let isH2 = $state(false);
45
+ let isH3 = $state(false);
46
+ let isBulletList = $state(false);
47
+ let isOrderedList = $state(false);
48
+ let isBlockquote = $state(false);
49
+ let isLink = $state(false);
50
+ let canUndo = $state(false);
51
+ let canRedo = $state(false);
52
+
53
+ function syncButtonStates(e: Editor) {
54
+ isBold = e.isActive('bold');
55
+ isItalic = e.isActive('italic');
56
+ isUnderline = e.isActive('underline');
57
+ isStrike = e.isActive('strike');
58
+ isH1 = e.isActive('heading', { level: 1 });
59
+ isH2 = e.isActive('heading', { level: 2 });
60
+ isH3 = e.isActive('heading', { level: 3 });
61
+ isBulletList = e.isActive('bulletList');
62
+ isOrderedList = e.isActive('orderedList');
63
+ isBlockquote = e.isActive('blockquote');
64
+ isLink = e.isActive('link');
65
+ canUndo = e.can().undo();
66
+ canRedo = e.can().redo();
67
+ }
68
+
69
+ onMount(() => {
70
+ const e = new Editor({
71
+ element: editorElement,
72
+ extensions: [
73
+ StarterKit.configure({
74
+ heading: { levels: [1, 2, 3] },
75
+ }),
76
+ UnderlineExt,
77
+ LinkExt.configure({ openOnClick: false }),
78
+ ],
79
+ content: value,
80
+ editorProps: {
81
+ attributes: { dir: 'auto' },
82
+ },
83
+ onUpdate: ({ editor: e }) => {
84
+ value = e.getHTML();
85
+ syncButtonStates(e);
86
+ },
87
+ onSelectionUpdate: ({ editor: e }) => {
88
+ syncButtonStates(e);
89
+ },
90
+ });
91
+ editor = e;
92
+ });
93
+
94
+ onDestroy(() => {
95
+ editor?.destroy();
96
+ });
97
+
98
+ // Syncs an externally-changed `value` prop into the editor.
99
+ // Treats TipTap's empty-doc representation ('<p></p>') as equivalent to ''
100
+ // so the effect doesn't endlessly call setContent on an empty editor.
101
+ $effect(() => {
102
+ if (!editor) return;
103
+ const incoming = value;
104
+ const current = editor.getHTML();
105
+ const normalizedCurrent = current === '<p></p>' ? '' : current;
106
+ if (normalizedCurrent !== incoming) {
107
+ editor.commands.setContent(incoming);
108
+ syncButtonStates(editor);
109
+ }
110
+ });
111
+
112
+ function handleLink() {
113
+ if (!editor) return;
114
+ if (editor.isActive('link')) {
115
+ editor.chain().focus().unsetLink().run();
116
+ } else {
117
+ const url = window.prompt('URL');
118
+ if (url) editor.chain().focus().setLink({ href: url }).run();
119
+ }
120
+ }
121
+ </script>
122
+
123
+ <div class="flex flex-col rounded-md border bg-muted/30">
124
+ <div class="flex flex-wrap items-center gap-0.5 border-b p-1.5">
125
+ {#if editor}
126
+ <button
127
+ type="button"
128
+ onclick={() => editor?.chain().focus().toggleBold().run()}
129
+ class={cn('rounded p-1.5 transition-colors hover:bg-muted', isBold ? 'bg-muted text-foreground' : 'text-muted-foreground')}
130
+ ><Bold size={14} /></button>
131
+
132
+ <button
133
+ type="button"
134
+ onclick={() => editor?.chain().focus().toggleItalic().run()}
135
+ class={cn('rounded p-1.5 transition-colors hover:bg-muted', isItalic ? 'bg-muted text-foreground' : 'text-muted-foreground')}
136
+ ><Italic size={14} /></button>
137
+
138
+ <button
139
+ type="button"
140
+ onclick={() => editor?.chain().focus().toggleUnderline().run()}
141
+ class={cn('rounded p-1.5 transition-colors hover:bg-muted', isUnderline ? 'bg-muted text-foreground' : 'text-muted-foreground')}
142
+ ><Underline size={14} /></button>
143
+
144
+ <button
145
+ type="button"
146
+ onclick={() => editor?.chain().focus().toggleStrike().run()}
147
+ class={cn('rounded p-1.5 transition-colors hover:bg-muted', isStrike ? 'bg-muted text-foreground' : 'text-muted-foreground')}
148
+ ><Strikethrough size={14} /></button>
149
+
150
+ <div class="mx-0.5 self-stretch w-px bg-border"></div>
151
+
152
+ <button
153
+ type="button"
154
+ onclick={() => editor?.chain().focus().toggleHeading({ level: 1 }).run()}
155
+ class={cn('rounded p-1.5 transition-colors hover:bg-muted', isH1 ? 'bg-muted text-foreground' : 'text-muted-foreground')}
156
+ ><Heading1 size={14} /></button>
157
+
158
+ <button
159
+ type="button"
160
+ onclick={() => editor?.chain().focus().toggleHeading({ level: 2 }).run()}
161
+ class={cn('rounded p-1.5 transition-colors hover:bg-muted', isH2 ? 'bg-muted text-foreground' : 'text-muted-foreground')}
162
+ ><Heading2 size={14} /></button>
163
+
164
+ <button
165
+ type="button"
166
+ onclick={() => editor?.chain().focus().toggleHeading({ level: 3 }).run()}
167
+ class={cn('rounded p-1.5 transition-colors hover:bg-muted', isH3 ? 'bg-muted text-foreground' : 'text-muted-foreground')}
168
+ ><Heading3 size={14} /></button>
169
+
170
+ <div class="mx-0.5 self-stretch w-px bg-border"></div>
171
+
172
+ <button
173
+ type="button"
174
+ onclick={() => editor?.chain().focus().toggleBulletList().run()}
175
+ class={cn('rounded p-1.5 transition-colors hover:bg-muted', isBulletList ? 'bg-muted text-foreground' : 'text-muted-foreground')}
176
+ ><List size={14} /></button>
177
+
178
+ <button
179
+ type="button"
180
+ onclick={() => editor?.chain().focus().toggleOrderedList().run()}
181
+ class={cn('rounded p-1.5 transition-colors hover:bg-muted', isOrderedList ? 'bg-muted text-foreground' : 'text-muted-foreground')}
182
+ ><ListOrdered size={14} /></button>
183
+
184
+ <button
185
+ type="button"
186
+ onclick={() => editor?.chain().focus().toggleBlockquote().run()}
187
+ class={cn('rounded p-1.5 transition-colors hover:bg-muted', isBlockquote ? 'bg-muted text-foreground' : 'text-muted-foreground')}
188
+ ><Quote size={14} /></button>
189
+
190
+ <button
191
+ type="button"
192
+ onclick={() => editor?.chain().focus().setHorizontalRule().run()}
193
+ class="rounded p-1.5 text-muted-foreground transition-colors hover:bg-muted"
194
+ ><Minus size={14} /></button>
195
+
196
+ <button
197
+ type="button"
198
+ onclick={handleLink}
199
+ class={cn('rounded p-1.5 transition-colors hover:bg-muted', isLink ? 'bg-muted text-foreground' : 'text-muted-foreground')}
200
+ ><Link size={14} /></button>
201
+
202
+ <div class="mx-0.5 self-stretch w-px bg-border"></div>
203
+
204
+ <button
205
+ type="button"
206
+ onclick={() => editor?.chain().focus().undo().run()}
207
+ disabled={!canUndo}
208
+ class="rounded p-1.5 text-muted-foreground transition-colors hover:bg-muted disabled:opacity-40"
209
+ ><Undo2 size={14} /></button>
210
+
211
+ <button
212
+ type="button"
213
+ onclick={() => editor?.chain().focus().redo().run()}
214
+ disabled={!canRedo}
215
+ class="rounded p-1.5 text-muted-foreground transition-colors hover:bg-muted disabled:opacity-40"
216
+ ><Redo2 size={14} /></button>
217
+ {/if}
218
+ </div>
219
+
220
+ <div bind:this={editorElement} class="rte-content min-h-48 max-h-96 overflow-y-auto p-3 text-sm"></div>
221
+ </div>
222
+
223
+ <style>
224
+ .rte-content :global(.ProseMirror) {
225
+ outline: none;
226
+ min-height: 180px;
227
+ }
228
+ .rte-content :global(.ProseMirror > * + *) {
229
+ margin-top: 0.4em;
230
+ }
231
+ .rte-content :global(.ProseMirror h1) {
232
+ font-size: 1.5rem;
233
+ font-weight: 700;
234
+ }
235
+ .rte-content :global(.ProseMirror h2) {
236
+ font-size: 1.25rem;
237
+ font-weight: 600;
238
+ }
239
+ .rte-content :global(.ProseMirror h3) {
240
+ font-size: 1.1rem;
241
+ font-weight: 600;
242
+ }
243
+ .rte-content :global(.ProseMirror ul) {
244
+ list-style-type: disc;
245
+ padding-left: 1.5rem;
246
+ }
247
+ .rte-content :global(.ProseMirror ol) {
248
+ list-style-type: decimal;
249
+ padding-left: 1.5rem;
250
+ }
251
+ .rte-content :global(.ProseMirror blockquote) {
252
+ border-left: 3px solid var(--border);
253
+ padding-left: 1rem;
254
+ color: var(--muted-foreground);
255
+ font-style: italic;
256
+ }
257
+ .rte-content :global(.ProseMirror hr) {
258
+ border: none;
259
+ border-top: 1px solid var(--border);
260
+ margin: 0.75rem 0;
261
+ }
262
+ .rte-content :global(.ProseMirror a) {
263
+ color: var(--primary);
264
+ text-decoration: underline;
265
+ cursor: pointer;
266
+ }
267
+ .rte-content :global(.ProseMirror code) {
268
+ background-color: var(--muted);
269
+ padding: 0.1em 0.3em;
270
+ border-radius: 3px;
271
+ font-size: 0.875em;
272
+ font-family: monospace;
273
+ }
274
+ .rte-content :global(.ProseMirror pre) {
275
+ background-color: var(--muted);
276
+ padding: 0.75rem 1rem;
277
+ border-radius: 6px;
278
+ overflow-x: auto;
279
+ }
280
+ .rte-content :global(.ProseMirror pre code) {
281
+ background: none;
282
+ padding: 0;
283
+ }
284
+ </style>
@@ -0,0 +1,57 @@
1
+ <script>
2
+ import { CircleSlash2 } from "lucide-svelte";
3
+ import DataTable from "../../../components/dataTable/dataTable.svelte";
4
+ import SidebarTrigger from "../../../components/sidebar/sidebarTrigger.svelte";
5
+ import { getStudioContext } from "../../../context";
6
+ import Singletone from "../../../components/singletone.svelte";
7
+ import { getExtensionUtils } from "../../../extensions/extensionUtils";
8
+ import ExtensionsComponents from "../../../components/extensionsComponents.svelte";
9
+
10
+ const { ctx, lobb } = getStudioContext();
11
+
12
+ let { collectionName } = $props();
13
+ let isSingletonCollection = $derived(ctx.meta.collections[collectionName].singleton);
14
+
15
+ let containerWidth = $state();
16
+ </script>
17
+
18
+ <div bind:clientWidth={containerWidth} class="h-full">
19
+ {#if collectionName}
20
+ <!-- TODO: add support in here for the views for each collection view -->
21
+ <!-- {#if true}
22
+ <ExtensionsComponents
23
+ name="studio.listView"
24
+ utils={getExtensionUtils(lobb, ctx)}
25
+ ></ExtensionsComponents>
26
+ {:else if isSingletonCollection} -->
27
+ {#if isSingletonCollection}
28
+ <Singletone collectionName={collectionName} />
29
+ {:else}
30
+ <DataTable
31
+ {collectionName}
32
+ tableProps={{
33
+ parentWidth: containerWidth,
34
+ }}
35
+ >
36
+ {#snippet headerLeft()}
37
+ <SidebarTrigger />
38
+ {/snippet}
39
+ </DataTable>
40
+ {/if}
41
+ {:else}
42
+ <div
43
+ class="relative flex h-full w-full flex-col items-center justify-center gap-4 text-muted-foreground"
44
+ >
45
+ <CircleSlash2 class="opacity-50" size="50" />
46
+ <div class="flex flex-col items-center justify-center">
47
+ <div>No collection selected</div>
48
+ <div class="text-xs">
49
+ Select a collection to view its entries or create new ones
50
+ </div>
51
+ </div>
52
+ <div class="absolute top-0 left-0 p-2.5">
53
+ <SidebarTrigger />
54
+ </div>
55
+ </div>
56
+ {/if}
57
+ </div>
@@ -0,0 +1,45 @@
1
+ <script lang="ts">
2
+ import type { SideBarData } from "../../../components/sidebar/sidebarElements.svelte";
3
+ import Sidebar from "../../../components/sidebar/sidebar.svelte";
4
+ import { getStudioContext } from "../../../context";
5
+ import Collection from "./collection.svelte";
6
+
7
+ const { ctx } = getStudioContext();
8
+ import { Table } from "lucide-svelte";
9
+
10
+ let { collectionName } = $props();
11
+
12
+ const collectionsList = $state(getCollectionsList());
13
+
14
+ function getCollectionsList() {
15
+ const collections = ctx.meta.collections;
16
+ let collectionsOwners: SideBarData = Object.entries(collections).map(
17
+ ([collectionName, collectionValue]) => {
18
+ return {
19
+ name: collectionName,
20
+ path: collectionValue.category ?? collectionValue.owner,
21
+ icon: Table,
22
+ href: `/studio/collections/${collectionName}`,
23
+ };
24
+ },
25
+ );
26
+
27
+ // updating the path from '__project' and '__core' to a more readable names
28
+ collectionsOwners = collectionsOwners.map((item) => {
29
+ if (item.path === "__project") {
30
+ item.path = "project";
31
+ } else if (item.path === "__core") {
32
+ item.path = "core";
33
+ }
34
+ return item;
35
+ });
36
+
37
+ return collectionsOwners;
38
+ }
39
+ </script>
40
+
41
+ <Sidebar title="Collections" data={collectionsList}>
42
+ {#key collectionName}
43
+ <Collection {collectionName} />
44
+ {/key}
45
+ </Sidebar>
@@ -0,0 +1,40 @@
1
+ <script lang="ts">
2
+ import { SvelteFlowProvider } from "@xyflow/svelte";
3
+ import Flow from "./flow.svelte";
4
+ import Sidebar from "../../../components/sidebar/sidebar.svelte";
5
+ import { location } from "@wjfe/n-savant";
6
+ import SyncManager from "./syncManager.svelte";
7
+ import SidebarTrigger from "../../../components/sidebar/sidebarTrigger.svelte";
8
+
9
+ const currentPage = $derived(location.url.pathname.replace("/studio", "").split("/")[2]);
10
+ </script>
11
+
12
+ <Sidebar
13
+ title="Data Model"
14
+ showSearch={false}
15
+ data={[
16
+ {
17
+ name: "graph",
18
+ href: "/studio/datamodel/graph",
19
+ },
20
+ {
21
+ name: "query_editor",
22
+ href: "/studio/datamodel/query_editor",
23
+ },
24
+ ]}
25
+ >
26
+ <div class="relative h-full w-full">
27
+ {#if currentPage === "graph"}
28
+ <SvelteFlowProvider>
29
+ <div style:width="100%" style:height="100%">
30
+ <Flow />
31
+ </div>
32
+ </SvelteFlowProvider>
33
+ {:else if currentPage === "query_editor"}
34
+ <SyncManager />
35
+ {/if}
36
+ <div class="absolute top-0 left-0 p-2.5">
37
+ <SidebarTrigger />
38
+ </div>
39
+ </div>
40
+ </Sidebar>
@@ -0,0 +1,22 @@
1
+ .svelte-flow {
2
+ --xy-edge-stroke-default: hsl(var(--muted-foreground));
3
+ --xy-edge-stroke-selected-default: hsl(var(--primary));
4
+ --xy-connectionline-stroke-default: hsl(var(--primary));
5
+ --xy-attribution-background-color-default: transparent;
6
+ --xy-minimap-background-color-default: hsl(var(--background));
7
+ --xy-minimap-mask-background-color-default: hsl(var(--primary) / 0.1);
8
+ --xy-minimap-node-background-color-default: hsl(var(--primary) / 0.2);
9
+ --xy-background-color-default: hsl(var(--soft));
10
+ --xy-background-pattern-dots-color-default: hsl(var(--muted-foreground));
11
+ --xy-background-pattern-lines-color-default: hsl(var(--background));
12
+ --xy-background-pattern-cross-color-default: hsl(var(--soft));
13
+ --xy-node-border-default: 1px solid hsl(var(--primary) / 0.25);
14
+ --xy-node-background-color-default: hsl(var(--background));
15
+ --xy-node-boxshadow-selected-default: 0 0 0 0.5px hsl(var(--primary) / 0.5);
16
+ --xy-handle-background-color-default: hsl(var(--primary));
17
+ --xy-handle-border-color-default: hsl(var(--background));
18
+ --xy-controls-button-background-color-default: hsl(var(--background));
19
+ --xy-controls-button-background-color-hover-default: hsl(var(--muted));
20
+ --xy-controls-button-border-color-default: hsl(var(--muted));
21
+ --xy-edge-label-background-color-default: hsl(var(--background));
22
+ }
@@ -0,0 +1,84 @@
1
+ <script lang="ts">
2
+ import "@xyflow/svelte/dist/style.css";
3
+ import "./flow.css";
4
+
5
+ import type { Node, Edge } from "@xyflow/svelte";
6
+
7
+ import {
8
+ SvelteFlow,
9
+ Background,
10
+ MiniMap,
11
+ Controls,
12
+ useSvelteFlow,
13
+ } from "@xyflow/svelte";
14
+ import { getLayoutedElements } from "./utils";
15
+ import { onMount } from "svelte";
16
+ import { getStudioContext } from "../../../context";
17
+
18
+ const { ctx } = getStudioContext();
19
+
20
+ const { fitView } = useSvelteFlow();
21
+
22
+ let nodes = $state.raw<Node[]>(generateNodes());
23
+ let edges = $state.raw<Edge[]>(generateEdges());
24
+
25
+ onMount(() => {
26
+ setTimeout(() => {
27
+ onLayout();
28
+ }, 0);
29
+ });
30
+
31
+ function generateNodes() {
32
+ const localNodes: Node[] = [];
33
+
34
+ for (const [collectionName, collectionValue] of Object.entries(
35
+ ctx.meta.collections,
36
+ )) {
37
+ if (collectionValue.owner !== "__project") {
38
+ continue;
39
+ }
40
+
41
+ localNodes.push({
42
+ id: collectionName,
43
+ data: { label: collectionName },
44
+ position: { x: 0, y: 0 },
45
+ });
46
+ }
47
+
48
+ return localNodes;
49
+ }
50
+
51
+ function generateEdges() {
52
+ const localEdges: Edge[] = [];
53
+
54
+ const relations = ctx.meta.relations;
55
+ for (let index = 0; index < relations.length; index++) {
56
+ const relation = relations[index];
57
+ localEdges.push({
58
+ id: `${relation.from.collection}_${relation.to.collection}`,
59
+ source: relation.from.collection,
60
+ target: relation.to.collection,
61
+ animated: true,
62
+ });
63
+ }
64
+
65
+ return localEdges;
66
+ }
67
+
68
+ function onLayout() {
69
+ const layouted = getLayoutedElements(nodes, edges);
70
+
71
+ nodes = [...layouted.nodes];
72
+ edges = [...layouted.edges];
73
+
74
+ fitView({
75
+ padding: 0.5,
76
+ });
77
+ }
78
+ </script>
79
+
80
+ <SvelteFlow bind:nodes bind:edges>
81
+ <Background />
82
+ <MiniMap />
83
+ <Controls />
84
+ </SvelteFlow>
@@ -0,0 +1,94 @@
1
+ <script lang="ts">
2
+ import DiffViewer from "../../../components/diffViewer.svelte";
3
+ import { getStudioContext } from "../../../context";
4
+
5
+ const { lobb, ctx } = getStudioContext();
6
+ import { onMount } from "svelte";
7
+ import stringify from "json-stable-stringify";
8
+ import CodeEditor from "../../../components/codeEditor.svelte";
9
+ import Table from "../../../components/dataTable/table.svelte";
10
+ import Button from "../../../components/ui/button/button.svelte";
11
+ import { LoaderCircle, SendHorizontal } from "lucide-svelte";
12
+
13
+ let configSchema: string = $state("");
14
+ let dbSchema: string = $state("");
15
+ let sqlPrompt = $state("");
16
+ let sqlResult = $state([]);
17
+
18
+ onMount(() => {
19
+ loadSchemas();
20
+ });
21
+
22
+ async function loadSchemas() {
23
+ configSchema = "";
24
+ dbSchema = "";
25
+ const response = await fetch(`${ctx.lobbUrl}/api/schema/diff`);
26
+ const result = await response.json();
27
+ configSchema = stringify(result.dbSchema, {
28
+ space: 2,
29
+ }) as string;
30
+ dbSchema = stringify(result.configSchema, {
31
+ space: 2,
32
+ }) as string;
33
+ }
34
+
35
+ async function handleExecute() {
36
+ const response = await lobb.createOne("core_query", {
37
+ query: sqlPrompt,
38
+ });
39
+ const result = await response.json();
40
+ sqlResult = result.data;
41
+ loadSchemas();
42
+ }
43
+ </script>
44
+
45
+ <div class="h-[50%] border-b overflow-auto">
46
+ {#if configSchema && dbSchema}
47
+ <DiffViewer
48
+ type="json"
49
+ original={configSchema}
50
+ modified={dbSchema}
51
+ class="h-full rounded-none border-0"
52
+ />
53
+ {:else}
54
+ <div class="flex justify-center items-center h-full gap-2">
55
+ <LoaderCircle class="animate-spin" />
56
+ <div>loading...</div>
57
+ </div>
58
+ {/if}
59
+ </div>
60
+ <div class="flex h-[50%] w-full">
61
+ <div class="h-full flex-1 flex flex-col border-r">
62
+ <div
63
+ class="h-10 flex items-center px-2 bg-background border-b justify-between"
64
+ >
65
+ <div>Query Editor</div>
66
+ <Button
67
+ class="h-7 px-3 text-xs font-normal"
68
+ Icon={SendHorizontal}
69
+ onclick={handleExecute}
70
+ >
71
+ Execute
72
+ </Button>
73
+ </div>
74
+ <CodeEditor
75
+ type="sql"
76
+ name="prompt"
77
+ bind:value={sqlPrompt}
78
+ class="flex-1 rounded-none border-0"
79
+ />
80
+ </div>
81
+ <div class="flex-1">
82
+ {#if Array.isArray(sqlResult) && sqlResult.length}
83
+ <Table
84
+ data={sqlResult}
85
+ showLastRowBorder={true}
86
+ showLastColumnBorder={true}
87
+ />
88
+ {:else}
89
+ <div class="flex flex-1 h-full items-center justify-center">
90
+ No results
91
+ </div>
92
+ {/if}
93
+ </div>
94
+ </div>
@@ -0,0 +1,35 @@
1
+ import Dagre from '@dagrejs/dagre';
2
+
3
+ export function getLayoutedElements(nodes: any[], edges: any[]) {
4
+ const g = new Dagre.graphlib.Graph().setDefaultEdgeLabel(() => ({}));
5
+ g.setGraph({ rankdir: "LR" });
6
+
7
+ edges.forEach((edge) => g.setEdge(edge.source, edge.target));
8
+ nodes.forEach((node) => {
9
+ return g.setNode(node.id, {
10
+ ...node,
11
+ width: node.measured?.width ?? 0,
12
+ height: node.measured?.height ?? 0,
13
+ });
14
+ });
15
+
16
+ Dagre.layout(g);
17
+
18
+ return {
19
+ nodes: nodes.map((node) => {
20
+ const position = g.node(node.id);
21
+ // We are shifting the dagre node position (anchor=center center) to the top left
22
+ // so it matches the Svelte Flow node anchor point (top left).
23
+ const x = position.x - (node.measured?.width ?? 0) / 2;
24
+ const y = position.y - (node.measured?.height ?? 0) / 2;
25
+
26
+ return {
27
+ ...node,
28
+ position: { x, y },
29
+ targetPosition: "left",
30
+ sourcePosition: "right",
31
+ };
32
+ }),
33
+ edges,
34
+ };
35
+ }
@@ -0,0 +1,19 @@
1
+ <script>
2
+ import ExtensionsComponents from "../../../components/extensionsComponents.svelte";
3
+ import { getExtensionUtils } from "../../../extensions/extensionUtils";
4
+ import { getStudioContext } from "../../../context";
5
+
6
+ let { extension, page } = $props();
7
+
8
+ const { lobb, ctx } = getStudioContext();
9
+ </script>
10
+
11
+ <div class="grid overflow-auto bg-background">
12
+ {#key extension && page}
13
+ <ExtensionsComponents
14
+ name="pages.{page}"
15
+ utils={getExtensionUtils(lobb, ctx)}
16
+ filterByExtensions={[extension]}
17
+ />
18
+ {/key}
19
+ </div>