@ocelescope/plugin 0.7.0 → 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.
Files changed (2) hide show
  1. package/dist/index.js +153 -692
  2. package/package.json +13 -19
package/dist/index.js CHANGED
@@ -1,325 +1,48 @@
1
- import { BarList, FileDropzone, FullScreenUpload, OcelSelect, defineModule, defineModuleRoute, env, getModuleRoute, useInvalidate } from "@ocelescope/core";
2
- import { CheckIcon, Columns2Icon, DatabaseIcon, DownloadIcon, EllipsisVertical, PuzzleIcon, Rows2Icon, Trash2Icon, UploadIcon } from "lucide-react";
1
+ import { FileDropzone, FullScreenUpload, OcelSelect, defineModule, defineModuleRoute, env, getModuleRoute } from "@ocelescope/core";
2
+ import { EllipsisVertical, PuzzleIcon, Trash2Icon, UploadIcon } from "lucide-react";
3
3
  import { useRouter } from "next/router.js";
4
- import { ActionIcon, Anchor, Badge, Box, Breadcrumbs, Button, Card, Center, Container, Divider, Flex, Group, Input, Loader, LoadingOverlay, Menu, Modal, MultiSelect, ScrollArea, Select, SimpleGrid, Skeleton, Slider, Splitter, Stack, Switch, Text, ThemeIcon, Title, Tooltip, useComputedColorScheme } from "@mantine/core";
5
- import { useDeletePlugin, useDisableDiscoveryMethod, useDownloadPluginResults, useEnableDiscoveryMethod, useEventAttributes, useEventCounts, useEventIds, useGetComputedValues, useGetExtensionMeta, useGetPlugin, useGetPluginMethod, useGetResourceMeta, useListDiscoveryMethods, useObjectAttributes, useObjectCounts, useObjectIds, usePluginResult, usePlugins, useRunPlugin, useSavePluginResults, useUpload } from "@ocelescope/api-base";
6
- import { createContext, memo, useCallback, useContext, useEffect, useMemo, useState } from "react";
7
- import { ResourceSelect, Visualization } from "@ocelescope/resources";
4
+ import { ActionIcon, Anchor, Badge, Breadcrumbs, Button, Card, Container, Group, LoadingOverlay, Menu, Modal, ScrollArea, SimpleGrid, Splitter, Stack, Text, ThemeIcon, Title } from "@mantine/core";
5
+ import { useDeletePlugin, useGetPlugin, useGetPluginMethod, usePlugins, useRunPlugin, useUpload } from "@ocelescope/api-base";
6
+ import { PluginForm, ResultSection } from "@ocelescope/plugin-components";
7
+ import { useCallback, useMemo, useState } from "react";
8
+ import { ResourceSelect } from "@ocelescope/resources";
8
9
  import { Controller, useForm, useWatch } from "react-hook-form";
9
- import validator from "@rjsf/validator-ajv8";
10
- import { getDefaultRegistry } from "@rjsf/core";
11
- import Editor from "@monaco-editor/react";
12
10
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
13
- import { useDebouncedValue } from "@mantine/hooks";
14
- import { keepPreviousData } from "@tanstack/react-query";
15
- import dynamic from "next/dynamic.js";
16
11
  import Link from "next/link.js";
17
12
  import { generateColor } from "@marko19907/string-to-color";
18
- //#region src/components/Form/Fields/CodeField.tsx
19
- const EDITOR_HEIGHT = 160;
20
- const OPTIONS = {
21
- minimap: { enabled: false },
22
- quickSuggestions: false,
23
- suggestOnTriggerCharacters: false,
24
- wordBasedSuggestions: "off",
25
- lineNumbers: "on",
26
- scrollBeyondLastLine: false,
27
- automaticLayout: true,
28
- scrollbar: { alwaysConsumeMouseWheel: false },
29
- wordWrap: "on",
30
- tabSize: 2,
31
- fontSize: 13,
32
- padding: {
33
- top: 8,
34
- bottom: 8
35
- }
36
- };
37
- const CodeField = memo(({ schema, required, formData, onChange, rawErrors, disabled, readonly, fieldPathId: { path } }) => {
38
- const meta = schema["x-ui-meta"];
39
- const colorScheme = useComputedColorScheme("light", { getInitialValueInEffect: true });
40
- const readOnly = disabled || readonly;
41
- const options = useMemo(() => ({
42
- ...OPTIONS,
43
- readOnly
44
- }), [readOnly]);
45
- return /* @__PURE__ */ jsx(Input.Wrapper, {
46
- label: schema.title,
47
- description: schema.description,
48
- required,
49
- error: rawErrors?.[0],
50
- children: /* @__PURE__ */ jsx(Box, {
51
- style: {
52
- border: "1px solid var(--mantine-color-default-border)",
53
- borderRadius: "var(--mantine-radius-default)",
54
- overflow: "hidden"
55
- },
56
- children: /* @__PURE__ */ jsx(Editor, {
57
- height: EDITOR_HEIGHT,
58
- language: meta?.language ?? "plaintext",
59
- value: formData ?? "",
60
- theme: colorScheme === "dark" ? "vs-dark" : "light",
61
- options,
62
- onChange: (value) => onChange(value ?? "", path),
63
- loading: /* @__PURE__ */ jsx(Skeleton, {
64
- height: EDITOR_HEIGHT,
65
- radius: 0
66
- })
67
- })
68
- })
69
- });
70
- });
71
- //#endregion
72
- //#region src/components/Form/PluginFormContext.tsx
73
- const PluginFormContext = createContext(void 0);
74
- const usePluginForm = () => {
75
- const context = useContext(PluginFormContext);
76
- if (!context) throw new Error("Custom plugin fields have to be rendered inside <PluginFormProvider>");
77
- return context;
78
- };
79
- const useOcelId = (ocelRef) => {
80
- const { control } = usePluginForm();
81
- return useWatch({
82
- control,
83
- name: `input_ocels.${ocelRef}`
84
- });
85
- };
86
- const PluginFormProvider = ({ pluginId, methodName, control, children }) => {
87
- const value = useMemo(() => ({
88
- pluginId,
89
- methodName,
90
- control
91
- }), [
92
- pluginId,
93
- methodName,
94
- control
95
- ]);
96
- return /* @__PURE__ */ jsx(PluginFormContext.Provider, {
97
- value,
98
- children
99
- });
100
- };
101
- //#endregion
102
- //#region src/components/Form/Fields/ComputedSelect.tsx
13
+ //#region src/components/Form/index.tsx
103
14
  const compact = (record) => Object.fromEntries(Object.entries(record ?? {}).filter(([, value]) => !!value));
