@lobb-js/studio 0.1.34 → 0.1.36
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 -2
- package/src/lib/Lobb.d.ts +30 -0
- package/src/lib/Lobb.ts +241 -0
- package/src/lib/components/Studio.svelte +5 -5
- package/src/lib/components/routes/collections/collection.svelte +46 -0
- package/src/lib/components/routes/collections/collections.svelte +43 -0
- package/src/lib/components/routes/collections/collections.svelte.d.ts +5 -0
- package/src/lib/components/routes/data_model/dataModel.svelte +40 -0
- package/src/lib/components/routes/data_model/dataModel.svelte.d.ts +3 -0
- package/src/lib/components/routes/data_model/flow.css +22 -0
- package/src/lib/components/routes/data_model/flow.svelte +82 -0
- package/src/lib/components/routes/data_model/flow.svelte.d.ts +5 -0
- package/src/lib/components/routes/data_model/syncManager.svelte +93 -0
- package/src/lib/components/routes/data_model/syncManager.svelte.d.ts +3 -0
- package/src/lib/components/routes/data_model/utils.d.ts +4 -0
- package/src/lib/components/routes/data_model/utils.ts +35 -0
- package/src/lib/components/routes/extensions/extension.svelte +16 -0
- package/src/lib/components/routes/home.svelte +36 -0
- package/src/lib/components/routes/workflows/workflows.svelte +135 -0
- package/src/lib/components/routes/workflows/workflows.svelte.d.ts +5 -0
- package/src/lib/eventSystem.d.ts +1 -0
- package/src/lib/eventSystem.ts +38 -0
- package/src/lib/extensions/extension.types.d.ts +83 -0
- package/src/lib/extensions/extension.types.ts +93 -0
- package/src/lib/extensions/extensionUtils.d.ts +8 -0
- package/src/lib/extensions/extensionUtils.ts +192 -0
- package/src/lib/index.d.ts +9 -0
- package/src/lib/index.ts +36 -0
- package/src/lib/store.svelte.d.ts +4 -0
- package/src/lib/store.svelte.ts +33 -0
- package/src/lib/store.types.d.ts +26 -0
- package/src/lib/store.types.ts +28 -0
- package/src/lib/utils.d.ts +27 -0
- package/src/lib/utils.ts +84 -0
|
@@ -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,16 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import ExtensionsComponents from "$lib/components/extensionsComponents.svelte";
|
|
3
|
+
import { getExtensionUtils } from "$lib/extensions/extensionUtils";
|
|
4
|
+
|
|
5
|
+
let { extension, page } = $props();
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<div class="grid overflow-auto bg-background">
|
|
9
|
+
{#key extension && page}
|
|
10
|
+
<ExtensionsComponents
|
|
11
|
+
name="pages.{page}"
|
|
12
|
+
utils={getExtensionUtils()}
|
|
13
|
+
filterByExtensions={[extension]}
|
|
14
|
+
/>
|
|
15
|
+
{/key}
|
|
16
|
+
</div>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import Button from "$lib/components/ui/button/button.svelte";
|
|
3
|
+
import { ctx } from "$lib/store.svelte";
|
|
4
|
+
import { location } from "@wjfe/n-savant";
|
|
5
|
+
import { ArrowRight } from "lucide-svelte";
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<div class="flex flex-col">
|
|
9
|
+
<div
|
|
10
|
+
class="flex flex-1 w-full flex-col items-center justify-center gap-4 text-muted-foreground"
|
|
11
|
+
>
|
|
12
|
+
<div class="flex flex-col items-center justify-center p-4">
|
|
13
|
+
<div class="text-3xl">Welcome to Lobb!</div>
|
|
14
|
+
<div class="text-xs text-center">
|
|
15
|
+
Your journey starts here. Explore and make the most of your
|
|
16
|
+
experience.
|
|
17
|
+
</div>
|
|
18
|
+
</div>
|
|
19
|
+
<div class="flex flex-col items-center justify-center">
|
|
20
|
+
<Button
|
|
21
|
+
Icon={ArrowRight}
|
|
22
|
+
variant="outline"
|
|
23
|
+
class="h-7 px-3 text-xs font-normal"
|
|
24
|
+
onclick={() => location.navigate("/collections")}
|
|
25
|
+
>
|
|
26
|
+
Go to collections
|
|
27
|
+
</Button>
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
<div class="flex justify-end p-2 text-xs text-muted-foreground/50">
|
|
31
|
+
<div class="flex flex-col text-end">
|
|
32
|
+
<div>studio: v{ctx.studioVersion}</div>
|
|
33
|
+
<div>core: v{ctx.meta.version}</div>
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { SideBarData } from "$lib/components/sidebar/sidebarElements.svelte";
|
|
3
|
+
import WorkflowEditor, {
|
|
4
|
+
type WorkflowEntry,
|
|
5
|
+
} from "$lib/components/workflowEditor.svelte";
|
|
6
|
+
import { lobb } from "$lib";
|
|
7
|
+
import Sidebar from "$lib/components/sidebar/sidebar.svelte";
|
|
8
|
+
import Button from "$lib/components/ui/button/button.svelte";
|
|
9
|
+
import { ctx } from "$lib/store.svelte";
|
|
10
|
+
import { location } from "@wjfe/n-savant";
|
|
11
|
+
import { CircleSlash2, Plus, Trash2 } from "lucide-svelte";
|
|
12
|
+
import { onMount } from "svelte";
|
|
13
|
+
import { showDialog } from "$lib/components/confirmationDialog/store.svelte";
|
|
14
|
+
import SidebarTrigger from "$lib/components/sidebar/sidebarTrigger.svelte";
|
|
15
|
+
|
|
16
|
+
let { workflowName } = $props();
|
|
17
|
+
|
|
18
|
+
let sidebarData: SideBarData | null = $state(null);
|
|
19
|
+
let workflowEntry: WorkflowEntry | null = $state(null);
|
|
20
|
+
|
|
21
|
+
onMount(async () => {
|
|
22
|
+
getSidebarData();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
$effect(() => {
|
|
26
|
+
fetchWorkflowData(workflowName);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
async function getSidebarData() {
|
|
30
|
+
const response = await lobb.findAll("core_workflows", {});
|
|
31
|
+
const result = await response.json();
|
|
32
|
+
const workflows: any[] = result.data;
|
|
33
|
+
sidebarData = workflows.map((workflow) => {
|
|
34
|
+
return {
|
|
35
|
+
name: workflow.name,
|
|
36
|
+
path: workflow.directory,
|
|
37
|
+
onclick: () => {
|
|
38
|
+
location.navigate(`/workflows/${workflow.name}`);
|
|
39
|
+
},
|
|
40
|
+
meta: {
|
|
41
|
+
id: workflow.id,
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async function fetchWorkflowData(workflowName: string) {
|
|
48
|
+
if (workflowName && workflowName !== "new") {
|
|
49
|
+
const response = await lobb.findAll("core_workflows", {
|
|
50
|
+
filter: {
|
|
51
|
+
name: workflowName,
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
const result = await response.json();
|
|
55
|
+
const workflow = result.data[0];
|
|
56
|
+
workflowEntry = workflow;
|
|
57
|
+
} else {
|
|
58
|
+
const workflowHandlerDefaultValue =
|
|
59
|
+
ctx.meta.collections.core_workflows.fields.handler
|
|
60
|
+
.pre_processors.default;
|
|
61
|
+
workflowEntry = {
|
|
62
|
+
name: "",
|
|
63
|
+
event_name: "",
|
|
64
|
+
handler: workflowHandlerDefaultValue,
|
|
65
|
+
directory: "",
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async function handleWorkflowDelete(
|
|
71
|
+
workflowName: string,
|
|
72
|
+
workflowId: string,
|
|
73
|
+
) {
|
|
74
|
+
const result = await showDialog(
|
|
75
|
+
"Are you sure?",
|
|
76
|
+
"This will delete the Workflow you selected.",
|
|
77
|
+
);
|
|
78
|
+
if (result) {
|
|
79
|
+
await lobb.deleteOne("core_workflows", workflowId);
|
|
80
|
+
getSidebarData();
|
|
81
|
+
if (workflowEntry && workflowName === workflowEntry.name) {
|
|
82
|
+
location.navigate("/workflows");
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
</script>
|
|
87
|
+
|
|
88
|
+
<Sidebar title="Workflows" data={sidebarData}>
|
|
89
|
+
{#snippet belowSearch()}
|
|
90
|
+
<div class="pb-4 px-2">
|
|
91
|
+
<Button
|
|
92
|
+
class="h-7 px-3 text-xs font-normal w-full"
|
|
93
|
+
variant="outline"
|
|
94
|
+
onclick={() => location.navigate("/workflows/new")}
|
|
95
|
+
Icon={Plus}
|
|
96
|
+
>
|
|
97
|
+
Create a Workflow
|
|
98
|
+
</Button>
|
|
99
|
+
</div>
|
|
100
|
+
{/snippet}
|
|
101
|
+
{#snippet elementRightSide(element)}
|
|
102
|
+
<Button
|
|
103
|
+
class="h-6 w-6 text-muted-foreground hover:bg-transparent"
|
|
104
|
+
variant="ghost"
|
|
105
|
+
size="icon"
|
|
106
|
+
onclick={() => handleWorkflowDelete(element.name, element.meta?.id)}
|
|
107
|
+
Icon={Trash2}
|
|
108
|
+
></Button>
|
|
109
|
+
{/snippet}
|
|
110
|
+
<div class="relative h-full w-full">
|
|
111
|
+
{#if workflowName === undefined}
|
|
112
|
+
<div
|
|
113
|
+
class="flex h-full w-full flex-col items-center justify-center gap-4 text-muted-foreground"
|
|
114
|
+
>
|
|
115
|
+
<CircleSlash2 class="opacity-50" size="50" />
|
|
116
|
+
<div class="flex flex-col items-center justify-center">
|
|
117
|
+
<div>No workflow selected</div>
|
|
118
|
+
<div class="text-xs">
|
|
119
|
+
Select a workflow to edit it or create new ones
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
</div>
|
|
123
|
+
{:else if workflowEntry}
|
|
124
|
+
{#key workflowEntry}
|
|
125
|
+
<WorkflowEditor
|
|
126
|
+
bind:workflow={workflowEntry}
|
|
127
|
+
refreshSidebar={getSidebarData}
|
|
128
|
+
/>
|
|
129
|
+
{/key}
|
|
130
|
+
{/if}
|
|
131
|
+
<div class="absolute top-0 left-0 p-2.5">
|
|
132
|
+
<SidebarTrigger />
|
|
133
|
+
</div>
|
|
134
|
+
</div>
|
|
135
|
+
</Sidebar>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function emitEvent(eventName: string, input: Record<string, any>): Promise<Record<string, any>>;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { toast } from "svelte-sonner";
|
|
2
|
+
import { ctx } from "./store.svelte";
|
|
3
|
+
import { openCreateDetailView, openUpdateDetailView } from "./components/detailView/store.svelte";
|
|
4
|
+
|
|
5
|
+
export async function emitEvent(eventName: string, input: Record<string, any>) {
|
|
6
|
+
const workflows = ctx.meta.studio_workflows.filter(
|
|
7
|
+
(workflow) => {
|
|
8
|
+
return eventName.startsWith(workflow.eventName);
|
|
9
|
+
},
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
for (let index = 0; index < workflows.length; index++) {
|
|
13
|
+
const workflow = workflows[index];
|
|
14
|
+
try {
|
|
15
|
+
const localOutput = await workflow.handler(
|
|
16
|
+
input,
|
|
17
|
+
await getEventContext(),
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
if (localOutput) {
|
|
21
|
+
input = localOutput;
|
|
22
|
+
}
|
|
23
|
+
} catch (error) {
|
|
24
|
+
toast.error((error as any).message);
|
|
25
|
+
throw error;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return input;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async function getEventContext() {
|
|
33
|
+
return {
|
|
34
|
+
toast,
|
|
35
|
+
openCreateDetailView,
|
|
36
|
+
openUpdateDetailView,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { Lobb } from "$lib/Lobb";
|
|
2
|
+
import type { CTX } from "../lib/store.types";
|
|
3
|
+
import type { Button } from "$lib/components/ui/button";
|
|
4
|
+
import type { Input } from "$lib/components/ui/input";
|
|
5
|
+
import type { Separator } from "$lib/components/ui/separator";
|
|
6
|
+
import type { Skeleton } from "$lib/components/ui/skeleton";
|
|
7
|
+
import type LlmButton from "$lib/components/LlmButton.svelte";
|
|
8
|
+
import type Sidebar from "$lib/components/sidebar/sidebar.svelte";
|
|
9
|
+
import type SidebarTrigger from "$lib/components/sidebar/sidebarTrigger.svelte";
|
|
10
|
+
import type CreateDetailViewButton from "$lib/components/detailView/create/createDetailViewButton.svelte";
|
|
11
|
+
import type UpdateDetailViewButton from "$lib/components/detailView/update/updateDetailViewButton.svelte";
|
|
12
|
+
import type { getFileFromUser, mediaQueries } from "$lib/utils";
|
|
13
|
+
import type Table from "$lib/components/dataTable/table.svelte";
|
|
14
|
+
import type { Location } from "@wjfe/n-savant";
|
|
15
|
+
import type RangeCalendarButton from "$lib/components/rangeCalendarButton.svelte";
|
|
16
|
+
import type DataTable from "$lib/components/dataTable/dataTable.svelte";
|
|
17
|
+
import type Drawer from "$lib/components/drawer.svelte";
|
|
18
|
+
import type SelectRecord from "$lib/components/selectRecord.svelte";
|
|
19
|
+
import * as Popover from "$lib/components/ui/popover";
|
|
20
|
+
import * as intlDate from "@internationalized/date";
|
|
21
|
+
import * as Icons from "lucide-svelte";
|
|
22
|
+
import { ContextMenu } from "bits-ui";
|
|
23
|
+
import * as Tooltip from "$lib/components/ui/tooltip";
|
|
24
|
+
import * as Breadcrumb from "$lib/components/ui/breadcrumb";
|
|
25
|
+
import { showDialog } from "$lib/components/confirmationDialog/store.svelte";
|
|
26
|
+
import { toast } from "svelte-sonner";
|
|
27
|
+
import { Switch } from "$lib/components/ui/switch";
|
|
28
|
+
export interface Components {
|
|
29
|
+
Button: typeof Button;
|
|
30
|
+
Input: typeof Input;
|
|
31
|
+
Separator: typeof Separator;
|
|
32
|
+
Skeleton: typeof Skeleton;
|
|
33
|
+
LlmButton: typeof LlmButton;
|
|
34
|
+
Sidebar: typeof Sidebar;
|
|
35
|
+
SidebarTrigger: typeof SidebarTrigger;
|
|
36
|
+
CreateDetailViewButton: typeof CreateDetailViewButton;
|
|
37
|
+
UpdateDetailViewButton: typeof UpdateDetailViewButton;
|
|
38
|
+
Tooltip: typeof Tooltip;
|
|
39
|
+
Breadcrumb: typeof Breadcrumb;
|
|
40
|
+
ContextMenu: typeof ContextMenu;
|
|
41
|
+
Popover: typeof Popover;
|
|
42
|
+
Icons: typeof Icons;
|
|
43
|
+
Table: typeof Table;
|
|
44
|
+
RangeCalendarButton: typeof RangeCalendarButton;
|
|
45
|
+
DataTable: typeof DataTable;
|
|
46
|
+
Drawer: typeof Drawer;
|
|
47
|
+
SelectRecord: typeof SelectRecord;
|
|
48
|
+
Switch: typeof Switch;
|
|
49
|
+
}
|
|
50
|
+
export interface ExtensionUtils {
|
|
51
|
+
ctx: CTX;
|
|
52
|
+
lobb: Lobb;
|
|
53
|
+
location: Location;
|
|
54
|
+
toast: typeof toast;
|
|
55
|
+
showDialog: typeof showDialog;
|
|
56
|
+
getFileFromUser: typeof getFileFromUser;
|
|
57
|
+
components: Components;
|
|
58
|
+
mediaQueries: typeof mediaQueries;
|
|
59
|
+
intlDate: typeof intlDate;
|
|
60
|
+
}
|
|
61
|
+
interface DashboardNav {
|
|
62
|
+
label: string;
|
|
63
|
+
icon: any;
|
|
64
|
+
href?: string;
|
|
65
|
+
onclick?: () => void;
|
|
66
|
+
navs?: DashboardNav[];
|
|
67
|
+
}
|
|
68
|
+
export interface DashboardNavs {
|
|
69
|
+
top?: DashboardNav[];
|
|
70
|
+
middle?: DashboardNav[];
|
|
71
|
+
bottom?: DashboardNav[];
|
|
72
|
+
}
|
|
73
|
+
export interface ExtensionProps {
|
|
74
|
+
utils: ExtensionUtils;
|
|
75
|
+
[key: string]: any;
|
|
76
|
+
}
|
|
77
|
+
export interface Extension {
|
|
78
|
+
name: string;
|
|
79
|
+
onStartup?: (utils: ExtensionUtils) => Promise<void>;
|
|
80
|
+
components?: Record<string, any>;
|
|
81
|
+
dashboardNavs?: DashboardNavs;
|
|
82
|
+
}
|
|
83
|
+
export {};
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { Lobb } from "$lib/Lobb";
|
|
2
|
+
import type { CTX } from "../lib/store.types";
|
|
3
|
+
import type { Button } from "$lib/components/ui/button";
|
|
4
|
+
import type { Input } from "$lib/components/ui/input";
|
|
5
|
+
import type { Separator } from "$lib/components/ui/separator";
|
|
6
|
+
import type { Skeleton } from "$lib/components/ui/skeleton";
|
|
7
|
+
import type LlmButton from "$lib/components/LlmButton.svelte";
|
|
8
|
+
import type Sidebar from "$lib/components/sidebar/sidebar.svelte";
|
|
9
|
+
import type SidebarTrigger from "$lib/components/sidebar/sidebarTrigger.svelte";
|
|
10
|
+
import type CreateDetailViewButton from "$lib/components/detailView/create/createDetailViewButton.svelte";
|
|
11
|
+
import type UpdateDetailViewButton from "$lib/components/detailView/update/updateDetailViewButton.svelte";
|
|
12
|
+
import type { getFileFromUser, mediaQueries } from "$lib/utils";
|
|
13
|
+
import type Table from "$lib/components/dataTable/table.svelte";
|
|
14
|
+
import type { Location } from "@wjfe/n-savant";
|
|
15
|
+
import type RangeCalendarButton from "$lib/components/rangeCalendarButton.svelte";
|
|
16
|
+
import type DataTable from "$lib/components/dataTable/dataTable.svelte";
|
|
17
|
+
import type Drawer from "$lib/components/drawer.svelte";
|
|
18
|
+
import type SelectRecord from "$lib/components/selectRecord.svelte";
|
|
19
|
+
import * as Popover from "$lib/components/ui/popover";
|
|
20
|
+
import * as intlDate from "@internationalized/date";
|
|
21
|
+
import * as Icons from "lucide-svelte"
|
|
22
|
+
import { ContextMenu } from "bits-ui";
|
|
23
|
+
import * as Tooltip from "$lib/components/ui/tooltip";
|
|
24
|
+
import * as Breadcrumb from "$lib/components/ui/breadcrumb";
|
|
25
|
+
import { showDialog } from "$lib/components/confirmationDialog/store.svelte";
|
|
26
|
+
import { toast } from "svelte-sonner";
|
|
27
|
+
import type Drawer from "$lib/components/drawer.svelte";
|
|
28
|
+
import { Switch } from "$lib/components/ui/switch";
|
|
29
|
+
|
|
30
|
+
// extensions utils
|
|
31
|
+
export interface Components {
|
|
32
|
+
Button: typeof Button;
|
|
33
|
+
Input: typeof Input;
|
|
34
|
+
Separator: typeof Separator;
|
|
35
|
+
Skeleton: typeof Skeleton;
|
|
36
|
+
LlmButton: typeof LlmButton;
|
|
37
|
+
Sidebar: typeof Sidebar;
|
|
38
|
+
SidebarTrigger: typeof SidebarTrigger;
|
|
39
|
+
CreateDetailViewButton: typeof CreateDetailViewButton;
|
|
40
|
+
UpdateDetailViewButton: typeof UpdateDetailViewButton;
|
|
41
|
+
Tooltip: typeof Tooltip;
|
|
42
|
+
Breadcrumb: typeof Breadcrumb;
|
|
43
|
+
ContextMenu: typeof ContextMenu;
|
|
44
|
+
Popover: typeof Popover;
|
|
45
|
+
Icons: typeof Icons;
|
|
46
|
+
Table: typeof Table;
|
|
47
|
+
RangeCalendarButton: typeof RangeCalendarButton;
|
|
48
|
+
DataTable: typeof DataTable;
|
|
49
|
+
Drawer: typeof Drawer;
|
|
50
|
+
SelectRecord: typeof SelectRecord,
|
|
51
|
+
Switch: typeof Switch,
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface ExtensionUtils {
|
|
55
|
+
ctx: CTX;
|
|
56
|
+
lobb: Lobb;
|
|
57
|
+
location: Location;
|
|
58
|
+
toast: typeof toast;
|
|
59
|
+
showDialog: typeof showDialog;
|
|
60
|
+
getFileFromUser: typeof getFileFromUser;
|
|
61
|
+
components: Components;
|
|
62
|
+
mediaQueries: typeof mediaQueries;
|
|
63
|
+
intlDate: typeof intlDate;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// dashboard nav
|
|
67
|
+
interface DashboardNav {
|
|
68
|
+
label: string;
|
|
69
|
+
icon: any;
|
|
70
|
+
href?: string;
|
|
71
|
+
onclick?: () => void;
|
|
72
|
+
navs?: DashboardNav[];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface DashboardNavs {
|
|
76
|
+
top?: DashboardNav[];
|
|
77
|
+
middle?: DashboardNav[];
|
|
78
|
+
bottom?: DashboardNav[];
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// extension components base Props
|
|
82
|
+
export interface ExtensionProps {
|
|
83
|
+
utils: ExtensionUtils;
|
|
84
|
+
[key: string]: any;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// extension exported object
|
|
88
|
+
export interface Extension {
|
|
89
|
+
name: string;
|
|
90
|
+
onStartup?: (utils: ExtensionUtils) => Promise<void>;
|
|
91
|
+
components?: Record<string, any>;
|
|
92
|
+
dashboardNavs?: DashboardNavs;
|
|
93
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Components, DashboardNavs, Extension, ExtensionUtils } from "./extension.types";
|
|
2
|
+
export declare function getComponents(): Components;
|
|
3
|
+
export declare function getExtensionUtils(): ExtensionUtils;
|
|
4
|
+
export declare function getExtensionsThatHasDash(): string[];
|
|
5
|
+
export declare function loadExtensions(studioExtensions?: any[]): Promise<Record<string, Extension>>;
|
|
6
|
+
export declare function loadExtensionComponents(name: string, filterByExtensions?: string[]): any[];
|
|
7
|
+
export declare function executeExtensionsOnStartup(): Promise<void>;
|
|
8
|
+
export declare function getDashboardNavs(): DashboardNavs;
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { default as Studio } from "./components/Studio.svelte";
|
|
2
|
+
export { Lobb } from "./Lobb";
|
|
3
|
+
export * from "./utils";
|
|
4
|
+
export * from "./eventSystem";
|
|
5
|
+
export { ctx, lobb } from "./store.svelte";
|
|
6
|
+
export type * from "./store.types";
|
|
7
|
+
export declare function createSet(lastNumber: number): Set<number>;
|
|
8
|
+
export declare function moveElement<T>(array: T[], fromIndex: number, toIndex: number): T[];
|
|
9
|
+
export declare function pxToRem(px: number): number;
|