@lobb-js/studio 0.1.31
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/.env.example +1 -0
- package/.storybook/main.ts +31 -0
- package/.storybook/preview.ts +21 -0
- package/.storybook/vitest.setup.ts +7 -0
- package/README.md +47 -0
- package/components.json +16 -0
- package/docker-entrypoint.sh +7 -0
- package/dockerfile +27 -0
- package/index.html +13 -0
- package/package.json +77 -0
- package/public/lobb.svg +15 -0
- package/src/Studio.svelte +150 -0
- package/src/app.css +121 -0
- package/src/components-export.ts +21 -0
- package/src/extensions/extension.types.ts +93 -0
- package/src/extensions/extensionUtils.ts +192 -0
- package/src/lib/Lobb.ts +241 -0
- package/src/lib/components/LlmButton.svelte +136 -0
- package/src/lib/components/alertView.svelte +20 -0
- package/src/lib/components/breadCrumbs.svelte +60 -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 +107 -0
- package/src/lib/components/dataTable/childRecords.svelte +140 -0
- package/src/lib/components/dataTable/dataTable.svelte +223 -0
- package/src/lib/components/dataTable/fieldCell.svelte +74 -0
- package/src/lib/components/dataTable/filter.svelte +282 -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 +154 -0
- package/src/lib/components/dataTable/sort.svelte +171 -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 +68 -0
- package/src/lib/components/detailView/create/createDetailView.svelte +226 -0
- package/src/lib/components/detailView/create/createDetailViewButton.svelte +32 -0
- package/src/lib/components/detailView/create/createManyView.svelte +250 -0
- package/src/lib/components/detailView/create/subRecords.svelte +48 -0
- package/src/lib/components/detailView/detailViewForm.svelte +104 -0
- package/src/lib/components/detailView/fieldCustomInput.svelte +23 -0
- package/src/lib/components/detailView/fieldInput.svelte +287 -0
- package/src/lib/components/detailView/fieldInputReplacement.svelte +199 -0
- package/src/lib/components/detailView/store.svelte.ts +61 -0
- package/src/lib/components/detailView/update/children.svelte +94 -0
- package/src/lib/components/detailView/update/updateDetailView.svelte +175 -0
- package/src/lib/components/detailView/update/updateDetailViewButton.svelte +32 -0
- package/src/lib/components/detailView/utils.ts +177 -0
- package/src/lib/components/diffViewer.svelte +102 -0
- package/src/lib/components/drawer.svelte +28 -0
- package/src/lib/components/extensionsComponents.svelte +31 -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 +238 -0
- package/src/lib/components/monacoEditor.svelte +181 -0
- package/src/lib/components/rangeCalendarButton.svelte +257 -0
- package/src/lib/components/selectRecord.svelte +126 -0
- package/src/lib/components/setServerPage.svelte +48 -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 +69 -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 +187 -0
- package/src/lib/eventSystem.ts +38 -0
- package/src/lib/index.ts +40 -0
- package/src/lib/store.svelte.ts +21 -0
- package/src/lib/store.types.ts +28 -0
- package/src/lib/utils.ts +84 -0
- package/src/main.ts +18 -0
- package/src/routes/collections/collection.svelte +46 -0
- package/src/routes/collections/collections.svelte +43 -0
- package/src/routes/data_model/dataModel.svelte +40 -0
- package/src/routes/data_model/flow.css +22 -0
- package/src/routes/data_model/flow.svelte +82 -0
- package/src/routes/data_model/syncManager.svelte +93 -0
- package/src/routes/data_model/utils.ts +35 -0
- package/src/routes/extensions/extension.svelte +16 -0
- package/src/routes/home.svelte +36 -0
- package/src/routes/workflows/workflows.svelte +135 -0
- package/src/stories/Configure.mdx +364 -0
- package/src/stories/assets/accessibility.png +0 -0
- package/src/stories/assets/accessibility.svg +1 -0
- package/src/stories/assets/addon-library.png +0 -0
- package/src/stories/assets/assets.png +0 -0
- package/src/stories/assets/avif-test-image.avif +0 -0
- package/src/stories/assets/context.png +0 -0
- package/src/stories/assets/discord.svg +1 -0
- package/src/stories/assets/docs.png +0 -0
- package/src/stories/assets/figma-plugin.png +0 -0
- package/src/stories/assets/github.svg +1 -0
- package/src/stories/assets/share.png +0 -0
- package/src/stories/assets/styling.png +0 -0
- package/src/stories/assets/testing.png +0 -0
- package/src/stories/assets/theming.png +0 -0
- package/src/stories/assets/tutorials.svg +1 -0
- package/src/stories/assets/youtube.svg +1 -0
- package/src/stories/detailView/detailViewForm.stories.svelte +79 -0
- package/src/stories/examples/Button.stories.svelte +31 -0
- package/src/stories/examples/Button.svelte +30 -0
- package/src/stories/examples/Header.stories.svelte +26 -0
- package/src/stories/examples/Header.svelte +45 -0
- package/src/stories/examples/Page.stories.svelte +29 -0
- package/src/stories/examples/Page.svelte +70 -0
- package/src/stories/examples/button.css +30 -0
- package/src/stories/examples/header.css +32 -0
- package/src/stories/examples/page.css +68 -0
- package/src/vite-env.d.ts +2 -0
- package/svelte.config.js +7 -0
- package/todo.md +24 -0
- package/tsconfig.app.json +25 -0
- package/tsconfig.json +14 -0
- package/tsconfig.node.json +24 -0
- package/vite-plugin-contextual-lib.js +66 -0
- package/vite.build.svelte.config.ts +18 -0
- package/vite.config.ts +84 -0
- package/vite.extension.config.ts +81 -0
- package/vite_utils.ts +28 -0
- package/vitest.shims.d.ts +1 -0
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Components,
|
|
3
|
+
DashboardNavs,
|
|
4
|
+
Extension,
|
|
5
|
+
ExtensionUtils,
|
|
6
|
+
} from "./extension.types";
|
|
7
|
+
import { lobb } from "$lib";
|
|
8
|
+
import { ctx } from "$lib/store.svelte";
|
|
9
|
+
import { toast } from "svelte-sonner";
|
|
10
|
+
import { showDialog } from "$lib/components/confirmationDialog/store.svelte";
|
|
11
|
+
import { Button } from "$lib/components/ui/button";
|
|
12
|
+
import { Input } from "$lib/components/ui/input";
|
|
13
|
+
import { Separator } from "$lib/components/ui/separator";
|
|
14
|
+
import { Skeleton } from "$lib/components/ui/skeleton";
|
|
15
|
+
import Table from "$lib/components/dataTable/table.svelte";
|
|
16
|
+
import { getFileFromUser, mediaQueries } from "$lib/utils";
|
|
17
|
+
import LlmButton from "$lib/components/LlmButton.svelte";
|
|
18
|
+
import Sidebar from "$lib/components/sidebar/sidebar.svelte";
|
|
19
|
+
import SidebarTrigger from "$lib/components/sidebar/sidebarTrigger.svelte";
|
|
20
|
+
import CreateDetailViewButton from "$lib/components/detailView/create/createDetailViewButton.svelte";
|
|
21
|
+
import UpdateDetailViewButton from "$lib/components/detailView/update/updateDetailViewButton.svelte";
|
|
22
|
+
import * as intlDate from "@internationalized/date";
|
|
23
|
+
import * as Tooltip from "$lib/components/ui/tooltip";
|
|
24
|
+
import * as Breadcrumb from "$lib/components/ui/breadcrumb";
|
|
25
|
+
import { ContextMenu } from "bits-ui";
|
|
26
|
+
import * as Popover from "$lib/components/ui/popover";
|
|
27
|
+
import * as Icons from "lucide-svelte";
|
|
28
|
+
import { location } from "@wjfe/n-savant";
|
|
29
|
+
import RangeCalendarButton from "$lib/components/rangeCalendarButton.svelte";
|
|
30
|
+
import DataTable from "$lib/components/dataTable/dataTable.svelte";
|
|
31
|
+
import Drawer from "$lib/components/drawer.svelte";
|
|
32
|
+
import SelectRecord from "$lib/components/selectRecord.svelte";
|
|
33
|
+
import { Switch } from "$lib/components/ui/switch";
|
|
34
|
+
|
|
35
|
+
export function getComponents(): Components {
|
|
36
|
+
return {
|
|
37
|
+
Button: Button,
|
|
38
|
+
Input: Input,
|
|
39
|
+
Separator: Separator,
|
|
40
|
+
Skeleton: Skeleton,
|
|
41
|
+
LlmButton: LlmButton,
|
|
42
|
+
Sidebar: Sidebar,
|
|
43
|
+
SidebarTrigger: SidebarTrigger,
|
|
44
|
+
CreateDetailViewButton: CreateDetailViewButton,
|
|
45
|
+
UpdateDetailViewButton: UpdateDetailViewButton,
|
|
46
|
+
Tooltip: Tooltip,
|
|
47
|
+
Breadcrumb: Breadcrumb,
|
|
48
|
+
ContextMenu: ContextMenu,
|
|
49
|
+
Popover: Popover,
|
|
50
|
+
Icons: Icons,
|
|
51
|
+
Table: Table,
|
|
52
|
+
RangeCalendarButton: RangeCalendarButton,
|
|
53
|
+
DataTable: DataTable,
|
|
54
|
+
Drawer: Drawer,
|
|
55
|
+
SelectRecord: SelectRecord,
|
|
56
|
+
Switch: Switch,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function getExtensionUtils(): ExtensionUtils {
|
|
61
|
+
return {
|
|
62
|
+
ctx: ctx,
|
|
63
|
+
lobb: lobb,
|
|
64
|
+
location: location,
|
|
65
|
+
toast: toast,
|
|
66
|
+
showDialog: showDialog,
|
|
67
|
+
getFileFromUser: getFileFromUser,
|
|
68
|
+
components: getComponents(),
|
|
69
|
+
mediaQueries: mediaQueries,
|
|
70
|
+
intlDate: intlDate,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function getExtensionsThatHasDash(): string[] {
|
|
75
|
+
const extensionNames = Object.keys(ctx.meta.extensions);
|
|
76
|
+
|
|
77
|
+
const extensionsWithDashExt = [];
|
|
78
|
+
for (let index = 0; index < extensionNames.length; index++) {
|
|
79
|
+
const extensionName = extensionNames[index];
|
|
80
|
+
const extensionMeta = ctx.meta.extensions[extensionName];
|
|
81
|
+
if (extensionMeta.hasDashboardExtension) {
|
|
82
|
+
extensionsWithDashExt.push(extensionName);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return extensionsWithDashExt;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export async function loadExtensions(studioExtensions?: any[]): Promise<Record<string, Extension>> {
|
|
90
|
+
const extensions: Record<string, Extension> = {};
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
if (studioExtensions) {
|
|
94
|
+
for (let index = 0; index < studioExtensions.length; index++) {
|
|
95
|
+
const studioExtension = studioExtensions[index](getExtensionUtils());
|
|
96
|
+
extensions[studioExtension.name] = studioExtension;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const extensionsThatHasDash = getExtensionsThatHasDash();
|
|
101
|
+
for (let index = 0; index < extensionsThatHasDash.length; index++) {
|
|
102
|
+
const extensionName = extensionsThatHasDash[index];
|
|
103
|
+
|
|
104
|
+
// Try to import locally-installed extension by name first
|
|
105
|
+
if (extensionName && extensions[extensionName]) {
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Fall back to remote import
|
|
110
|
+
try {
|
|
111
|
+
extensions[extensionName] = (
|
|
112
|
+
await import(
|
|
113
|
+
/* @vite-ignore */ `${ctx.lobbUrl}/api/extensions/${extensionName}/dashboard?v=${ctx.meta.extensions[extensionName]?.version}`
|
|
114
|
+
)
|
|
115
|
+
).extension(getExtensionUtils());
|
|
116
|
+
} catch (error) {
|
|
117
|
+
console.warn(`Failed to load remote extension ${extensionName}`, error);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return extensions;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export function loadExtensionComponents(
|
|
125
|
+
name: string,
|
|
126
|
+
filterByExtensions?: string[],
|
|
127
|
+
): any[] {
|
|
128
|
+
const components = [];
|
|
129
|
+
for (const [extensionName, extensionValue] of Object.entries(
|
|
130
|
+
ctx.extensions,
|
|
131
|
+
)) {
|
|
132
|
+
if (filterByExtensions && !filterByExtensions.includes(extensionName)) {
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
if (extensionValue.components) {
|
|
136
|
+
for (const [componentName, componentValue] of Object.entries(
|
|
137
|
+
extensionValue.components,
|
|
138
|
+
)) {
|
|
139
|
+
if (name.startsWith(componentName)) {
|
|
140
|
+
components.push(componentValue);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return components;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export async function executeExtensionsOnStartup() {
|
|
149
|
+
const extensionNames: string[] = Object.keys(ctx.extensions);
|
|
150
|
+
for (let index = 0; index < extensionNames.length; index++) {
|
|
151
|
+
const extensionName = extensionNames[index];
|
|
152
|
+
const extension = ctx.extensions[extensionName];
|
|
153
|
+
if (extension) {
|
|
154
|
+
if (extension.onStartup) {
|
|
155
|
+
await extension.onStartup(getExtensionUtils());
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export function getDashboardNavs(): DashboardNavs {
|
|
162
|
+
let navs: DashboardNavs = {
|
|
163
|
+
top: [],
|
|
164
|
+
middle: [],
|
|
165
|
+
bottom: [],
|
|
166
|
+
};
|
|
167
|
+
const extensionNames: string[] = Object.keys(ctx.extensions);
|
|
168
|
+
for (let index = 0; index < extensionNames.length; index++) {
|
|
169
|
+
const extensionName = extensionNames[index];
|
|
170
|
+
const extension = ctx.extensions[extensionName];
|
|
171
|
+
if (extension) {
|
|
172
|
+
if (extension.dashboardNavs && extension.dashboardNavs.top && navs.top) {
|
|
173
|
+
navs.top = [...navs.top, ...extension.dashboardNavs.top];
|
|
174
|
+
}
|
|
175
|
+
if (
|
|
176
|
+
extension.dashboardNavs &&
|
|
177
|
+
extension.dashboardNavs.middle &&
|
|
178
|
+
navs.middle
|
|
179
|
+
) {
|
|
180
|
+
navs.middle = [...navs.middle, ...extension.dashboardNavs.middle];
|
|
181
|
+
}
|
|
182
|
+
if (
|
|
183
|
+
extension.dashboardNavs &&
|
|
184
|
+
extension.dashboardNavs.bottom &&
|
|
185
|
+
navs.bottom
|
|
186
|
+
) {
|
|
187
|
+
navs.bottom = [...navs.bottom, ...extension.dashboardNavs.bottom];
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return navs;
|
|
192
|
+
}
|
package/src/lib/Lobb.ts
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import qs from "qs";
|
|
2
|
+
import { parseFunction } from "./utils";
|
|
3
|
+
|
|
4
|
+
interface RouteParams {
|
|
5
|
+
method: string;
|
|
6
|
+
route: string;
|
|
7
|
+
payload?: any;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
type OnResponseHandlers = (reponse: Response) => void;
|
|
11
|
+
|
|
12
|
+
export class Lobb {
|
|
13
|
+
public lobbUrl: string;
|
|
14
|
+
private headers: HeadersInit = [];
|
|
15
|
+
private onResponseHandlers: Array<OnResponseHandlers> = [];
|
|
16
|
+
|
|
17
|
+
constructor(lobbUrl: string) {
|
|
18
|
+
this.lobbUrl = lobbUrl;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
public async onResponse(callback: OnResponseHandlers) {
|
|
22
|
+
this.onResponseHandlers.push(callback);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
public setHeaders(headers: HeadersInit) {
|
|
26
|
+
this.headers = headers;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
public getHeaders() {
|
|
30
|
+
return this.headers;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
public async getMeta() {
|
|
34
|
+
const response = await fetch(`${this.lobbUrl}/api/meta`, {
|
|
35
|
+
headers: this.headers,
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const metaResponse = await this.handleResponse(response);
|
|
39
|
+
const meta = await metaResponse.json();
|
|
40
|
+
|
|
41
|
+
meta.studio_workflows = meta.studio_workflows.map((workflow: any) => {
|
|
42
|
+
return {
|
|
43
|
+
...workflow,
|
|
44
|
+
handler: parseFunction(workflow.handler),
|
|
45
|
+
};
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
return meta;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// CRUD methods
|
|
52
|
+
public async findAll(collectionName: string, params: any) {
|
|
53
|
+
const response = await fetch(
|
|
54
|
+
`${this.lobbUrl}/api/collections/${collectionName}/search`,
|
|
55
|
+
{
|
|
56
|
+
method: "POST",
|
|
57
|
+
headers: this.headers,
|
|
58
|
+
body: JSON.stringify(params),
|
|
59
|
+
},
|
|
60
|
+
);
|
|
61
|
+
return await this.handleResponse(response);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
public async findOne(
|
|
65
|
+
collectionName: string,
|
|
66
|
+
id: string,
|
|
67
|
+
queryParams?: any,
|
|
68
|
+
) {
|
|
69
|
+
queryParams = queryParams ? `?${qs.stringify(queryParams)}` : "";
|
|
70
|
+
const response = await fetch(
|
|
71
|
+
`${this.lobbUrl}/api/collections/${collectionName}/${id}${queryParams}`,
|
|
72
|
+
{
|
|
73
|
+
method: "GET",
|
|
74
|
+
headers: this.headers,
|
|
75
|
+
},
|
|
76
|
+
);
|
|
77
|
+
return await this.handleResponse(response);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
public async createOne(collectionName: string, body: any, file?: File) {
|
|
81
|
+
if (file) {
|
|
82
|
+
const formData = new FormData();
|
|
83
|
+
Object.keys(body).forEach((key) => {
|
|
84
|
+
formData.append(key, body[key]);
|
|
85
|
+
});
|
|
86
|
+
formData.append("file", file, file.name);
|
|
87
|
+
formData.append(
|
|
88
|
+
"payload",
|
|
89
|
+
JSON.stringify({
|
|
90
|
+
data: body,
|
|
91
|
+
}),
|
|
92
|
+
);
|
|
93
|
+
body = formData;
|
|
94
|
+
} else {
|
|
95
|
+
body = JSON.stringify({
|
|
96
|
+
data: body,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
const response = await fetch(
|
|
100
|
+
`${this.lobbUrl}/api/collections/${collectionName}`,
|
|
101
|
+
{
|
|
102
|
+
method: "POST",
|
|
103
|
+
headers: this.headers,
|
|
104
|
+
body,
|
|
105
|
+
},
|
|
106
|
+
);
|
|
107
|
+
return await this.handleResponse(response);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
public async updateOne(collectionName: string, id: string, body: any) {
|
|
111
|
+
const response = await fetch(
|
|
112
|
+
`${this.lobbUrl}/api/collections/${collectionName}/${id}`,
|
|
113
|
+
{
|
|
114
|
+
method: "PATCH",
|
|
115
|
+
headers: this.headers,
|
|
116
|
+
body: JSON.stringify({
|
|
117
|
+
data: body,
|
|
118
|
+
}),
|
|
119
|
+
},
|
|
120
|
+
);
|
|
121
|
+
return await this.handleResponse(response);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
public async readSingleton(
|
|
125
|
+
collectionName: string,
|
|
126
|
+
) {
|
|
127
|
+
const response = await fetch(
|
|
128
|
+
`${this.lobbUrl}/api/collections/${collectionName}/singleton`,
|
|
129
|
+
{
|
|
130
|
+
method: "GET",
|
|
131
|
+
headers: this.headers,
|
|
132
|
+
},
|
|
133
|
+
);
|
|
134
|
+
return await this.handleResponse(response);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
public async updateSingleton(
|
|
138
|
+
collectionName: string,
|
|
139
|
+
body: Record<string, any>
|
|
140
|
+
) {
|
|
141
|
+
const response = await fetch(
|
|
142
|
+
`${this.lobbUrl}/api/collections/${collectionName}/singleton`,
|
|
143
|
+
{
|
|
144
|
+
method: "PATCH",
|
|
145
|
+
headers: this.headers,
|
|
146
|
+
body: JSON.stringify({
|
|
147
|
+
data: body,
|
|
148
|
+
}),
|
|
149
|
+
},
|
|
150
|
+
);
|
|
151
|
+
return await this.handleResponse(response);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
public async deleteOne(collectionName: string, id: string, force = false) {
|
|
155
|
+
const response = await fetch(
|
|
156
|
+
`${this.lobbUrl}/api/collections/${collectionName}/${id}${force ? "?force" : ""}`,
|
|
157
|
+
{
|
|
158
|
+
method: "DELETE",
|
|
159
|
+
headers: this.headers,
|
|
160
|
+
},
|
|
161
|
+
);
|
|
162
|
+
return await this.handleResponse(response);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
public async deleteMany(collectionName: string, filter: any, force = false) {
|
|
166
|
+
const response = await fetch(
|
|
167
|
+
`${this.lobbUrl}/api/collections/${collectionName}${force ? "?force" : ""}`,
|
|
168
|
+
{
|
|
169
|
+
method: "DELETE",
|
|
170
|
+
headers: this.headers,
|
|
171
|
+
body: JSON.stringify({
|
|
172
|
+
filter,
|
|
173
|
+
}),
|
|
174
|
+
},
|
|
175
|
+
);
|
|
176
|
+
return await this.handleResponse(response);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
public async createMany(collectionName: string, body: any) {
|
|
180
|
+
body = JSON.stringify({
|
|
181
|
+
data: body,
|
|
182
|
+
});
|
|
183
|
+
const response = await fetch(
|
|
184
|
+
`${this.lobbUrl}/api/collections/${collectionName}`,
|
|
185
|
+
{
|
|
186
|
+
method: "POST",
|
|
187
|
+
headers: this.headers,
|
|
188
|
+
body,
|
|
189
|
+
},
|
|
190
|
+
);
|
|
191
|
+
return await this.handleResponse(response);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
public async updateMany(collectionName: string, body: any, filter: any) {
|
|
195
|
+
const response = await fetch(
|
|
196
|
+
`${this.lobbUrl}/api/collections/${collectionName}`,
|
|
197
|
+
{
|
|
198
|
+
method: "PATCH",
|
|
199
|
+
headers: this.headers,
|
|
200
|
+
body: JSON.stringify({
|
|
201
|
+
filter,
|
|
202
|
+
data: body,
|
|
203
|
+
}),
|
|
204
|
+
},
|
|
205
|
+
);
|
|
206
|
+
return await this.handleResponse(response);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
public async transactions(body: any[], rollback?: boolean) {
|
|
210
|
+
const response = await fetch(
|
|
211
|
+
`${this.lobbUrl}/api/collections/transactions${
|
|
212
|
+
rollback ? "?rollback" : ""
|
|
213
|
+
}`,
|
|
214
|
+
{
|
|
215
|
+
method: "POST",
|
|
216
|
+
headers: this.headers,
|
|
217
|
+
body: JSON.stringify(body),
|
|
218
|
+
},
|
|
219
|
+
);
|
|
220
|
+
return await this.handleResponse(response);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// CUSTOM REQUEST methods
|
|
224
|
+
public async request(params: RouteParams) {
|
|
225
|
+
const response = await fetch(`${this.lobbUrl}${params.route}`, {
|
|
226
|
+
method: params.method,
|
|
227
|
+
headers: this.headers,
|
|
228
|
+
body: JSON.stringify(params.payload),
|
|
229
|
+
});
|
|
230
|
+
return await this.handleResponse(response);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// HELPER methods
|
|
234
|
+
private async handleResponse(response: Response): Promise<Response> {
|
|
235
|
+
for (let index = 0; index < this.onResponseHandlers.length; index++) {
|
|
236
|
+
const handler = this.onResponseHandlers[index];
|
|
237
|
+
handler(response.clone());
|
|
238
|
+
}
|
|
239
|
+
return response;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { lobb } from "$lib";
|
|
3
|
+
import { Brain, LoaderIcon, Send } from "lucide-svelte";
|
|
4
|
+
import Button, { type ButtonProps } from "./ui/button/button.svelte";
|
|
5
|
+
import * as Popover from "./ui/popover";
|
|
6
|
+
import Textarea from "./ui/textarea/textarea.svelte";
|
|
7
|
+
import { toast } from "svelte-sonner";
|
|
8
|
+
import { ctx } from "$lib/store.svelte";
|
|
9
|
+
|
|
10
|
+
interface LocalProp {
|
|
11
|
+
value?: any;
|
|
12
|
+
title: string;
|
|
13
|
+
description?: string;
|
|
14
|
+
placeholder?: string;
|
|
15
|
+
format?: any;
|
|
16
|
+
messages?: any[];
|
|
17
|
+
variant?: ButtonProps["variant"];
|
|
18
|
+
class?: ButtonProps["class"];
|
|
19
|
+
Icon?: ButtonProps["Icon"];
|
|
20
|
+
children?: ButtonProps["children"];
|
|
21
|
+
onApiResponseComplete?: (res: any) => Promise<void>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
let {
|
|
25
|
+
value = $bindable(),
|
|
26
|
+
title,
|
|
27
|
+
description,
|
|
28
|
+
placeholder = "write prompt description",
|
|
29
|
+
variant = "default",
|
|
30
|
+
Icon = Brain,
|
|
31
|
+
onApiResponseComplete,
|
|
32
|
+
messages,
|
|
33
|
+
format = { type: "text" },
|
|
34
|
+
...props
|
|
35
|
+
}: LocalProp = $props();
|
|
36
|
+
|
|
37
|
+
let loading = $state(false);
|
|
38
|
+
let popoverOpen = $state(false);
|
|
39
|
+
let prompt = $state("");
|
|
40
|
+
|
|
41
|
+
let localMessages: any[] = [];
|
|
42
|
+
if (messages) {
|
|
43
|
+
localMessages.push(...messages);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function handleSubmit(e: Event) {
|
|
47
|
+
e.preventDefault();
|
|
48
|
+
loading = true;
|
|
49
|
+
popoverOpen = false;
|
|
50
|
+
if (prompt) {
|
|
51
|
+
const response = await fetch(
|
|
52
|
+
`${lobb.lobbUrl}/api/collections/llm_chat`,
|
|
53
|
+
{
|
|
54
|
+
method: "POST",
|
|
55
|
+
headers: lobb.getHeaders(),
|
|
56
|
+
body: JSON.stringify({
|
|
57
|
+
stream: true,
|
|
58
|
+
format: format,
|
|
59
|
+
messages: [
|
|
60
|
+
...localMessages,
|
|
61
|
+
{
|
|
62
|
+
role: "user",
|
|
63
|
+
content: prompt,
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
}),
|
|
67
|
+
},
|
|
68
|
+
);
|
|
69
|
+
if (response.ok && response.body) {
|
|
70
|
+
value = "";
|
|
71
|
+
const reader = response.body.getReader();
|
|
72
|
+
const textDecoder = new TextDecoder();
|
|
73
|
+
let done = false;
|
|
74
|
+
while (!done) {
|
|
75
|
+
const { done: readerDone, ...stream } = await reader.read();
|
|
76
|
+
const llmValue = stream.value;
|
|
77
|
+
done = readerDone;
|
|
78
|
+
const chunk = textDecoder.decode(llmValue, {
|
|
79
|
+
stream: true,
|
|
80
|
+
});
|
|
81
|
+
value += chunk;
|
|
82
|
+
}
|
|
83
|
+
if (format.type === "json_object") {
|
|
84
|
+
value = JSON.stringify(JSON.parse(value), null, 2);
|
|
85
|
+
}
|
|
86
|
+
} else {
|
|
87
|
+
toast.error("Failed to fetch stream");
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (onApiResponseComplete) {
|
|
91
|
+
await onApiResponseComplete(value);
|
|
92
|
+
}
|
|
93
|
+
loading = false;
|
|
94
|
+
}
|
|
95
|
+
</script>
|
|
96
|
+
|
|
97
|
+
{#if ctx.meta.extensions.llm && ctx.meta.collections.llm_chat}
|
|
98
|
+
<Popover.Root bind:open={popoverOpen}>
|
|
99
|
+
<Popover.Trigger>
|
|
100
|
+
<Button {variant} class={props.class}>
|
|
101
|
+
{#if loading}
|
|
102
|
+
<LoaderIcon class="animate-spin" />
|
|
103
|
+
{:else}
|
|
104
|
+
<Icon />
|
|
105
|
+
{/if}
|
|
106
|
+
{#if props.children}
|
|
107
|
+
{@render props.children()}
|
|
108
|
+
{/if}
|
|
109
|
+
</Button>
|
|
110
|
+
</Popover.Trigger>
|
|
111
|
+
<Popover.Content collisionPadding={20} class="mt-2 w-screen max-w-[20rem]">
|
|
112
|
+
<form
|
|
113
|
+
onsubmit={handleSubmit}
|
|
114
|
+
class="flex flex-col items-start gap-4"
|
|
115
|
+
>
|
|
116
|
+
<div>
|
|
117
|
+
<div class="text-sm font-semibold">{title}</div>
|
|
118
|
+
<div class="text-xs text-muted-foreground">
|
|
119
|
+
{description}
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
<Textarea
|
|
123
|
+
bind:value={prompt}
|
|
124
|
+
placeholder="Type here..."
|
|
125
|
+
rows={5}
|
|
126
|
+
class="bg-muted text-xs"
|
|
127
|
+
/>
|
|
128
|
+
<Button
|
|
129
|
+
type="submit"
|
|
130
|
+
Icon={Send}
|
|
131
|
+
class="h-7 px-3 text-xs font-normal">Submit</Button
|
|
132
|
+
>
|
|
133
|
+
</form>
|
|
134
|
+
</Popover.Content>
|
|
135
|
+
</Popover.Root>
|
|
136
|
+
{/if}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import * as Alert from "$lib/components/ui/alert/index.js";
|
|
3
|
+
import { AlertCircleIcon } from "lucide-svelte";
|
|
4
|
+
import type { Snippet } from "svelte";
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
title: string;
|
|
8
|
+
children: Snippet<[]>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const { title, children }: Props = $props();
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<Alert.Root variant="destructive">
|
|
15
|
+
<AlertCircleIcon />
|
|
16
|
+
<Alert.Title>{title}</Alert.Title>
|
|
17
|
+
<Alert.Description>
|
|
18
|
+
{@render children?.()}
|
|
19
|
+
</Alert.Description>
|
|
20
|
+
</Alert.Root>
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import * as Breadcrumb from "./ui/breadcrumb";
|
|
3
|
+
import { ctx } from "$lib/store.svelte";
|
|
4
|
+
import { mediaQueries } from "$lib/utils";
|
|
5
|
+
import { location } from "@wjfe/n-savant";
|
|
6
|
+
|
|
7
|
+
const isSmall = $derived(!mediaQueries.sm.current);
|
|
8
|
+
const pathNames = $derived(
|
|
9
|
+
location.url.pathname
|
|
10
|
+
.split("/")
|
|
11
|
+
.filter((el: any) => el !== "")
|
|
12
|
+
.slice(0, 3),
|
|
13
|
+
);
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
{#if isSmall}
|
|
17
|
+
<div class="text-muted-foreground text-sm">
|
|
18
|
+
{pathNames[pathNames.length - 1] || "Home"}
|
|
19
|
+
</div>
|
|
20
|
+
{:else}
|
|
21
|
+
<Breadcrumb.Root>
|
|
22
|
+
<Breadcrumb.List class="flex-nowrap">
|
|
23
|
+
<Breadcrumb.Item>
|
|
24
|
+
{#if pathNames.length === 0}
|
|
25
|
+
<Breadcrumb.Page>Home</Breadcrumb.Page>
|
|
26
|
+
{:else}
|
|
27
|
+
<Breadcrumb.Link
|
|
28
|
+
class="cursor-pointer"
|
|
29
|
+
onclick={() => location.navigate(`/`)}
|
|
30
|
+
>
|
|
31
|
+
Home
|
|
32
|
+
</Breadcrumb.Link>
|
|
33
|
+
<Breadcrumb.Separator />
|
|
34
|
+
{/if}
|
|
35
|
+
</Breadcrumb.Item>
|
|
36
|
+
{#each pathNames as path, index}
|
|
37
|
+
{@const isLastElement = pathNames.length - 1 === index}
|
|
38
|
+
{@const currentFullPaths = pathNames
|
|
39
|
+
.slice(0, index + 1)
|
|
40
|
+
.join("/")}
|
|
41
|
+
<Breadcrumb.Item>
|
|
42
|
+
{#if isLastElement}
|
|
43
|
+
<Breadcrumb.Page>{path}</Breadcrumb.Page>
|
|
44
|
+
{:else}
|
|
45
|
+
<Breadcrumb.Link
|
|
46
|
+
class="cursor-pointer"
|
|
47
|
+
onclick={() =>
|
|
48
|
+
location.navigate(`/${currentFullPaths}`)}
|
|
49
|
+
>
|
|
50
|
+
{path}
|
|
51
|
+
</Breadcrumb.Link>
|
|
52
|
+
{/if}
|
|
53
|
+
</Breadcrumb.Item>
|
|
54
|
+
{#if !isLastElement}
|
|
55
|
+
<Breadcrumb.Separator />
|
|
56
|
+
{/if}
|
|
57
|
+
{/each}
|
|
58
|
+
</Breadcrumb.List>
|
|
59
|
+
</Breadcrumb.Root>
|
|
60
|
+
{/if}
|