104
- const useSelectOptions = (provider) => {
105
- const { pluginId, methodName, control } = usePluginForm();
106
- const formValues = useWatch({ control });
107
- const [debouncedBody] = useDebouncedValue(useMemo(() => JSON.stringify({
108
- input: formValues.input,
109
- input_ocels: compact(formValues.input_ocels),
110
- input_resources: compact(formValues.input_resources)
111
- }), [formValues]), 300);
112
- const { data } = useGetComputedValues(pluginId, methodName, provider, useMemo(() => JSON.parse(debouncedBody), [debouncedBody]), { query: {
113
- placeholderData: keepPreviousData,
114
- refetchOnWindowFocus: false,
115
- refetchOnReconnect: false
116
- } });
117
- return { options: data };
118
- };
119
- const ComputedSelect = memo(({ schema, required, formData, onChange, fieldPathId: { path } }) => {
120
- const meta = schema["x-ui-meta"];
121
- const isMulti = schema.type === "array";
122
- const SelectComponent = isMulti ? MultiSelect : Select;
123
- const { options } = useSelectOptions(meta.provider);
124
- return /* @__PURE__ */ jsx(SelectComponent, {
125
- label: schema.title,
126
- description: schema.description,
127
- required,
128
- value: formData ?? (isMulti ? [] : null),
129
- onChange: (value) => onChange(value, path),
130
- data: options
131
- });
132
- });
133
- //#endregion
134
- //#region src/components/Form/Fields/EnumMultiSelect.tsx
135
- const itemsOf = (schema, registry) => {
136
- const { items } = schema;
137
- if (!items || typeof items === "boolean" || Array.isArray(items)) return;
138
- return registry.schemaUtils.retrieveSchema(items);
139
- };
140
- const isStringEnumArray = (schema, registry) => {
141
- if (schema.type !== "array") return false;
142
- const enumValues = itemsOf(schema, registry)?.enum;
143
- return Array.isArray(enumValues) && enumValues.length > 0 && enumValues.every((value) => typeof value === "string");
144
- };
145
- const EnumMultiSelect = memo(({ schema, registry, required, formData, onChange, rawErrors, disabled, readonly, fieldPathId: { path } }) => {
146
- const options = useMemo(() => itemsOf(schema, registry)?.enum ?? [], [schema, registry]);
147
- return /* @__PURE__ */ jsx(MultiSelect, {
148
- label: schema.title,
149
- description: schema.description,
150
- required,
151
- error: rawErrors?.[0],
152
- data: options,
153
- value: Array.isArray(formData) ? formData : [],
154
- onChange: (value) => onChange(value, path),
155
- disabled: disabled || readonly,
156
- clearable: true,
157
- searchable: true
158
- });
159
- });
160
- //#endregion
161
- //#region src/components/Form/Fields/OCELField.tsx
162
- const AttributeSelect = (useAttributes) => ({ ocelId, isMulti, value, ...props }) => {
163
- const { data: attributes } = useAttributes(ocelId);
164
- const names = useMemo(() => [...new Set((attributes ?? []).map(({ name }) => name))], [attributes]);
165
- return /* @__PURE__ */ jsx(isMulti ? MultiSelect : Select, {
166
- value: value ?? (isMulti ? [] : null),
167
- ...props,
168
- data: names,
169
- disabled: !ocelId,
170
- clearable: true
171
- });
172
- };
173
- const TypeSelect = (useCounts) => ({ ocelId, isMulti, value, ...props }) => {
174
- const { data: counts } = useCounts(ocelId);
175
- const types = useMemo(() => Object.keys(counts ?? {}), [counts]);
176
- return /* @__PURE__ */ jsx(isMulti ? MultiSelect : Select, {
177
- ...props,
178
- value: value ?? (isMulti ? [] : null),
179
- data: types,
180
- clearable: true
181
- });
182
- };
183
- const IdSelect = (useIds) => ({ ocelId, isMulti, value, ...props }) => {
184
- const [search, setSearch] = useState();
185
- const [debouncedSearch] = useDebouncedValue(search, 300);
186
- const { data: ids } = useIds(ocelId, { search: debouncedSearch });
187
- return /* @__PURE__ */ jsx(isMulti ? MultiSelect : Select, {
188
- ...props,
189
- value: value ?? (isMulti ? [] : null),
190
- data: ids?.response,
191
- searchValue: search,
192
- onSearchChange: setSearch,
193
- searchable: true
194
- });
195
- };
196
- const OCEL_FIELDS = {
197
- event_type: TypeSelect(useEventCounts),
198
- object_type: TypeSelect(useObjectCounts),
199
- event_attribute: AttributeSelect(useEventAttributes),
200
- object_attribute: AttributeSelect(useObjectAttributes),
201
- event_id: IdSelect(useEventIds),
202
- object_id: IdSelect(useObjectIds)
203
- };
204
- const OCELField = memo(({ schema, required, formData, onChange, fieldPathId: { path } }) => {
205
- const ocelRef = schema["x-ui-meta"]?.["ocel_id"];
206
- const ocelFieldType = schema["x-ui-meta"]?.["field_type"];
207
- const ocelId = useOcelId(ocelRef);
208
- const isMulti = schema.type === "array";
209
- useEffect(() => {
210
- onChange(isMulti ? [] : void 0, path);
211
- }, [
212
- ocelId,
213
- isMulti,
214
- onChange,
215
- path
216
- ]);
217
- const Selector = OCEL_FIELDS[ocelFieldType];
218
- return Selector && /* @__PURE__ */ jsx(Selector, {
219
- ocelId,
220
- isMulti,
221
- value: formData,
222
- onChange: (value) => onChange(value, path),
223
- label: schema.title,
224
- description: schema.description,
225
- disabled: !ocelId,
226
- required
227
- }, ocelId);
228
- });
229
- //#endregion
230
- //#region src/components/Form/Fields/SliderField.tsx
231
- const decimalsOf = (step) => String(step).split(".")[1]?.length ?? 0;
232
- const SliderField = memo(({ schema, required, formData, onChange, rawErrors, disabled, readonly, fieldPathId: { path } }) => {
233
- const meta = schema["x-ui-meta"];
234
- const { min, max } = meta;
235
- const step = meta.step ?? (schema.type === "integer" ? 1 : (max - min) / 100);
236
- const decimals = decimalsOf(step);
237
- const marks = useMemo(() => meta.marks?.map((value) => ({
238
- value,
239
- label: value.toFixed(decimals)
240
- })), [meta.marks, decimals]);
241
- const fallback = typeof schema.default === "number" ? schema.default : min;
242
- const value = typeof formData === "number" ? formData : fallback;
243
- return /* @__PURE__ */ jsx(Input.Wrapper, {
244
- label: schema.title,
245
- description: schema.description,
246
- required,
247
- error: rawErrors?.[0],
248
- children: /* @__PURE__ */ jsx(Box, {
249
- px: marks ? "md" : void 0,
250
- pb: marks ? "xl" : void 0,
251
- children: /* @__PURE__ */ jsx(Slider, {
252
- mt: "xs",
253
- min,
254
- max,
255
- step,
256
- marks,
257
- label: (current) => current.toFixed(decimals),
258
- value,
259
- onChange: (current) => onChange(current, path),
260
- disabled: disabled || readonly
261
- })
262
- })
15
+ const ConfigurationSection = ({ control, schema, pluginId, methodName, onSubmit }) => {
16
+ const inputResources = useWatch({
17
+ control,
18
+ name: "input_resources"
263
19
  });
264
- });
265
- //#endregion
266
- //#region src/components/Form/Fields/index.tsx
267
- const DefaultSchemaField = getDefaultRegistry().fields.SchemaField;
268
- const CUSTOM_FORM_MAP = {
269
- ocel: OCELField,
270
- computed_select: ComputedSelect,
271
- code: CodeField,
272
- slider: SliderField
273
- };
274
- const resolveField = ({ schema, registry }) => {
275
- const uiType = schema["x-ui-meta"]?.type;
276
- if (uiType) return CUSTOM_FORM_MAP[uiType];
277
- if (isStringEnumArray(schema, registry)) return EnumMultiSelect;
278
- };
279
- const CustomSchemaField = (props) => {
280
- const FormComponent = resolveField(props);
281
- return FormComponent ? /* @__PURE__ */ jsx(FormComponent, { ...props }) : /* @__PURE__ */ jsx(DefaultSchemaField, { ...props });
282
- };
283
- //#endregion
284
- //#region src/components/Form/MantineForm.tsx
285
- const Form = dynamic(() => import("@rjsf/mantine").then((m) => m.Form), { ssr: false });
286
- //#endregion
287
- //#region src/components/Form/PluginForm.tsx
288
- const FIELDS = { SchemaField: CustomSchemaField };
289
- const PluginForm = ({ schema, control, pluginId, methodName, onSubmit }) => {
290
- const formSchema = useMemo(() => ({
291
- ...schema,
292
- title: ""
293
- }), [schema]);
294
- return /* @__PURE__ */ jsx(PluginFormProvider, {
20
+ return /* @__PURE__ */ jsx(Controller, {
295
21
  control,
296
- pluginId,
297
- methodName,
298
- children: /* @__PURE__ */ jsx(Controller, {
299
- control,
300
- name: "input",
301
- render: ({ field }) => /* @__PURE__ */ jsx(Form, {
302
- schema: formSchema,
303
- formData: field.value,
304
- validator,
305
- fields: FIELDS,
306
- onChange: ({ formData }) => field.onChange(formData),
307
- onSubmit
308
- })
22
+ name: "input",
23
+ render: ({ field }) => /* @__PURE__ */ jsx(PluginForm, {
24
+ pluginId,
25
+ methodName,
26
+ schema,
27
+ inputResources,
28
+ formData: field.value,
29
+ onChange: ({ formData }) => field.onChange(formData),
30
+ onSubmit
309
31
  })
310
32
  });
311
33
  };
312
- //#endregion
313
- //#region src/components/Form/index.tsx
314
34
  const PluginInput = ({ pluginId, method, onSuccess }) => {
315
35
  const { mutate: runPlugin } = useRunPlugin({ mutation: { onSuccess } });
316
- const { control, handleSubmit } = useForm({ defaultValues: {
317
- input_ocels: Object.fromEntries(Object.keys(method.input_ocels ?? {}).map((name) => [name, void 0])),
318
- input_resources: Object.fromEntries(Object.keys(method.input_resources ?? {}).map((name) => [name, void 0])),
36
+ const defaultValue = {
37
+ input_resources: Object.fromEntries(method.inputs.map(({ name }) => [name, void 0])),
319
38
  formData: {}
320
- } });
321
- const onSubmit = useCallback(() => handleSubmit((data) => runPlugin({
322
- data,
39
+ };
40
+ const { control, handleSubmit } = useForm({ defaultValues: defaultValue });
41
+ const onSubmit = useCallback(() => handleSubmit(({ input_resources, input }) => runPlugin({
42
+ data: {
43
+ input_resources: compact(input_resources),
44
+ input
45
+ },
323
46
  methodName: method.name,
324
47
  pluginId
325
48
  }))(), [
@@ -330,46 +53,40 @@ const PluginInput = ({ pluginId, method, onSuccess }) => {
330
53
  ]);
331
54
  return /* @__PURE__ */ jsxs(Stack, {
332
55
  gap: "md",
333
- children: [
334
- Object.entries(method.input_ocels ?? {}).map(([name, { label, description, extension }]) => /* @__PURE__ */ jsx(Controller, {
335
- control,
336
- name: `input_ocels.${name}`,
337
- rules: { required: "Please select a value" },
338
- render: ({ field, fieldState }) => /* @__PURE__ */ jsx(OcelSelect, {
339
- label,
340
- required: true,
341
- extension: extension ?? void 0,
342
- description,
343
- error: fieldState.error?.message,
344
- onChange: field.onChange,
345
- value: field.value
346
- })
347
- }, name)),
348
- Object.entries(method.input_resources ?? {}).map(([name, [resource_type, { label, description }]]) => /* @__PURE__ */ jsx(Controller, {
349
- control,
350
- name: `input_resources.${name}`,
351
- rules: { required: "Please select a value" },
352
- render: ({ field, fieldState }) => /* @__PURE__ */ jsx(ResourceSelect, {
353
- label,
354
- required: true,
355
- type: resource_type,
356
- description,
357
- onChange: field.onChange,
358
- error: fieldState.error?.message,
359
- value: field.value
360
- })
361
- }, name)),
362
- method.input_schema ? /* @__PURE__ */ jsx(PluginForm, {
363
- pluginId,
364
- methodName: method.name,
365
- schema: method.input_schema,
366
- control,
367
- onSubmit
368
- }) : /* @__PURE__ */ jsx(Button, {
369
- onClick: onSubmit,
370
- children: "Submit"
56
+ children: [method.inputs.map((io) => /* @__PURE__ */ jsx(Controller, {
57
+ control,
58
+ name: `input_resources.${io.name}`,
59
+ rules: !io.is_optional ? { required: "Please select a value" } : {},
60
+ render: ({ field, fieldState }) => io.type === "ocel" ? /* @__PURE__ */ jsx(OcelSelect, {
61
+ label: io.label,
62
+ clearable: io.is_optional,
63
+ required: !io.is_optional,
64
+ description: io.description,
65
+ searchable: true,
66
+ error: fieldState.error?.message,
67
+ onChange: field.onChange,
68
+ value: field.value
69
+ }) : /* @__PURE__ */ jsx(ResourceSelect, {
70
+ clearable: io.is_optional,
71
+ label: io.label,
72
+ required: !io.is_optional,
73
+ type: io.schema_id,
74
+ description: io.description,
75
+ onChange: field.onChange,
76
+ error: fieldState.error?.message,
77
+ value: field.value,
78
+ searchable: true
371
79
  })
372
- ]
80
+ }, io.name)), method.configuration_schema ? /* @__PURE__ */ jsx(ConfigurationSection, {
81
+ control,
82
+ pluginId,
83
+ methodName: method.name,
84
+ schema: method.configuration_schema,
85
+ onSubmit
86
+ }) : /* @__PURE__ */ jsx(Button, {
87
+ onClick: onSubmit,
88
+ children: "Submit"
89
+ })]
373
90
  });
374
91
  };
375
92
  //#endregion
@@ -396,7 +113,7 @@ const PluginBreadcrumbs = () => {
396
113
  pluginId: plugin?.id,
397
114
  methodName: void 0
398
115
  } },
399
- children: plugin?.meta.label
116
+ children: plugin?.label
400
117
  }),
401
118
  pluginMethod && /* @__PURE__ */ jsx(Anchor, {
402
119
  component: Link,
@@ -410,186 +127,6 @@ const PluginBreadcrumbs = () => {
410
127
  ] });
411
128
  };
412
129
  //#endregion
413
- //#region src/components/ResultSection/ResultSection.tsx
414
- const ResourceView = ({ resource }) => {
415
- return /* @__PURE__ */ jsx(Visualization, { visualization: resource.visualization });
416
- };
417
- const OCELView = ({ ocel }) => {
418
- return /* @__PURE__ */ jsxs(Stack, {
419
- gap: "md",
420
- children: [/* @__PURE__ */ jsx(BarList, {
421
- data: ocel.activity_count,
422
- labelHeader: "Activity"
423
- }), /* @__PURE__ */ jsx(BarList, {
424
- data: ocel.object_count,
425
- labelHeader: "Object type"
426
- })]
427
- });
428
- };
429
- const entityTypeOf = (output) => output.type === "ocel" ? "ocel" : output.resource_type || "resource";
430
- const ResultLabel = ({ label, entityType, bold }) => /* @__PURE__ */ jsxs(Group, {
431
- gap: "xs",
432
- wrap: "nowrap",
433
- children: [/* @__PURE__ */ jsx(Text, {
434
- fw: bold ? 600 : void 0,
435
- truncate: true,
436
- children: label
437
- }), /* @__PURE__ */ jsx(Badge, {
438
- size: "sm",
439
- color: generateColor(entityType),
440
- children: entityType
441
- })]
442
- });
443
- const ResultSection = ({ pluginId, methodName, taskId }) => {
444
- const { data: pluginSummary } = usePluginResult(pluginId, methodName, taskId, { query: { refetchInterval: ({ state }) => {
445
- if (state.data == null) return 1e3;
446
- return false;
447
- } } });
448
- const [selected, setSelected] = useState([0]);
449
- const [orientation, setOrientation] = useState("vertical");
450
- const { mutate: saveResults, isPending: isSaving, isSuccess: isSaved, reset: resetSave } = useSavePluginResults();
451
- const { mutate: downloadResults, isPending: isDownloading } = useDownloadPluginResults({ request: { responseType: "blob" } });
452
- const options = useMemo(() => (pluginSummary ?? []).map((output) => {
453
- return {
454
- value: output.result_index,
455
- label: entityTypeOf(output),
456
- entityType: output.type
457
- };
458
- }), [pluginSummary]);
459
- if (!pluginSummary) return /* @__PURE__ */ jsx(Center, {
460
- h: "100%",
461
- children: /* @__PURE__ */ jsxs(Stack, {
462
- align: "center",
463
- gap: "sm",
464
- children: [/* @__PURE__ */ jsx(Loader, {}), /* @__PURE__ */ jsx(Text, {
465
- c: "dimmed",
466
- children: "Running…"
467
- })]
468
- })
469
- });
470
- if (pluginSummary.length === 0) return /* @__PURE__ */ jsx(Center, {
471
- h: "100%",
472
- children: /* @__PURE__ */ jsx(Text, {
473
- c: "dimmed",
474
- children: "This run produced no results."
475
- })
476
- });
477
- const handleSelectionChange = (value) => {
478
- setSelected(value);
479
- resetSave();
480
- };
481
- const handleDownload = () => {
482
- downloadResults({
483
- pluginId,
484
- methodName,
485
- taskId,
486
- data: { indices: selected }
487
- }, { onSuccess: (data) => {
488
- const url = URL.createObjectURL(data);
489
- const anchor = document.createElement("a");
490
- anchor.href = url;
491
- anchor.download = `${methodName}_results.zip`;
492
- anchor.click();
493
- URL.revokeObjectURL(url);
494
- } });
495
- };
496
- const handleSaveToSession = () => {
497
- saveResults({
498
- pluginId,
499
- methodName,
500
- taskId,
501
- data: { indices: selected }
502
- });
503
- };
504
- return /* @__PURE__ */ jsxs(Stack, {
505
- gap: 0,
506
- h: "100%",
507
- children: [/* @__PURE__ */ jsxs(Group, {
508
- px: "sm",
509
- py: "xs",
510
- wrap: "nowrap",
511
- style: { borderBottom: "1px solid var(--mantine-color-default-border)" },
512
- children: [options.length > 1 ? /* @__PURE__ */ jsx(MultiSelect, {
513
- flex: 1,
514
- data: options,
515
- onChange: handleSelectionChange,
516
- value: selected,
517
- placeholder: "Select results to display",
518
- searchable: true,
519
- clearable: true,
520
- comboboxProps: { withinPortal: true },
521
- renderOption: ({ option }) => {
522
- const output = pluginSummary[Number(option.value)];
523
- return /* @__PURE__ */ jsx(ResultLabel, {
524
- label: option.label,
525
- entityType: output ? entityTypeOf(output) : option.label
526
- });
527
- }
528
- }) : options[0] && /* @__PURE__ */ jsx(ResultLabel, {
529
- label: options[0].label,
530
- entityType: options[0].entityType,
531
- bold: true
532
- }), /* @__PURE__ */ jsxs(Group, {
533
- gap: "xs",
534
- ml: "auto",
535
- wrap: "nowrap",
536
- children: [
537
- selected.length > 1 && /* @__PURE__ */ jsx(Tooltip, {
538
- label: orientation === "vertical" ? "Show side by side" : "Stack vertically",
539
- children: /* @__PURE__ */ jsx(ActionIcon, {
540
- variant: "default",
541
- size: "lg",
542
- onClick: () => setOrientation((prev) => prev === "vertical" ? "horizontal" : "vertical"),
543
- children: orientation === "vertical" ? /* @__PURE__ */ jsx(Columns2Icon, { size: 16 }) : /* @__PURE__ */ jsx(Rows2Icon, { size: 16 })
544
- })
545
- }),
546
- /* @__PURE__ */ jsx(Button, {
547
- variant: "default",
548
- leftSection: /* @__PURE__ */ jsx(DownloadIcon, { size: 16 }),
549
- onClick: handleDownload,
550
- loading: isDownloading,
551
- disabled: selected.length === 0,
552
- children: "Download"
553
- }),
554
- /* @__PURE__ */ jsx(Button, {
555
- variant: "light",
556
- color: isSaved ? "green" : void 0,
557
- leftSection: isSaved ? /* @__PURE__ */ jsx(CheckIcon, { size: 16 }) : /* @__PURE__ */ jsx(DatabaseIcon, { size: 16 }),
558
- onClick: handleSaveToSession,
559
- loading: isSaving,
560
- disabled: selected.length === 0,
561
- children: isSaved ? "Saved" : "Save to session"
562
- })
563
- ]
564
- })]
565
- }), selected.length === 0 ? /* @__PURE__ */ jsx(Center, {
566
- style: { flex: 1 },
567
- children: /* @__PURE__ */ jsx(Text, {
568
- c: "dimmed",
569
- children: "Select one or more results to display."
570
- })
571
- }) : /* @__PURE__ */ jsx(Splitter, {
572
- orientation,
573
- lineSize: 4,
574
- handleColor: "var(--mantine-color-default-border)",
575
- style: {
576
- flex: 1,
577
- minHeight: 0
578
- },
579
- children: pluginSummary?.filter(({ result_index }) => selected.includes(result_index)).map((output, index) => /* @__PURE__ */ jsx(Splitter.Pane, {
580
- defaultSize: 100 / selected.length,
581
- min: "15%",
582
- children: /* @__PURE__ */ jsx(Box, {
583
- h: "100%",
584
- p: "xs",
585
- pos: "relative",
586
- children: output.type === "ocel" ? /* @__PURE__ */ jsx(OCELView, { ocel: output }) : /* @__PURE__ */ jsx(ResourceView, { resource: output })
587
- })
588
- }, index))
589
- }, `${orientation}:${selected.join(",")}`)]
590
- });
591
- };
592
- //#endregion
593
130
  //#region src/pages/MethodPage.tsx
594
131
  const COLLAPSED_SIZES = [0, 100];
595
132
  const SPLIT_SIZES = [75, 25];
@@ -602,78 +139,53 @@ const MethodPage = ({ pluginId, methodName }) => {
602
139
  setCurrentTask(taskId);
603
140
  setSizes((prev) => prev[0] === 0 ? SPLIT_SIZES : prev);
604
141
  };
605
- return /* @__PURE__ */ jsx(Flex, {
606
- direction: "column",
142
+ return /* @__PURE__ */ jsxs(Splitter, {
143
+ sizes,
144
+ onSizeChange: setSizes,
145
+ lineSize: 4,
146
+ handleColor: "var(--mantine-color-default-border)",
607
147
  h: "100%",
608
- children: /* @__PURE__ */ jsxs(Splitter, {
609
- sizes,
610
- onSizeChange: setSizes,
611
- lineSize: 4,
612
- handleColor: "var(--mantine-color-default-border)",
613
- px: "xs",
614
- style: {
615
- flex: 1,
616
- minHeight: 0
617
- },
618
- children: [/* @__PURE__ */ jsx(Splitter.Pane, {
619
- defaultSize: 0,
620
- min: "20%",
621
- collapsible: true,
622
- children: /* @__PURE__ */ jsx(Box, {
623
- h: "100%",
624
- pos: "relative",
625
- style: { overflow: "hidden" },
626
- children: currentTask ? /* @__PURE__ */ jsx(ResultSection, {
627
- pluginId,
628
- methodName,
629
- taskId: currentTask
630
- }) : /* @__PURE__ */ jsx(Center, {
631
- h: "100%",
632
- p: "md",
633
- children: /* @__PURE__ */ jsx(Text, {
148
+ children: [/* @__PURE__ */ jsx(Splitter.Pane, {
149
+ defaultSize: 0,
150
+ min: "20%",
151
+ collapsible: true,
152
+ children: /* @__PURE__ */ jsx(ResultSection, { taskId: currentTask })
153
+ }), /* @__PURE__ */ jsx(Splitter.Pane, {
154
+ defaultSize: 100,
155
+ min: "20%",
156
+ collapsible: true,
157
+ children: /* @__PURE__ */ jsx(ScrollArea, {
158
+ h: "100%",
159
+ type: "auto",
160
+ children: /* @__PURE__ */ jsxs(Stack, {
161
+ gap: "sm",
162
+ p: "md",
163
+ maw: 640,
164
+ mx: "auto",
165
+ w: "100%",
166
+ children: [
167
+ /* @__PURE__ */ jsx(Group, {
168
+ justify: "center",
169
+ children: /* @__PURE__ */ jsx(PluginBreadcrumbs, {})
170
+ }),
171
+ /* @__PURE__ */ jsx(Title, {
172
+ ta: "center",
173
+ children: pluginMethod.label ?? methodName
174
+ }),
175
+ pluginMethod.description && /* @__PURE__ */ jsx(Text, {
634
176
  c: "dimmed",
635
177
  ta: "center",
636
- children: "Run the method to see its results here."
178
+ children: pluginMethod.description
179
+ }),
180
+ /* @__PURE__ */ jsx(PluginInput, {
181
+ onSuccess: handleSuccess,
182
+ pluginId,
183
+ method: pluginMethod
637
184
  })
638
- })
639
- })
640
- }), /* @__PURE__ */ jsx(Splitter.Pane, {
641
- defaultSize: 100,
642
- min: "20%",
643
- collapsible: true,
644
- children: /* @__PURE__ */ jsx(ScrollArea, {
645
- h: "100%",
646
- type: "auto",
647
- children: /* @__PURE__ */ jsxs(Stack, {
648
- gap: "sm",
649
- p: "md",
650
- maw: 640,
651
- mx: "auto",
652
- w: "100%",
653
- children: [
654
- /* @__PURE__ */ jsx(Group, {
655
- justify: "center",
656
- children: /* @__PURE__ */ jsx(PluginBreadcrumbs, {})
657
- }),
658
- /* @__PURE__ */ jsx(Title, {
659
- ta: "center",
660
- children: pluginMethod.label ?? methodName
661
- }),
662
- pluginMethod.description && /* @__PURE__ */ jsx(Text, {
663
- c: "dimmed",
664
- ta: "center",
665
- children: pluginMethod.description
666
- }),
667
- /* @__PURE__ */ jsx(PluginInput, {
668
- onSuccess: handleSuccess,
669
- pluginId,
670
- method: pluginMethod
671
- })
672
- ]
673
- })
185
+ ]
674
186
  })
675
- })]
676
- })
187
+ })
188
+ })]
677
189
  });
678
190
  };
679
191
  //#endregion
@@ -738,111 +250,58 @@ const GenericCard = ({ menuItems, title, description, link, tags, version }) =>
738
250
  //#region src/pages/PluginPage.tsx
739
251
  const MethodCard = ({ method }) => {
740
252
  const { query } = useRouter();
741
- const { data: resourceMeta = {} } = useGetResourceMeta();
742
- const { data: extensionMeta = {} } = useGetExtensionMeta();
743
- const tags = useMemo(() => {
744
- const extensions = Object.values(method.input_ocels ?? {}).map(({ extension }) => extension ? extensionMeta[extension]?.label : void 0);
745
- const inputResources = Object.values(method.input_resources ?? {}).map(([resourceName]) => resourceMeta[resourceName]?.label ?? resourceName);
746
- const resultNames = (method.results ?? []).filter((result) => result.type === "resource").map((result) => resourceMeta[result.resource_type]?.label ?? result.resource_type);
747
- return Array.from(/* @__PURE__ */ new Set([
748
- ...inputResources,
749
- ...resultNames,
750
- ...extensions
751
- ])).filter((tag) => !!tag);
752
- }, [
753
- resourceMeta,
754
- extensionMeta,
755
- method
756
- ]);
253
+ const tags = Array.from(new Set([...method.inputs, ...method.outputs].map((io) => io.type === "ocel" ? "OCEL" : io.resource_label)));
757
254
  return /* @__PURE__ */ jsx(GenericCard, {
758
255
  title: method.label ?? method.name,
759
256
  description: method.description ?? "",
760
- tags,
761
257
  link: {
762
258
  href: { query: {
763
259
  ...query,
764
260
  methodName: method.name
765
261
  } },
766
262
  children: "Run Method"
767
- }
263
+ },
264
+ tags
768
265
  });
769
266
  };
770
267
  const PluginPage = ({ pluginId }) => {
771
268
  const { data: plugin } = useGetPlugin(pluginId);
772
- const { data: allMethods = [] } = useListDiscoveryMethods();
773
- const invalidate = useInvalidate();
774
- const onSuccess = () => invalidate(["discoveryMethods"]);
775
- const { mutate: disableMethod } = useDisableDiscoveryMethod({ mutation: { onSuccess } });
776
- const { mutate: enableMethod } = useEnableDiscoveryMethod({ mutation: { onSuccess } });
777
- const discoveryMethods = useMemo(() => allMethods.flatMap((meta) => meta.variants.filter((v) => v.pluginId === pluginId).map((v) => ({
778
- name: meta.name,
779
- variant: v
780
- }))), [allMethods, pluginId]);
781
- return /* @__PURE__ */ jsx(Container, {
269
+ return /* @__PURE__ */ jsxs(Container, {
782
270
  fluid: true,
783
- children: /* @__PURE__ */ jsxs(Stack, { children: [
784
- /* @__PURE__ */ jsxs(Stack, {
785
- gap: 0,
786
- align: "center",
787
- children: [
788
- /* @__PURE__ */ jsx(PluginBreadcrumbs, {}),
789
- /* @__PURE__ */ jsxs(Title, {
790
- mt: "xs",
791
- children: [" ", plugin?.meta.label]
792
- }),
793
- /* @__PURE__ */ jsx(Text, {
794
- c: "dimmed",
795
- children: plugin?.meta.description
796
- })
797
- ]
798
- }),
799
- /* @__PURE__ */ jsx(SimpleGrid, {
800
- cols: {
801
- base: 1,
802
- sm: 2,
803
- lg: 4
804
- },
805
- spacing: {
806
- base: 10,
807
- sm: "xl"
808
- },
809
- verticalSpacing: {
810
- base: "md",
811
- sm: "xl"
812
- },
813
- children: plugin?.methods.map((method) => /* @__PURE__ */ jsx(MethodCard, {
814
- method,
815
- pluginId: plugin.id
816
- }, method.name))
817
- }),
818
- discoveryMethods.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Divider, {}), /* @__PURE__ */ jsxs(Stack, {
819
- gap: "xs",
820
- children: [/* @__PURE__ */ jsx(Title, {
821
- order: 4,
822
- children: "Discovery Methods"
823
- }), discoveryMethods.map(({ name, variant }) => /* @__PURE__ */ jsxs(Group, {
824
- justify: "space-between",
825
- children: [/* @__PURE__ */ jsxs(Stack, {
826
- gap: 0,
827
- children: [/* @__PURE__ */ jsx(Text, {
828
- fw: 500,
829
- children: name
830
- }), variant.description && /* @__PURE__ */ jsx(Text, {
831
- size: "sm",
832
- c: "dimmed",
833
- children: variant.description
834
- })]
835
- }), /* @__PURE__ */ jsx(Switch, {
836
- label: "Active in Discovery",
837
- checked: variant.enabled ?? true,
838
- onChange: (e) => {
839
- if (e.currentTarget.checked) enableMethod({ methodId: variant.methodId });
840
- else disableMethod({ methodId: variant.methodId });
841
- }
842
- })]
843
- }, variant.methodId))]
844
- })] })
845
- ] })
271
+ p: "md",
272
+ children: [/* @__PURE__ */ jsxs(Stack, {
273
+ gap: 0,
274
+ align: "center",
275
+ children: [
276
+ /* @__PURE__ */ jsx(PluginBreadcrumbs, {}),
277
+ /* @__PURE__ */ jsxs(Title, {
278
+ mt: "xs",
279
+ children: [" ", plugin?.label]
280
+ }),
281
+ /* @__PURE__ */ jsx(Text, {
282
+ c: "dimmed",
283
+ children: plugin?.description
284
+ })
285
+ ]
286
+ }), /* @__PURE__ */ jsx(SimpleGrid, {
287
+ cols: {
288
+ base: 1,
289
+ sm: 2,
290
+ lg: 4
291
+ },
292
+ spacing: {
293
+ base: 10,
294
+ sm: "xl"
295
+ },
296
+ verticalSpacing: {
297
+ base: "md",
298
+ sm: "xl"
299
+ },
300
+ children: plugin?.methods.map((method) => /* @__PURE__ */ jsx(MethodCard, {
301
+ method,
302
+ pluginId: plugin.id
303
+ }, method.name))
304
+ })]
846
305
  });
847
306
  };
848
307
  //#endregion
@@ -858,7 +317,7 @@ const PluginUploadSection = ({ onSuccess }) => {
858
317
  //#endregion
859
318
  //#region src/components/PluginCard/PluginCard.tsx
860
319
  const PluginCard = ({ plugin }) => {
861
- const { description, label, version } = plugin.meta;
320
+ const { description, label, version } = plugin;
862
321
  const { mutate: deletePlugin } = useDeletePlugin();
863
322
  return /* @__PURE__ */ jsx(GenericCard, {
864
323
  title: label,
@@ -932,6 +391,7 @@ const PluginsOverview = () => {
932
391
  const { data: plugins } = usePlugins();
933
392
  return /* @__PURE__ */ jsxs(Stack, {
934
393
  gap: "md",
394
+ p: "md",
935
395
  children: [/* @__PURE__ */ jsxs("div", {
936
396
  style: { maxWidth: 720 },
937
397
  children: [/* @__PURE__ */ jsx(Title, {
@@ -1004,6 +464,11 @@ const PluginRoute = () => {
1004
464
  }
1005
465
  return /* @__PURE__ */ jsx(PluginsOverview, {});
1006
466
  };
467
+ var pages_default = defineModuleRoute({
468
+ name: "plugins",
469
+ label: "Plugins",
470
+ component: PluginRoute
471
+ });
1007
472
  //#endregion
1008
473
  //#region src/index.ts
1009
474
  var src_default = defineModule({
@@ -1011,11 +476,7 @@ var src_default = defineModule({
1011
476
  description: "Run process mining plugins inside ocelescope",
1012
477
  label: "Plugins",
1013
478
  authors: [{ name: "Öztürk, Görkem-Emre" }],
1014
- routes: [defineModuleRoute({
1015
- name: "plugins",
1016
- label: "Plugins",
1017
- component: PluginRoute
1018
- })],
479
+ routes: [pages_default],
1019
480
  icon: PuzzleIcon
1020
481
  });
1021
482
  //#endregion
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ocelescope/plugin",
3
3
  "type": "module",
4
- "version": "0.7.0",
4
+ "version": "0.8.0",
5
5
  "description": "A module for inspections of OCELs",
6
6
  "author": "Görkem-Emre Öztürk <goerkem.oeztuerk@rwth-aachen.de>",
7
7
  "license": "MIT",
@@ -31,30 +31,24 @@
31
31
  "@mantine/core": "^9.5.1",
32
32
  "@mantine/hooks": "^9.5.1",
33
33
  "@marko19907/string-to-color": "^1.0.2",
34
- "@monaco-editor/react": "^4.7.0",
35
- "@rjsf/core": "^6.8.0",
36
- "@rjsf/mantine": "^6.8.0",
37
- "@rjsf/utils": "^6.8.0",
38
- "@rjsf/validator-ajv8": "^6.8.0",
39
- "@tanstack/react-query": "^5.90.21",
34
+ "@ocelescope/api-base": "^0.8.0",
35
+ "@ocelescope/core": "^0.8.0",
36
+ "@ocelescope/plugin-components": "^0.8.0",
37
+ "@ocelescope/resources": "^0.8.0",
40
38
  "embla-carousel": "^8.6.0",
41
39
  "embla-carousel-react": "^8.6.0",
42
40
  "lucide-react": "^1.0.0",
43
- "next": "16.3.0",
44
- "react": "^19.2.8",
45
- "react-dom": "^19.2.8",
46
- "react-hook-form": "^7.71.2",
47
- "@ocelescope/core": "^0.7.0",
48
- "@ocelescope/api-base": "^0.7.0",
49
- "@ocelescope/resources": "^0.7.0"
41
+ "next": "16.3.2",
42
+ "react": "^19.3.0",
43
+ "react-dom": "^19.3.0",
44
+ "react-hook-form": "^7.71.2"
50
45
  },
51
46
  "devDependencies": {
52
- "@rjsf/mantine": "^6.8.0",
53
47
  "@types/file-saver": "^2.0.7",
54
- "@types/node": "^26.2.0",
55
- "@types/react": "^19.2.18",
56
- "@types/react-dom": "^19.2.4",
57
- "tsdown": "^0.22.14",
48
+ "@types/node": "^26.6.1",
49
+ "@types/react": "^19.3.0",
50
+ "@types/react-dom": "^19.3.0",
51
+ "tsdown": "^0.23.0",
58
52
  "typescript": "^6.0.3"
59
53
  },
60
54
  "scripts": {