@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.
- package/package.json +2 -1
- package/src/App.svelte +5 -0
- package/src/app.css +124 -0
- package/src/lib/components/LlmButton.svelte +137 -0
- package/src/lib/components/Studio.svelte +129 -0
- package/src/lib/components/alertView.svelte +20 -0
- package/src/lib/components/breadCrumbs.svelte +60 -0
- package/src/lib/components/codeEditor.svelte +152 -0
- package/src/lib/components/combobox.svelte +92 -0
- package/src/lib/components/confirmationDialog/confirmationDialog.svelte +33 -0
- package/src/lib/components/confirmationDialog/store.svelte.ts +28 -0
- package/src/lib/components/createManyButton.svelte +109 -0
- package/src/lib/components/dataTable/childRecords.svelte +142 -0
- package/src/lib/components/dataTable/dataTable.svelte +225 -0
- package/src/lib/components/dataTable/fieldCell.svelte +77 -0
- package/src/lib/components/dataTable/filter.svelte +284 -0
- package/src/lib/components/dataTable/filterButton.svelte +39 -0
- package/src/lib/components/dataTable/footer.svelte +84 -0
- package/src/lib/components/dataTable/header.svelte +155 -0
- package/src/lib/components/dataTable/sort.svelte +173 -0
- package/src/lib/components/dataTable/sortButton.svelte +36 -0
- package/src/lib/components/dataTable/table.svelte +337 -0
- package/src/lib/components/dataTable/utils.ts +127 -0
- package/src/lib/components/detailView/create/children.svelte +70 -0
- package/src/lib/components/detailView/create/createDetailView.svelte +228 -0
- package/src/lib/components/detailView/create/createDetailViewButton.svelte +37 -0
- package/src/lib/components/detailView/create/createManyView.svelte +252 -0
- package/src/lib/components/detailView/create/subRecords.svelte +50 -0
- package/src/lib/components/detailView/detailViewForm.svelte +104 -0
- package/src/lib/components/detailView/fieldCustomInput.svelte +26 -0
- package/src/lib/components/detailView/fieldInput.svelte +258 -0
- package/src/lib/components/detailView/fieldInputReplacement.svelte +199 -0
- package/src/lib/components/detailView/store.svelte.ts +59 -0
- package/src/lib/components/detailView/update/children.svelte +96 -0
- package/src/lib/components/detailView/update/updateDetailView.svelte +176 -0
- package/src/lib/components/detailView/update/updateDetailViewButton.svelte +56 -0
- package/src/lib/components/detailView/utils.ts +176 -0
- package/src/lib/components/diffViewer.svelte +105 -0
- package/src/lib/components/drawer.svelte +28 -0
- package/src/lib/components/extensionsComponents.svelte +33 -0
- package/src/lib/components/foreingKeyInput.svelte +80 -0
- package/src/lib/components/header.svelte +45 -0
- package/src/lib/components/loadingTypesForMonacoEditor.ts +36 -0
- package/src/lib/components/miniSidebar.svelte +226 -0
- package/src/lib/components/rangeCalendarButton.svelte +257 -0
- package/src/lib/components/richTextEditor.svelte +284 -0
- package/src/lib/components/routes/collections/collection.svelte +57 -0
- package/src/lib/components/routes/collections/collections.svelte +45 -0
- package/src/lib/components/routes/data_model/dataModel.svelte +40 -0
- package/src/lib/components/routes/data_model/flow.css +22 -0
- package/src/lib/components/routes/data_model/flow.svelte +84 -0
- package/src/lib/components/routes/data_model/syncManager.svelte +94 -0
- package/src/lib/components/routes/data_model/utils.ts +35 -0
- package/src/lib/components/routes/extensions/extension.svelte +19 -0
- package/src/lib/components/routes/home.svelte +40 -0
- package/src/lib/components/routes/workflows/workflows.svelte +136 -0
- package/src/lib/components/selectRecord.svelte +130 -0
- package/src/lib/components/setServerPage.svelte +50 -0
- package/src/lib/components/sidebar/index.ts +4 -0
- package/src/lib/components/sidebar/sidebar.svelte +149 -0
- package/src/lib/components/sidebar/sidebarElements.svelte +144 -0
- package/src/lib/components/sidebar/sidebarTrigger.svelte +33 -0
- package/src/lib/components/singletone.svelte +71 -0
- package/src/lib/components/ui/accordion/accordion-content.svelte +22 -0
- package/src/lib/components/ui/accordion/accordion-item.svelte +12 -0
- package/src/lib/components/ui/accordion/accordion-trigger.svelte +31 -0
- package/src/lib/components/ui/accordion/index.ts +17 -0
- package/src/lib/components/ui/alert/alert-description.svelte +16 -0
- package/src/lib/components/ui/alert/alert-title.svelte +24 -0
- package/src/lib/components/ui/alert/alert.svelte +39 -0
- package/src/lib/components/ui/alert/index.ts +14 -0
- package/src/lib/components/ui/alert-dialog/alert-dialog-action.svelte +13 -0
- package/src/lib/components/ui/alert-dialog/alert-dialog-cancel.svelte +17 -0
- package/src/lib/components/ui/alert-dialog/alert-dialog-content.svelte +26 -0
- package/src/lib/components/ui/alert-dialog/alert-dialog-description.svelte +16 -0
- package/src/lib/components/ui/alert-dialog/alert-dialog-footer.svelte +20 -0
- package/src/lib/components/ui/alert-dialog/alert-dialog-header.svelte +20 -0
- package/src/lib/components/ui/alert-dialog/alert-dialog-overlay.svelte +19 -0
- package/src/lib/components/ui/alert-dialog/alert-dialog-title.svelte +18 -0
- package/src/lib/components/ui/alert-dialog/index.ts +40 -0
- package/src/lib/components/ui/breadcrumb/breadcrumb-ellipsis.svelte +23 -0
- package/src/lib/components/ui/breadcrumb/breadcrumb-item.svelte +16 -0
- package/src/lib/components/ui/breadcrumb/breadcrumb-link.svelte +31 -0
- package/src/lib/components/ui/breadcrumb/breadcrumb-list.svelte +23 -0
- package/src/lib/components/ui/breadcrumb/breadcrumb-page.svelte +23 -0
- package/src/lib/components/ui/breadcrumb/breadcrumb-separator.svelte +27 -0
- package/src/lib/components/ui/breadcrumb/breadcrumb.svelte +15 -0
- package/src/lib/components/ui/breadcrumb/index.ts +25 -0
- package/src/lib/components/ui/button/button.svelte +110 -0
- package/src/lib/components/ui/button/index.ts +17 -0
- package/src/lib/components/ui/checkbox/checkbox.svelte +35 -0
- package/src/lib/components/ui/checkbox/index.ts +6 -0
- package/src/lib/components/ui/command/command-dialog.svelte +35 -0
- package/src/lib/components/ui/command/command-empty.svelte +12 -0
- package/src/lib/components/ui/command/command-group.svelte +31 -0
- package/src/lib/components/ui/command/command-input.svelte +25 -0
- package/src/lib/components/ui/command/command-item.svelte +19 -0
- package/src/lib/components/ui/command/command-link-item.svelte +19 -0
- package/src/lib/components/ui/command/command-list.svelte +16 -0
- package/src/lib/components/ui/command/command-separator.svelte +12 -0
- package/src/lib/components/ui/command/command-shortcut.svelte +20 -0
- package/src/lib/components/ui/command/command.svelte +21 -0
- package/src/lib/components/ui/command/index.ts +40 -0
- package/src/lib/components/ui/dialog/dialog-content.svelte +38 -0
- package/src/lib/components/ui/dialog/dialog-description.svelte +16 -0
- package/src/lib/components/ui/dialog/dialog-footer.svelte +20 -0
- package/src/lib/components/ui/dialog/dialog-header.svelte +20 -0
- package/src/lib/components/ui/dialog/dialog-overlay.svelte +19 -0
- package/src/lib/components/ui/dialog/dialog-title.svelte +16 -0
- package/src/lib/components/ui/dialog/index.ts +37 -0
- package/src/lib/components/ui/input/index.ts +7 -0
- package/src/lib/components/ui/input/input.svelte +46 -0
- package/src/lib/components/ui/label/index.ts +7 -0
- package/src/lib/components/ui/label/label.svelte +19 -0
- package/src/lib/components/ui/popover/index.ts +17 -0
- package/src/lib/components/ui/popover/popover-content.svelte +28 -0
- package/src/lib/components/ui/range-calendar/index.ts +30 -0
- package/src/lib/components/ui/range-calendar/range-calendar-cell.svelte +19 -0
- package/src/lib/components/ui/range-calendar/range-calendar-day.svelte +35 -0
- package/src/lib/components/ui/range-calendar/range-calendar-grid-body.svelte +12 -0
- package/src/lib/components/ui/range-calendar/range-calendar-grid-head.svelte +12 -0
- package/src/lib/components/ui/range-calendar/range-calendar-grid-row.svelte +12 -0
- package/src/lib/components/ui/range-calendar/range-calendar-grid.svelte +16 -0
- package/src/lib/components/ui/range-calendar/range-calendar-head-cell.svelte +16 -0
- package/src/lib/components/ui/range-calendar/range-calendar-header.svelte +16 -0
- package/src/lib/components/ui/range-calendar/range-calendar-heading.svelte +16 -0
- package/src/lib/components/ui/range-calendar/range-calendar-months.svelte +20 -0
- package/src/lib/components/ui/range-calendar/range-calendar-next-button.svelte +27 -0
- package/src/lib/components/ui/range-calendar/range-calendar-prev-button.svelte +27 -0
- package/src/lib/components/ui/range-calendar/range-calendar.svelte +57 -0
- package/src/lib/components/ui/select/index.ts +34 -0
- package/src/lib/components/ui/select/select-content.svelte +38 -0
- package/src/lib/components/ui/select/select-group-heading.svelte +16 -0
- package/src/lib/components/ui/select/select-item.svelte +37 -0
- package/src/lib/components/ui/select/select-scroll-down-button.svelte +19 -0
- package/src/lib/components/ui/select/select-scroll-up-button.svelte +19 -0
- package/src/lib/components/ui/select/select-separator.svelte +13 -0
- package/src/lib/components/ui/select/select-trigger.svelte +24 -0
- package/src/lib/components/ui/separator/index.ts +7 -0
- package/src/lib/components/ui/separator/separator.svelte +22 -0
- package/src/lib/components/ui/skeleton/index.ts +7 -0
- package/src/lib/components/ui/skeleton/skeleton.svelte +22 -0
- package/src/lib/components/ui/sonner/index.ts +1 -0
- package/src/lib/components/ui/sonner/sonner.svelte +20 -0
- package/src/lib/components/ui/switch/index.ts +7 -0
- package/src/lib/components/ui/switch/switch.svelte +27 -0
- package/src/lib/components/ui/textarea/index.ts +7 -0
- package/src/lib/components/ui/textarea/textarea.svelte +22 -0
- package/src/lib/components/ui/tooltip/index.ts +18 -0
- package/src/lib/components/ui/tooltip/tooltip-content.svelte +21 -0
- package/src/lib/components/workflowEditor.svelte +188 -0
- package/src/lib/context.ts +22 -0
- package/src/lib/eventSystem.ts +40 -0
- package/src/lib/extensions/extension.types.ts +92 -0
- package/src/lib/extensions/extensionUtils.ts +156 -0
- package/src/lib/index.ts +24 -0
- package/src/lib/store.svelte.ts +13 -0
- package/src/lib/store.types.ts +28 -0
- package/src/lib/utils.ts +68 -0
- package/src/main.ts +18 -0
- package/src/stories/detailView/detailViewForm.stories.svelte +79 -0
- package/src/vite-env.d.ts +2 -0
- package/vite-plugins/index.js +2 -0
- package/vite-plugins/lobb-proxy.js +36 -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>
|