@finos/legend-application-studio 28.21.11 → 28.21.13
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/lib/components/editor/ActivityBar.d.ts +13 -0
- package/lib/components/editor/ActivityBar.d.ts.map +1 -1
- package/lib/components/editor/ActivityBar.js +59 -4
- package/lib/components/editor/ActivityBar.js.map +1 -1
- package/lib/components/editor/command/project-search.css +1 -1
- package/lib/components/editor/command/project-search.css.map +1 -1
- package/lib/components/editor/editor-group/GrammarTextEditor.d.ts.map +1 -1
- package/lib/components/editor/editor-group/GrammarTextEditor.js +6 -2
- package/lib/components/editor/editor-group/GrammarTextEditor.js.map +1 -1
- package/lib/components/editor/editor-group/dataProduct/DataProductEditor.d.ts +2 -0
- package/lib/components/editor/editor-group/dataProduct/DataProductEditor.d.ts.map +1 -1
- package/lib/components/editor/editor-group/dataProduct/DataProductEditor.js +81 -19
- package/lib/components/editor/editor-group/dataProduct/DataProductEditor.js.map +1 -1
- package/lib/components/editor/editor-group/database-editor/DatabaseDiagramCanvas.d.ts.map +1 -1
- package/lib/components/editor/editor-group/database-editor/DatabaseDiagramCanvas.js +10 -1
- package/lib/components/editor/editor-group/database-editor/DatabaseDiagramCanvas.js.map +1 -1
- package/lib/components/editor/editor-group/database-editor/DatabaseEditor.d.ts.map +1 -1
- package/lib/components/editor/editor-group/database-editor/DatabaseEditor.js +3 -10
- package/lib/components/editor/editor-group/database-editor/DatabaseEditor.js.map +1 -1
- package/lib/components/editor/editor-group/diff-editor/EntityChangeConflictEditor.d.ts.map +1 -1
- package/lib/components/editor/editor-group/diff-editor/EntityChangeConflictEditor.js +8 -3
- package/lib/components/editor/editor-group/diff-editor/EntityChangeConflictEditor.js.map +1 -1
- package/lib/components/editor/editor-group/function-activator/FunctionEditor.js +1 -1
- package/lib/components/editor/editor-group/function-activator/FunctionEditor.js.map +1 -1
- package/lib/components/editor/editor-group/mapping-editor/MappingEditor.js +1 -1
- package/lib/components/editor/editor-group/mapping-editor/MappingEditor.js.map +1 -1
- package/lib/components/editor/editor-group/service-editor/ServiceEditor.d.ts.map +1 -1
- package/lib/components/editor/editor-group/service-editor/ServiceEditor.js +4 -1
- package/lib/components/editor/editor-group/service-editor/ServiceEditor.js.map +1 -1
- package/lib/components/editor/editor-group/uml-editor/StereotypeSelector.d.ts.map +1 -1
- package/lib/components/editor/editor-group/uml-editor/StereotypeSelector.js +1 -3
- package/lib/components/editor/editor-group/uml-editor/StereotypeSelector.js.map +1 -1
- package/lib/components/editor/editor-group/uml-editor/TaggedValueEditor.d.ts.map +1 -1
- package/lib/components/editor/editor-group/uml-editor/TaggedValueEditor.js +1 -3
- package/lib/components/editor/editor-group/uml-editor/TaggedValueEditor.js.map +1 -1
- package/lib/components/editor/ingest-definition-editor.css +1 -1
- package/lib/components/editor/ingest-definition-editor.css.map +1 -1
- package/lib/components/workspace-setup/WorkspaceSetup.d.ts.map +1 -1
- package/lib/components/workspace-setup/WorkspaceSetup.js +20 -3
- package/lib/components/workspace-setup/WorkspaceSetup.js.map +1 -1
- package/lib/index.css +2 -2
- package/lib/index.css.map +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/package.json +1 -1
- package/lib/stores/editor/editor-state/element-editor-state/DatabaseEditorState.d.ts +0 -9
- package/lib/stores/editor/editor-state/element-editor-state/DatabaseEditorState.d.ts.map +1 -1
- package/lib/stores/editor/editor-state/element-editor-state/DatabaseEditorState.js +0 -34
- package/lib/stores/editor/editor-state/element-editor-state/DatabaseEditorState.js.map +1 -1
- package/lib/stores/extensions/DSL_DataProduct_LegendStudioApplicationPlugin_Extension.d.ts +61 -0
- package/lib/stores/extensions/DSL_DataProduct_LegendStudioApplicationPlugin_Extension.d.ts.map +1 -0
- package/lib/stores/extensions/DSL_DataProduct_LegendStudioApplicationPlugin_Extension.js +17 -0
- package/lib/stores/extensions/DSL_DataProduct_LegendStudioApplicationPlugin_Extension.js.map +1 -0
- package/lib/stores/extensions/DSL_Service_LegendStudioApplicationPlugin_Extension.d.ts +2 -2
- package/lib/stores/extensions/DSL_Service_LegendStudioApplicationPlugin_Extension.d.ts.map +1 -1
- package/lib/stores/workspace-setup/WorkspaceSetupStore.d.ts.map +1 -1
- package/lib/stores/workspace-setup/WorkspaceSetupStore.js +6 -2
- package/lib/stores/workspace-setup/WorkspaceSetupStore.js.map +1 -1
- package/package.json +12 -12
- package/src/components/editor/ActivityBar.tsx +88 -0
- package/src/components/editor/editor-group/GrammarTextEditor.tsx +8 -2
- package/src/components/editor/editor-group/dataProduct/DataProductEditor.tsx +207 -29
- package/src/components/editor/editor-group/database-editor/DatabaseDiagramCanvas.tsx +11 -0
- package/src/components/editor/editor-group/database-editor/DatabaseEditor.tsx +1 -28
- package/src/components/editor/editor-group/diff-editor/EntityChangeConflictEditor.tsx +10 -3
- package/src/components/editor/editor-group/function-activator/FunctionEditor.tsx +1 -1
- package/src/components/editor/editor-group/mapping-editor/MappingEditor.tsx +1 -1
- package/src/components/editor/editor-group/service-editor/ServiceEditor.tsx +5 -1
- package/src/components/editor/editor-group/uml-editor/StereotypeSelector.tsx +1 -5
- package/src/components/editor/editor-group/uml-editor/TaggedValueEditor.tsx +1 -5
- package/src/components/workspace-setup/WorkspaceSetup.tsx +27 -2
- package/src/index.ts +1 -0
- package/src/stores/editor/editor-state/element-editor-state/DatabaseEditorState.ts +0 -42
- package/src/stores/extensions/DSL_DataProduct_LegendStudioApplicationPlugin_Extension.ts +77 -0
- package/src/stores/extensions/DSL_Service_LegendStudioApplicationPlugin_Extension.ts +1 -2
- package/src/stores/workspace-setup/WorkspaceSetupStore.ts +5 -0
- package/tsconfig.json +1 -0
|
@@ -80,6 +80,7 @@ import {
|
|
|
80
80
|
PanelLoadingIndicator,
|
|
81
81
|
GearSuggestIcon,
|
|
82
82
|
FlaskIcon,
|
|
83
|
+
SparkleIcon,
|
|
83
84
|
} from '@finos/legend-art';
|
|
84
85
|
import {
|
|
85
86
|
type ChangeEventHandler,
|
|
@@ -106,7 +107,7 @@ import {
|
|
|
106
107
|
QueryBuilderAdvancedWorkflowState,
|
|
107
108
|
type QueryBuilderState,
|
|
108
109
|
} from '@finos/legend-query-builder';
|
|
109
|
-
import { action, autorun, flowResult } from 'mobx';
|
|
110
|
+
import { action, autorun, flowResult, runInAction } from 'mobx';
|
|
110
111
|
import { DataProductTestableEditor } from './testable/DataProductTestableEditor.js';
|
|
111
112
|
import { CODE_EDITOR_LANGUAGE } from '@finos/legend-code-editor';
|
|
112
113
|
import { CodeEditor } from '@finos/legend-lego/code-editor';
|
|
@@ -202,6 +203,11 @@ import {
|
|
|
202
203
|
import type { LegendStudioApplicationStore } from '../../../../stores/LegendStudioBaseStore.js';
|
|
203
204
|
import type { DepotServerClient } from '@finos/legend-server-depot';
|
|
204
205
|
import { RelationElementEditor } from '../data-editor/RelationElementsDataEditor.js';
|
|
206
|
+
import type {
|
|
207
|
+
AccessPointMeta,
|
|
208
|
+
DataProductDocResponse,
|
|
209
|
+
DSL_DataProduct_LegendStudioApplicationPlugin_Extension,
|
|
210
|
+
} from '../../../../stores/extensions/DSL_DataProduct_LegendStudioApplicationPlugin_Extension.js';
|
|
205
211
|
|
|
206
212
|
export enum AP_GROUP_MODAL_ERRORS {
|
|
207
213
|
GROUP_NAME_EMPTY = 'Group Name is empty',
|
|
@@ -262,8 +268,11 @@ const hoverIcon = () => {
|
|
|
262
268
|
};
|
|
263
269
|
|
|
264
270
|
const AccessPointTitle = observer(
|
|
265
|
-
(props: {
|
|
266
|
-
|
|
271
|
+
(props: {
|
|
272
|
+
accessPointState: LakehouseAccessPointState;
|
|
273
|
+
aiSuggestionMeta?: AccessPointMeta | undefined;
|
|
274
|
+
}) => {
|
|
275
|
+
const { accessPointState, aiSuggestionMeta } = props;
|
|
267
276
|
const accessPoint = accessPointState.accessPoint;
|
|
268
277
|
const [editingName, setEditingName] = useState(
|
|
269
278
|
accessPoint.id === newNamePlaceholder,
|
|
@@ -286,29 +295,37 @@ const AccessPointTitle = observer(
|
|
|
286
295
|
}
|
|
287
296
|
});
|
|
288
297
|
|
|
289
|
-
return
|
|
290
|
-
<
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
298
|
+
return (
|
|
299
|
+
<div style={{ flex: '1 1 auto', minWidth: 0 }}>
|
|
300
|
+
{editingName ? (
|
|
301
|
+
<textarea
|
|
302
|
+
className="access-point-editor__name"
|
|
303
|
+
spellCheck={false}
|
|
304
|
+
value={accessPoint.id}
|
|
305
|
+
onChange={updateAccessPointName}
|
|
306
|
+
placeholder={'Access Point Name'}
|
|
307
|
+
onBlur={handleNameBlur}
|
|
308
|
+
style={{
|
|
309
|
+
borderColor:
|
|
310
|
+
accessPoint.id === newNamePlaceholder
|
|
311
|
+
? 'var(--color-red-300)'
|
|
312
|
+
: 'transparent',
|
|
313
|
+
}}
|
|
314
|
+
/>
|
|
315
|
+
) : (
|
|
316
|
+
<div
|
|
317
|
+
className="access-point-editor__name__label"
|
|
318
|
+
onClick={handleNameEdit}
|
|
319
|
+
title="Click to edit access point name"
|
|
320
|
+
>
|
|
321
|
+
{accessPoint.id}
|
|
322
|
+
</div>
|
|
323
|
+
)}
|
|
324
|
+
{aiSuggestionMeta && (
|
|
325
|
+
<div className="data-product-editor__ai-suggestion-inline">
|
|
326
|
+
{aiSuggestionMeta.name}
|
|
327
|
+
</div>
|
|
328
|
+
)}
|
|
312
329
|
</div>
|
|
313
330
|
);
|
|
314
331
|
},
|
|
@@ -697,8 +714,9 @@ export const LakehouseDataProductAccessPointEditor = observer(
|
|
|
697
714
|
(props: {
|
|
698
715
|
accessPointState: LakehouseAccessPointState;
|
|
699
716
|
isReadOnly: boolean;
|
|
717
|
+
aiSuggestionMeta?: AccessPointMeta | undefined;
|
|
700
718
|
}) => {
|
|
701
|
-
const { accessPointState } = props;
|
|
719
|
+
const { accessPointState, aiSuggestionMeta } = props;
|
|
702
720
|
const editorStore = useEditorStore();
|
|
703
721
|
const accessPoint = accessPointState.accessPoint;
|
|
704
722
|
const groupState = accessPointState.state;
|
|
@@ -925,7 +943,10 @@ export const LakehouseDataProductAccessPointEditor = observer(
|
|
|
925
943
|
/>
|
|
926
944
|
<div style={{ flex: 1 }}>
|
|
927
945
|
<div className="access-point-editor__metadata">
|
|
928
|
-
<AccessPointTitle
|
|
946
|
+
<AccessPointTitle
|
|
947
|
+
accessPointState={accessPointState}
|
|
948
|
+
aiSuggestionMeta={aiSuggestionMeta}
|
|
949
|
+
/>
|
|
929
950
|
<div className="access-point-editor__info">
|
|
930
951
|
<div className="access-point-editor__reproducible">
|
|
931
952
|
<Checkbox
|
|
@@ -1166,6 +1187,11 @@ export const LakehouseDataProductAccessPointEditor = observer(
|
|
|
1166
1187
|
{isHoveringTitle && hoverIcon()}
|
|
1167
1188
|
</div>
|
|
1168
1189
|
)}
|
|
1190
|
+
{aiSuggestionMeta && (
|
|
1191
|
+
<div className="data-product-editor__ai-suggestion-inline">
|
|
1192
|
+
{aiSuggestionMeta.title}
|
|
1193
|
+
</div>
|
|
1194
|
+
)}
|
|
1169
1195
|
{editingDescription ? (
|
|
1170
1196
|
<textarea
|
|
1171
1197
|
className="panel__content__form__section__input"
|
|
@@ -1209,6 +1235,11 @@ export const LakehouseDataProductAccessPointEditor = observer(
|
|
|
1209
1235
|
{isHoveringDesc && hoverIcon()}
|
|
1210
1236
|
</div>
|
|
1211
1237
|
)}
|
|
1238
|
+
{aiSuggestionMeta && (
|
|
1239
|
+
<div className="data-product-editor__ai-suggestion-inline">
|
|
1240
|
+
{aiSuggestionMeta.description}
|
|
1241
|
+
</div>
|
|
1242
|
+
)}
|
|
1212
1243
|
<div className="access-point-editor__content">
|
|
1213
1244
|
<div className="access-point-editor__generic-entry">
|
|
1214
1245
|
<div className="access-point-editor__entry__container">
|
|
@@ -1712,6 +1743,10 @@ const AccessPointGroupEditor = observer(
|
|
|
1712
1743
|
const [isHoveringName, setIsHoveringName] = useState(false);
|
|
1713
1744
|
const [editingTitle, setEditingTitle] = useState(false);
|
|
1714
1745
|
const [isHoveringTitle, setIsHoveringTitle] = useState(false);
|
|
1746
|
+
const [isSuggestingWithAI, setIsSuggestingWithAI] = useState(false);
|
|
1747
|
+
const [aiSuggestion, setAISuggestion] = useState<
|
|
1748
|
+
DataProductDocResponse | undefined
|
|
1749
|
+
>(undefined);
|
|
1715
1750
|
const handleDescriptionEdit = () => setEditingDescription(true);
|
|
1716
1751
|
const handleDescriptionBlur = () => {
|
|
1717
1752
|
setEditingDescription(false);
|
|
@@ -1767,6 +1802,96 @@ const AccessPointGroupEditor = observer(
|
|
|
1767
1802
|
accessPointGroup_setTitle(groupState.value, val);
|
|
1768
1803
|
};
|
|
1769
1804
|
|
|
1805
|
+
// AI suggestion for APG
|
|
1806
|
+
const legendAIUrl = editorStore.applicationStore.config.legendAIUrl;
|
|
1807
|
+
const aiDocSuggester = legendAIUrl
|
|
1808
|
+
? editorStore.pluginManager
|
|
1809
|
+
.getApplicationPlugins()
|
|
1810
|
+
.map((p) =>
|
|
1811
|
+
(
|
|
1812
|
+
p as DSL_DataProduct_LegendStudioApplicationPlugin_Extension
|
|
1813
|
+
).getExtraDataProductDocumentationAISuggester?.bind(p),
|
|
1814
|
+
)
|
|
1815
|
+
.find(Boolean)
|
|
1816
|
+
: undefined;
|
|
1817
|
+
const suggestWithAI = async (): Promise<void> => {
|
|
1818
|
+
if (!aiDocSuggester || !legendAIUrl) {
|
|
1819
|
+
return;
|
|
1820
|
+
}
|
|
1821
|
+
setIsSuggestingWithAI(true);
|
|
1822
|
+
setAISuggestion(undefined);
|
|
1823
|
+
try {
|
|
1824
|
+
const definitions =
|
|
1825
|
+
await editorStore.graphManagerState.graphManager.graphToPureCode(
|
|
1826
|
+
editorStore.graphManagerState.graph,
|
|
1827
|
+
);
|
|
1828
|
+
const product = productEditorState.product;
|
|
1829
|
+
const suggestion = await aiDocSuggester(
|
|
1830
|
+
{ definitions, data_product_name: product.path },
|
|
1831
|
+
legendAIUrl,
|
|
1832
|
+
);
|
|
1833
|
+
runInAction(() => {
|
|
1834
|
+
setAISuggestion(suggestion);
|
|
1835
|
+
});
|
|
1836
|
+
} finally {
|
|
1837
|
+
runInAction(() => {
|
|
1838
|
+
setIsSuggestingWithAI(false);
|
|
1839
|
+
});
|
|
1840
|
+
}
|
|
1841
|
+
};
|
|
1842
|
+
const applyAISuggestion = (): void => {
|
|
1843
|
+
if (!aiSuggestion) {
|
|
1844
|
+
return;
|
|
1845
|
+
}
|
|
1846
|
+
// Find matching group in the response
|
|
1847
|
+
const gIdx =
|
|
1848
|
+
productEditorState.accessPointGroupStates.indexOf(groupState);
|
|
1849
|
+
const groupMeta =
|
|
1850
|
+
aiSuggestion.access_point_groups.find(
|
|
1851
|
+
(g) => g.name === groupState.value.id,
|
|
1852
|
+
) ?? aiSuggestion.access_point_groups[gIdx];
|
|
1853
|
+
if (groupMeta) {
|
|
1854
|
+
accessPointGroup_setTitle(groupState.value, groupMeta.title);
|
|
1855
|
+
accessPointGroup_setDescription(
|
|
1856
|
+
groupState.value,
|
|
1857
|
+
groupMeta.description,
|
|
1858
|
+
);
|
|
1859
|
+
}
|
|
1860
|
+
// Apply access point metadata for this group
|
|
1861
|
+
const apMetas = aiSuggestion.access_points.filter(
|
|
1862
|
+
(ap) => ap.group === (groupMeta?.name ?? groupState.value.id),
|
|
1863
|
+
);
|
|
1864
|
+
for (
|
|
1865
|
+
let j = 0;
|
|
1866
|
+
j < apMetas.length && j < groupState.value.accessPoints.length;
|
|
1867
|
+
j++
|
|
1868
|
+
) {
|
|
1869
|
+
const apMeta = apMetas[j] as (typeof apMetas)[number];
|
|
1870
|
+
const ap = groupState.value.accessPoints[
|
|
1871
|
+
j
|
|
1872
|
+
] as (typeof groupState.value.accessPoints)[number];
|
|
1873
|
+
// Convert suggested name to valid identifier (alphanumeric + underscore)
|
|
1874
|
+
const sanitizedName = apMeta.name.replace(/[^0-9a-zA-Z_]/g, '_');
|
|
1875
|
+
if (sanitizedName) {
|
|
1876
|
+
ap.id = sanitizedName;
|
|
1877
|
+
}
|
|
1878
|
+
accessPoint_setTitle(ap, apMeta.title);
|
|
1879
|
+
accessPoint_setDescription(ap, apMeta.description);
|
|
1880
|
+
}
|
|
1881
|
+
setAISuggestion(undefined);
|
|
1882
|
+
};
|
|
1883
|
+
|
|
1884
|
+
// Computed AI suggestion for current group
|
|
1885
|
+
const groupIndex =
|
|
1886
|
+
productEditorState.accessPointGroupStates.indexOf(groupState);
|
|
1887
|
+
const aiGroupMeta =
|
|
1888
|
+
aiSuggestion?.access_point_groups.find(
|
|
1889
|
+
(g) => g.name === groupState.value.id,
|
|
1890
|
+
) ?? aiSuggestion?.access_point_groups[groupIndex];
|
|
1891
|
+
const aiAccessPointMetas = aiSuggestion?.access_points.filter(
|
|
1892
|
+
(ap) => ap.group === (aiGroupMeta?.name ?? groupState.value.id),
|
|
1893
|
+
);
|
|
1894
|
+
|
|
1770
1895
|
const handleRemoveAccessPointGroup = (): void => {
|
|
1771
1896
|
editorStore.applicationStore.alertService.setActionAlertInfo({
|
|
1772
1897
|
message:
|
|
@@ -1852,6 +1977,48 @@ const AccessPointGroupEditor = observer(
|
|
|
1852
1977
|
<TimesIcon />
|
|
1853
1978
|
</button>
|
|
1854
1979
|
</div>
|
|
1980
|
+
{aiDocSuggester && (
|
|
1981
|
+
<div style={{ padding: '0.25rem 0.5rem' }}>
|
|
1982
|
+
<PanelLoadingIndicator isLoading={isSuggestingWithAI} />
|
|
1983
|
+
{aiSuggestion ? (
|
|
1984
|
+
<div className="data-product-editor__ai-suggestion__actions">
|
|
1985
|
+
<button
|
|
1986
|
+
className="btn btn--dark data-product-editor__ai-suggestion__apply-btn"
|
|
1987
|
+
onClick={applyAISuggestion}
|
|
1988
|
+
title="Apply AI-generated title, description, and access point metadata for this group"
|
|
1989
|
+
>
|
|
1990
|
+
Apply
|
|
1991
|
+
</button>
|
|
1992
|
+
<button
|
|
1993
|
+
className="btn data-product-editor__ai-suggestion__dismiss-btn"
|
|
1994
|
+
onClick={(): void => setAISuggestion(undefined)}
|
|
1995
|
+
>
|
|
1996
|
+
Dismiss
|
|
1997
|
+
</button>
|
|
1998
|
+
<span className="data-product-editor__ai-suggestion-badge">
|
|
1999
|
+
<SparkleIcon />
|
|
2000
|
+
AI Suggestion
|
|
2001
|
+
</span>
|
|
2002
|
+
</div>
|
|
2003
|
+
) : (
|
|
2004
|
+
<button
|
|
2005
|
+
className="data-product-editor__ai-suggest-btn"
|
|
2006
|
+
onClick={(): void => {
|
|
2007
|
+
suggestWithAI().catch(
|
|
2008
|
+
editorStore.applicationStore.alertUnhandledError,
|
|
2009
|
+
);
|
|
2010
|
+
}}
|
|
2011
|
+
disabled={isSuggestingWithAI || isReadOnly}
|
|
2012
|
+
title="Use AI to suggest title and descriptions for this access point group"
|
|
2013
|
+
>
|
|
2014
|
+
<SparkleIcon />
|
|
2015
|
+
<span>
|
|
2016
|
+
{isSuggestingWithAI ? 'Suggesting...' : 'Suggest with AI'}
|
|
2017
|
+
</span>
|
|
2018
|
+
</button>
|
|
2019
|
+
)}
|
|
2020
|
+
</div>
|
|
2021
|
+
)}
|
|
1855
2022
|
<div className="access-point-editor__group-container__name-editor">
|
|
1856
2023
|
{editingTitle ? (
|
|
1857
2024
|
<textarea
|
|
@@ -1896,6 +2063,11 @@ const AccessPointGroupEditor = observer(
|
|
|
1896
2063
|
</div>
|
|
1897
2064
|
)}
|
|
1898
2065
|
</div>
|
|
2066
|
+
{aiGroupMeta && (
|
|
2067
|
+
<div className="data-product-editor__ai-suggestion-inline">
|
|
2068
|
+
{aiGroupMeta.title}
|
|
2069
|
+
</div>
|
|
2070
|
+
)}
|
|
1899
2071
|
<div className="access-point-editor__group-container__description-editor">
|
|
1900
2072
|
{editingDescription ? (
|
|
1901
2073
|
<textarea
|
|
@@ -1938,6 +2110,11 @@ const AccessPointGroupEditor = observer(
|
|
|
1938
2110
|
{isHoveringDescription && hoverIcon()}
|
|
1939
2111
|
</div>
|
|
1940
2112
|
)}
|
|
2113
|
+
{aiGroupMeta && (
|
|
2114
|
+
<div className="data-product-editor__ai-suggestion-inline">
|
|
2115
|
+
{aiGroupMeta.description}
|
|
2116
|
+
</div>
|
|
2117
|
+
)}
|
|
1941
2118
|
</div>
|
|
1942
2119
|
{editorStore.applicationStore.config.options.dataProductConfig && (
|
|
1943
2120
|
<AccessPointGroupPublicToggle groupState={groupState} />
|
|
@@ -1975,11 +2152,12 @@ const AccessPointGroupEditor = observer(
|
|
|
1975
2152
|
>
|
|
1976
2153
|
{groupState.accessPointStates
|
|
1977
2154
|
.filter(filterByType(LakehouseAccessPointState))
|
|
1978
|
-
.map((apState) => (
|
|
2155
|
+
.map((apState, idx) => (
|
|
1979
2156
|
<LakehouseDataProductAccessPointEditor
|
|
1980
2157
|
key={apState.uuid}
|
|
1981
2158
|
isReadOnly={isReadOnly}
|
|
1982
2159
|
accessPointState={apState}
|
|
2160
|
+
aiSuggestionMeta={aiAccessPointMetas?.[idx]}
|
|
1983
2161
|
/>
|
|
1984
2162
|
))}
|
|
1985
2163
|
</div>
|
|
@@ -58,6 +58,7 @@ import {
|
|
|
58
58
|
resolveJoinFormula,
|
|
59
59
|
} from './DatabaseDiagramHelper.js';
|
|
60
60
|
import type { DatabaseEditorState } from '../../../../stores/editor/editor-state/element-editor-state/DatabaseEditorState.js';
|
|
61
|
+
import { useApplicationStore } from '@finos/legend-application';
|
|
61
62
|
|
|
62
63
|
const NODE_TYPES = {
|
|
63
64
|
table: DatabaseTableNode,
|
|
@@ -107,6 +108,7 @@ const findRelationById = (
|
|
|
107
108
|
const DatabaseDiagramCanvasInner = observer(
|
|
108
109
|
(props: { editorState: DatabaseEditorState }) => {
|
|
109
110
|
const { editorState } = props;
|
|
111
|
+
const applicationStore = useApplicationStore();
|
|
110
112
|
const {
|
|
111
113
|
database,
|
|
112
114
|
selectedRelation,
|
|
@@ -521,6 +523,15 @@ const DatabaseDiagramCanvasInner = observer(
|
|
|
521
523
|
<div className="database-diagram__canvas-shell">
|
|
522
524
|
<ReactFlow
|
|
523
525
|
className="database-diagram__canvas"
|
|
526
|
+
// Drive ReactFlow's built-in light/dark variant from the active app
|
|
527
|
+
// theme. The chrome (canvas bg, controls, minimap, edges, dot pattern)
|
|
528
|
+
// then tracks studio's theme without us having to override individual
|
|
529
|
+
// `--xy-*` variables.
|
|
530
|
+
colorMode={
|
|
531
|
+
applicationStore.layoutService.TEMPORARY__isLightColorThemeEnabled
|
|
532
|
+
? 'light'
|
|
533
|
+
: 'dark'
|
|
534
|
+
}
|
|
524
535
|
nodes={selectionAwareNodes}
|
|
525
536
|
edges={selectionAwareEdges}
|
|
526
537
|
nodeTypes={NODE_TYPES}
|
|
@@ -20,12 +20,10 @@ import {
|
|
|
20
20
|
ChevronRightIcon,
|
|
21
21
|
InfoCircleIcon,
|
|
22
22
|
LockIcon,
|
|
23
|
-
MoonIcon,
|
|
24
23
|
ResizablePanel,
|
|
25
24
|
ResizablePanelGroup,
|
|
26
25
|
ResizablePanelSplitter,
|
|
27
26
|
ResizablePanelSplitterLine,
|
|
28
|
-
SunIcon,
|
|
29
27
|
clsx,
|
|
30
28
|
getCollapsiblePanelGroupProps,
|
|
31
29
|
} from '@finos/legend-art';
|
|
@@ -90,14 +88,7 @@ export const DatabaseEditor = observer(() => {
|
|
|
90
88
|
);
|
|
91
89
|
|
|
92
90
|
return (
|
|
93
|
-
<div
|
|
94
|
-
className={clsx('database-editor', {
|
|
95
|
-
// Local light-theme opt-in. Scoped to this editor only — the rest
|
|
96
|
-
// of Studio remains in its configured theme. SCSS overrides hang
|
|
97
|
-
// off this modifier class.
|
|
98
|
-
'database-editor--light': editorState.theme === 'light',
|
|
99
|
-
})}
|
|
100
|
-
>
|
|
91
|
+
<div className="database-editor">
|
|
101
92
|
<div className="database-editor__tabs__header">
|
|
102
93
|
<div className="database-editor__tabs">
|
|
103
94
|
{TABS.map((tab) => (
|
|
@@ -128,24 +119,6 @@ export const DatabaseEditor = observer(() => {
|
|
|
128
119
|
READ ONLY
|
|
129
120
|
</span>
|
|
130
121
|
</div>
|
|
131
|
-
{/*
|
|
132
|
-
* Light/dark theme toggle. Scoped to this editor only — see
|
|
133
|
-
* `DatabaseEditorState.toggleTheme`. Icon switches to surface the
|
|
134
|
-
* mode the user would jump TO if they clicked (current=dark shows
|
|
135
|
-
* a sun, current=light shows a moon).
|
|
136
|
-
*/}
|
|
137
|
-
<button
|
|
138
|
-
type="button"
|
|
139
|
-
className="database-editor__theme-toggle"
|
|
140
|
-
onClick={() => editorState.toggleTheme()}
|
|
141
|
-
title={
|
|
142
|
-
editorState.theme === 'dark'
|
|
143
|
-
? 'Switch to light theme (this editor only)'
|
|
144
|
-
: 'Switch to dark theme'
|
|
145
|
-
}
|
|
146
|
-
>
|
|
147
|
-
{editorState.theme === 'dark' ? <SunIcon /> : <MoonIcon />}
|
|
148
|
-
</button>
|
|
149
122
|
</div>
|
|
150
123
|
<div className="database-editor__content">
|
|
151
124
|
{database instanceof INTERNAL__LakehouseGeneratedDatabase && (
|
|
@@ -54,7 +54,7 @@ import {
|
|
|
54
54
|
getCodeEditorValue,
|
|
55
55
|
normalizeLineEnding,
|
|
56
56
|
clearMarkers,
|
|
57
|
-
|
|
57
|
+
getCodeEditorThemeForAppTheme,
|
|
58
58
|
CODE_EDITOR_LANGUAGE,
|
|
59
59
|
} from '@finos/legend-code-editor';
|
|
60
60
|
import {
|
|
@@ -154,6 +154,7 @@ export const EntityChangeConflictSideBarItem = observer(
|
|
|
154
154
|
const MergeConflictEditor = observer(
|
|
155
155
|
(props: { conflictEditorState: EntityChangeConflictEditorState }) => {
|
|
156
156
|
const { conflictEditorState } = props;
|
|
157
|
+
const applicationStore = useApplicationStore();
|
|
157
158
|
const isReadOnly = conflictEditorState.isReadOnly;
|
|
158
159
|
const [editor, setEditor] = useState<
|
|
159
160
|
monacoEditorAPI.IStandaloneCodeEditor | undefined
|
|
@@ -189,7 +190,10 @@ const MergeConflictEditor = observer(
|
|
|
189
190
|
const element = textInputRef.current;
|
|
190
191
|
const _editor = monacoEditorAPI.create(element, {
|
|
191
192
|
...getBaseCodeEditorOptions(),
|
|
192
|
-
theme
|
|
193
|
+
// Match the active app theme — see GrammarTextEditor for the rationale.
|
|
194
|
+
theme: getCodeEditorThemeForAppTheme(
|
|
195
|
+
applicationStore.layoutService.TEMPORARY__isLightColorThemeEnabled,
|
|
196
|
+
),
|
|
193
197
|
language: CODE_EDITOR_LANGUAGE.PURE,
|
|
194
198
|
minimap: { enabled: false },
|
|
195
199
|
formatOnType: true,
|
|
@@ -198,7 +202,10 @@ const MergeConflictEditor = observer(
|
|
|
198
202
|
_editor.focus(); // focus on the editor initially so we can correctly compute next/prev conflict chunks
|
|
199
203
|
setEditor(_editor);
|
|
200
204
|
}
|
|
201
|
-
}, [
|
|
205
|
+
}, [
|
|
206
|
+
editor,
|
|
207
|
+
applicationStore.layoutService.TEMPORARY__isLightColorThemeEnabled,
|
|
208
|
+
]);
|
|
202
209
|
|
|
203
210
|
if (editor) {
|
|
204
211
|
// dispose the old editor content setter in case the `updateInput` handler changes
|
|
@@ -1521,7 +1521,7 @@ export const FunctionEditor = observer(() => {
|
|
|
1521
1521
|
return (
|
|
1522
1522
|
<div
|
|
1523
1523
|
data-testid={LEGEND_STUDIO_TEST_ID.FUNCTION_EDITOR}
|
|
1524
|
-
className="function-editor uml-editor
|
|
1524
|
+
className="function-editor uml-editor"
|
|
1525
1525
|
>
|
|
1526
1526
|
<Panel>
|
|
1527
1527
|
<div className="panel__header">
|
|
@@ -220,7 +220,7 @@ const MappingClassMappingEditor = observer(() => {
|
|
|
220
220
|
);
|
|
221
221
|
|
|
222
222
|
return (
|
|
223
|
-
<div className=
|
|
223
|
+
<div className="mapping-editor">
|
|
224
224
|
<ResizablePanelGroup orientation="vertical">
|
|
225
225
|
<ResizablePanel size={300} minSize={300}>
|
|
226
226
|
<div className="mapping-editor__side-bar">
|
|
@@ -267,7 +267,11 @@ const ServiceGeneralEditor = observer(() => {
|
|
|
267
267
|
setIsSuggestingWithAI(true);
|
|
268
268
|
setAIDocSuggestion(undefined);
|
|
269
269
|
try {
|
|
270
|
-
const
|
|
270
|
+
const serviceGrammar =
|
|
271
|
+
await editorStore.graphManagerState.graphManager.elementsToPureCode([
|
|
272
|
+
service,
|
|
273
|
+
]);
|
|
274
|
+
const suggestion = await aiDocSuggester(serviceGrammar, legendAIUrl);
|
|
271
275
|
setAIDocSuggestion(suggestion);
|
|
272
276
|
} finally {
|
|
273
277
|
setIsSuggestingWithAI(false);
|
|
@@ -185,11 +185,7 @@ export const StereotypeSelector = observer(
|
|
|
185
185
|
dragSourceConnector={handleRef}
|
|
186
186
|
isDragging={isBeingDragged}
|
|
187
187
|
/>
|
|
188
|
-
<div
|
|
189
|
-
className={clsx('stereotype-selector', {
|
|
190
|
-
'stereotype-selector--dark': darkTheme,
|
|
191
|
-
})}
|
|
192
|
-
>
|
|
188
|
+
<div className="stereotype-selector">
|
|
193
189
|
<div className="stereotype-selector__profile">
|
|
194
190
|
<CustomSelectorInput
|
|
195
191
|
className="stereotype-selector__profile__selector"
|
|
@@ -194,11 +194,7 @@ export const TaggedValueEditor = observer(
|
|
|
194
194
|
dragSourceConnector={handleRef}
|
|
195
195
|
isDragging={isBeingDragged}
|
|
196
196
|
/>
|
|
197
|
-
<div
|
|
198
|
-
className={clsx('tagged-value-editor', {
|
|
199
|
-
'tagged-value-editor--dark': darkTheme,
|
|
200
|
-
})}
|
|
201
|
-
>
|
|
197
|
+
<div className="tagged-value-editor">
|
|
202
198
|
<div className="tagged-value-editor__profile">
|
|
203
199
|
<CustomSelectorInput
|
|
204
200
|
className="tagged-value-editor__profile__selector"
|
|
@@ -49,7 +49,7 @@ import { useApplicationNavigationContext } from '@finos/legend-application';
|
|
|
49
49
|
import { useParams } from '@finos/legend-application/browser';
|
|
50
50
|
import { LEGEND_STUDIO_DOCUMENTATION_KEY } from '../../__lib__/LegendStudioDocumentation.js';
|
|
51
51
|
import { CreateProjectModal } from './CreateProjectModal.js';
|
|
52
|
-
import { ActivityBarMenu } from '../editor/ActivityBar.js';
|
|
52
|
+
import { ActivityBarMenu, ColorThemeToggle } from '../editor/ActivityBar.js';
|
|
53
53
|
import { LEGEND_STUDIO_APPLICATION_NAVIGATION_CONTEXT_KEY } from '../../__lib__/LegendStudioApplicationNavigationContext.js';
|
|
54
54
|
import { CreateWorkspaceModal } from './CreateWorkspaceModal.js';
|
|
55
55
|
import { RecentWorkspacesPanel } from './RecentWorkspacesPanel.js';
|
|
@@ -369,14 +369,24 @@ export const WorkspaceSetup = withWorkspaceSetupStore(
|
|
|
369
369
|
// Build a unified option list: recent projects (that aren't already in the
|
|
370
370
|
// loaded set) are prepended so users can instantly re-open common work
|
|
371
371
|
// without waiting for the SDLC search to round-trip.
|
|
372
|
+
const sandboxProject =
|
|
373
|
+
setupStore.sandboxProject instanceof Project
|
|
374
|
+
? setupStore.sandboxProject
|
|
375
|
+
: undefined;
|
|
376
|
+
const sandboxProjectId = sandboxProject?.projectId;
|
|
372
377
|
const loadedProjectOptions = setupStore.projects
|
|
378
|
+
.filter((p) => p.projectId !== sandboxProjectId)
|
|
373
379
|
.map(buildProjectOption)
|
|
374
380
|
.sort(compareLabelFn);
|
|
375
381
|
const loadedProjectIds = new Set(
|
|
376
382
|
setupStore.projects.map((p) => p.projectId),
|
|
377
383
|
);
|
|
378
384
|
const recentProjectOptions: ProjectOption[] = setupStore.recentProjects
|
|
379
|
-
.filter(
|
|
385
|
+
.filter(
|
|
386
|
+
(r) =>
|
|
387
|
+
!loadedProjectIds.has(r.projectId) &&
|
|
388
|
+
r.projectId !== sandboxProjectId,
|
|
389
|
+
)
|
|
380
390
|
.map((r) => {
|
|
381
391
|
// Rebuild a real Project from the cached metadata; no synthetic
|
|
382
392
|
// fields needed since we persist everything the schema requires.
|
|
@@ -390,6 +400,17 @@ export const WorkspaceSetup = withWorkspaceSetupStore(
|
|
|
390
400
|
return { label: stub.name, value: stub };
|
|
391
401
|
});
|
|
392
402
|
const projectOptions: ProjectOption[] = [
|
|
403
|
+
// The user's own sandbox project is fetched via a dedicated call
|
|
404
|
+
// (`loadSandboxProject`) and excluded from the main search results, so
|
|
405
|
+
// we surface it here as a labeled option pinned to the top of the list.
|
|
406
|
+
...(sandboxProject
|
|
407
|
+
? [
|
|
408
|
+
{
|
|
409
|
+
label: `${sandboxProject.name} (sandbox)`,
|
|
410
|
+
value: sandboxProject,
|
|
411
|
+
},
|
|
412
|
+
]
|
|
413
|
+
: []),
|
|
393
414
|
...recentProjectOptions,
|
|
394
415
|
...loadedProjectOptions,
|
|
395
416
|
];
|
|
@@ -546,6 +567,10 @@ export const WorkspaceSetup = withWorkspaceSetupStore(
|
|
|
546
567
|
<div className="workspace-setup__body">
|
|
547
568
|
<div className="activity-bar">
|
|
548
569
|
<ActivityBarMenu />
|
|
570
|
+
{/* Spacer so the theme toggle sits at the bottom of the strip,
|
|
571
|
+
mirroring how the editor's activity bar is laid out. */}
|
|
572
|
+
<div className="activity-bar__items" />
|
|
573
|
+
<ColorThemeToggle />
|
|
549
574
|
</div>
|
|
550
575
|
<div
|
|
551
576
|
className="workspace-setup__content"
|
package/src/index.ts
CHANGED
|
@@ -64,6 +64,7 @@ export * from './stores/graph-modifier/DSL_Service_GraphModifierHelper.js';
|
|
|
64
64
|
export * from './stores/graph-modifier/RawValueSpecificationGraphModifierHelper.js';
|
|
65
65
|
export * from './stores/extensions/DSL_Mapping_LegendStudioApplicationPlugin_Extension.js';
|
|
66
66
|
export * from './stores/extensions/DSL_Service_LegendStudioApplicationPlugin_Extension.js';
|
|
67
|
+
export * from './stores/extensions/DSL_DataProduct_LegendStudioApplicationPlugin_Extension.js';
|
|
67
68
|
export * from './stores/extensions/DSL_Data_LegendStudioApplicationPlugin_Extension.js';
|
|
68
69
|
export { DataProductEditorState } from './stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.js';
|
|
69
70
|
|