Skip to content

Commit 62c2818

Browse files
committed
Merge branch 'develop' into wip/akirathan/strip-native-libs
2 parents c9003a0 + 0970a2a commit 62c2818

File tree

259 files changed

+8401
-6511
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

259 files changed

+8401
-6511
lines changed

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,3 +191,9 @@ test-results/
191191
test-traces/
192192
playwright-report/
193193
playwright/.cache/
194+
195+
#########
196+
## Git ##
197+
#########
198+
199+
/.mailmap

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@
1414
[11902]: https://github.com/enso-org/enso/pull/11902
1515
[11908]: https://github.com/enso-org/enso/pull/11908
1616

17+
#### Enso Standard Library
18+
19+
- [Allow using `/` to access files inside a directory reached through a data
20+
link.][11926]
21+
22+
[11926]: https://github.com/enso-org/enso/pull/11926
23+
1724
#### Enso Language & Runtime
1825

1926
- [Promote broken values instead of ignoring them][11777].

app/common/src/backendQuery.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ export type BackendMethods = object.ExtractKeys<Backend, object.MethodOf<Backend
1111

1212
/** For each backend method, an optional function defining how to create a query key from its arguments. */
1313
type BackendQueryNormalizers = {
14-
[Method in BackendMethods]?: (...args: Parameters<Backend[Method]>) => queryCore.QueryKey
14+
[Method in BackendMethods]?: (
15+
...args: Readonly<Parameters<Backend[Method]>>
16+
) => queryCore.QueryKey
1517
}
1618

1719
const NORMALIZE_METHOD_QUERY: BackendQueryNormalizers = {
@@ -22,7 +24,7 @@ const NORMALIZE_METHOD_QUERY: BackendQueryNormalizers = {
2224
/** Creates a partial query key representing the given method and arguments. */
2325
function normalizeMethodQuery<Method extends BackendMethods>(
2426
method: Method,
25-
args: Parameters<Backend[Method]>,
27+
args: Readonly<Parameters<Backend[Method]>>,
2628
) {
2729
return NORMALIZE_METHOD_QUERY[method]?.(...args) ?? args
2830
}
@@ -31,7 +33,7 @@ function normalizeMethodQuery<Method extends BackendMethods>(
3133
export function backendQueryOptions<Method extends BackendMethods>(
3234
backend: Backend | null,
3335
method: Method,
34-
args: Parameters<Backend[Method]>,
36+
args: Readonly<Parameters<Backend[Method]>>,
3537
keyExtra?: queryCore.QueryKey | undefined,
3638
): {
3739
queryKey: queryCore.QueryKey
@@ -47,7 +49,7 @@ export function backendQueryOptions<Method extends BackendMethods>(
4749
export function backendQueryKey<Method extends BackendMethods>(
4850
backend: Backend | null,
4951
method: Method,
50-
args: Parameters<Backend[Method]>,
52+
args: Readonly<Parameters<Backend[Method]>>,
5153
keyExtra?: queryCore.QueryKey | undefined,
5254
): queryCore.QueryKey {
5355
return [backend?.type, method, ...normalizeMethodQuery(method, args), ...(keyExtra ?? [])]

app/common/src/queryClient.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ declare module '@tanstack/query-core' {
3939
* @default false
4040
*/
4141
readonly awaitInvalidates?: queryCore.QueryKey[] | boolean
42+
readonly refetchType?: queryCore.InvalidateQueryFilters['refetchType']
4243
}
4344

4445
readonly queryMeta: {
@@ -98,6 +99,7 @@ export function createQueryClient<TStorageValue = string>(
9899
mutationCache: new queryCore.MutationCache({
99100
onSuccess: (_data, _variables, _context, mutation) => {
100101
const shouldAwaitInvalidates = mutation.meta?.awaitInvalidates ?? false
102+
const refetchType = mutation.meta?.refetchType ?? 'active'
101103
const invalidates = mutation.meta?.invalidates ?? []
102104
const invalidatesToAwait = (() => {
103105
if (Array.isArray(shouldAwaitInvalidates)) {
@@ -113,6 +115,7 @@ export function createQueryClient<TStorageValue = string>(
113115
for (const queryKey of invalidatesToIgnore) {
114116
void queryClient.invalidateQueries({
115117
predicate: query => queryCore.matchQuery({ queryKey }, query),
118+
refetchType,
116119
})
117120
}
118121

@@ -121,6 +124,7 @@ export function createQueryClient<TStorageValue = string>(
121124
invalidatesToAwait.map(queryKey =>
122125
queryClient.invalidateQueries({
123126
predicate: query => queryCore.matchQuery({ queryKey }, query),
127+
refetchType,
124128
}),
125129
),
126130
)

app/common/src/services/Backend.ts

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1070,6 +1070,18 @@ export function assetIsType<Type extends AssetType>(type: Type) {
10701070
return (asset: AnyAsset): asset is Extract<AnyAsset, Asset<Type>> => asset.type === type
10711071
}
10721072

1073+
/** Extract the type of an id and return a discriminated union containing both id and type. */
1074+
export function extractTypeFromId(id: AssetId): AnyAsset extends infer T ?
1075+
T extends T ?
1076+
Pick<T, ('id' | 'type') & keyof T>
1077+
: never
1078+
: never {
1079+
return {
1080+
type: id.match(/^(.+?)-/)?.[1],
1081+
id,
1082+
} as never
1083+
}
1084+
10731085
/** Creates a new placeholder asset id for the given asset type. */
10741086
export function createPlaceholderAssetId<Type extends AssetType>(
10751087
type: Type,
@@ -1674,11 +1686,7 @@ export default abstract class Backend {
16741686
title: string,
16751687
): Promise<CreatedProject>
16761688
/** Return project details. */
1677-
abstract getProjectDetails(
1678-
projectId: ProjectId,
1679-
directoryId: DirectoryId | null,
1680-
getPresignedUrl?: boolean,
1681-
): Promise<Project>
1689+
abstract getProjectDetails(projectId: ProjectId, getPresignedUrl?: boolean): Promise<Project>
16821690
/** Return Language Server logs for a project session. */
16831691
abstract getProjectSessionLogs(
16841692
projectSessionId: ProjectSessionId,
@@ -1767,8 +1775,8 @@ export default abstract class Backend {
17671775
projectId?: string | null,
17681776
metadata?: object | null,
17691777
): Promise<void>
1770-
/** Download from an arbitrary URL that is assumed to originate from this backend. */
1771-
abstract download(url: string, name?: string): Promise<void>
1778+
/** Download an asset. */
1779+
abstract download(assetId: AssetId, title: string): Promise<void>
17721780

17731781
/**
17741782
* Get the URL for the customer portal.

app/common/src/text/english.json

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@
3939
"editDescriptionError": "Could not edit description",
4040
"canOnlyDownloadFilesError": "You currently can only download files.",
4141
"noProjectSelectedError": "First select a project to download.",
42-
"downloadInvalidTypeError": "You can only download files, projects, and Datalinks",
4342
"downloadProjectError": "Could not download project '$0'",
4443
"downloadFileError": "Could not download file '$0'",
4544
"downloadDatalinkError": "Could not download Datalink '$0'",
@@ -64,9 +63,6 @@
6463
"nameShouldNotContainInvalidCharacters": "Name should not contain invalid characters",
6564
"invalidEmailValidationError": "Please enter a valid email address",
6665

67-
"projectHasNoSourceFilesPhrase": "project has no source files",
68-
"fileNotFoundPhrase": "file not found",
69-
7066
"noNewProfilePictureError": "Could not upload a new profile picture because no image was found",
7167

7268
"registrationError": "Something went wrong! Please try again or contact the administrators.",

app/common/src/utilities/data/array.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/** @file Utilities for manipulating arrays. */
22

3-
export const EMPTY_ARRAY: readonly never[] = []
3+
export const EMPTY_ARRAY: readonly [] = []
44

55
// ====================
66
// === shallowEqual ===

app/common/src/utilities/data/object.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,3 +219,12 @@ export function useObjectId() {
219219
* can be used to splice together objects without the risk of collisions.
220220
*/
221221
export type DisjointKeysUnion<A, B> = keyof A & keyof B extends never ? A & B : never
222+
223+
/**
224+
* Merge types of values of an object union. Useful to return an object that UNSAFELY
225+
* (at runtime) conforms to the shape of a discriminated union.
226+
* Especially useful for things like Tanstack Query results.
227+
*/
228+
export type MergeValuesOfObjectUnion<T> = {
229+
[K in `${keyof T & string}`]: T[K & keyof T]
230+
}

app/gui/.storybook/preview.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ const reactPreview: ReactPreview = {
6666

6767
(Story, context) => (
6868
<>
69-
<div className="enso-dashboard">
69+
<div className="enso-app">
7070
<Story {...context} />
7171
</div>
7272
<div id="enso-portal-root" className="enso-portal-root" />

app/gui/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
</head>
5151
<body>
5252
<div id="enso-spotlight" class="enso-spotlight"></div>
53-
<div id="enso-dashboard" class="enso-dashboard"></div>
53+
<div id="enso-app" class="enso-app"></div>
5454
<div id="enso-chat" class="enso-chat"></div>
5555
<div id="enso-portal-root" class="enso-portal-root"></div>
5656
<script type="module" src="/src/entrypoint.ts"></script>

app/gui/integration-test/dashboard/createAsset.spec.ts

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/** @file Test copying, moving, cutting and pasting. */
2-
import { expect, test, type Page } from '@playwright/test'
2+
import { expect, test } from '@playwright/test'
33

44
import { mockAllAndLogin } from './actions'
55

@@ -12,13 +12,6 @@ const SECRET_NAME = 'a secret name'
1212
/** The value of the created secret. */
1313
const SECRET_VALUE = 'a secret value'
1414

15-
/** Find an editor container. */
16-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
17-
function locateEditor(page: Page) {
18-
// Test ID of a placeholder editor component used during testing.
19-
return page.locator('.App')
20-
}
21-
2215
test('create folder', ({ page }) =>
2316
mockAllAndLogin({ page })
2417
.createFolder()

app/gui/integration-test/dashboard/delete.spec.ts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,6 @@ test('delete and restore', ({ page }) =>
2121
.contextMenu.restoreFromTrash()
2222
.driveTable.expectTrashPlaceholderRow()
2323
.goToCategory.cloud()
24-
.expectStartModal()
25-
.withStartModal(async (startModal) => {
26-
await expect(startModal).toBeVisible()
27-
})
28-
.close()
2924
.driveTable.withRows(async (rows) => {
3025
await expect(rows).toHaveCount(1)
3126
}))
@@ -50,8 +45,6 @@ test('delete and restore (keyboard)', ({ page }) =>
5045
.press('Mod+R')
5146
.driveTable.expectTrashPlaceholderRow()
5247
.goToCategory.cloud()
53-
.expectStartModal()
54-
.close()
5548
.driveTable.withRows(async (rows) => {
5649
await expect(rows).toHaveCount(1)
5750
}))

app/gui/integration-test/dashboard/driveView.spec.ts

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,8 @@
11
/** @file Test the drive view. */
2-
import { expect, test, type Locator, type Page } from '@playwright/test'
2+
import { expect, test, type Locator } from '@playwright/test'
33

44
import { TEXT, mockAllAndLogin } from './actions'
55

6-
/** Find an editor container. */
7-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
8-
function locateEditor(page: Page) {
9-
// Test ID of a placeholder editor component used during testing.
10-
return page.locator('.App')
11-
}
12-
136
/** Find a button to close the project. */
147
// eslint-disable-next-line @typescript-eslint/no-unused-vars
158
function locateStopProjectButton(page: Locator) {

app/gui/integration-test/dashboard/labelsPanel.spec.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,12 +84,13 @@ test('labels', ({ page }) =>
8484
// Labels panel with one entry
8585
await locateCreateButton(locateNewLabelModal(page)).click()
8686
await expect(locateLabelsPanel(page)).toBeVisible()
87+
expect(await locateLabelsPanelLabels(page).count()).toBe(1)
8788

8889
// Empty labels panel again, after deleting the only entry
8990
await locateLabelsPanelLabels(page).first().hover()
9091

9192
const labelsPanel = locateLabelsPanel(page)
9293
await labelsPanel.getByRole('button').and(labelsPanel.getByLabel(TEXT.delete)).click()
9394
await page.getByRole('button', { name: TEXT.delete }).getByText(TEXT.delete).click()
94-
expect(await locateLabelsPanelLabels(page).count()).toBeGreaterThanOrEqual(1)
95+
expect(await locateLabelsPanelLabels(page).count()).toBe(0)
9596
}))

app/gui/integration-test/dashboard/pageSwitcher.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { mockAllAndLogin } from './actions'
66
/** Find an editor container. */
77
function locateEditor(page: Page) {
88
// Test ID of a placeholder editor component used during testing.
9-
return page.locator('.App')
9+
return page.locator('.ProjectView')
1010
}
1111

1212
/** Find a drive view. */

app/gui/integration-test/dashboard/startModal.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { mockAllAndLogin } from './actions'
66
/** Find an editor container. */
77
function locateEditor(page: Page) {
88
// Test ID of a placeholder editor component used during testing.
9-
return page.locator('.App')
9+
return page.locator('.ProjectView')
1010
}
1111

1212
/** Find a samples list. */

app/gui/integration-test/project-view/graphNodeVisualization.spec.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import assert from 'assert'
33
import * as actions from './actions'
44
import { computedContent } from './css'
55
import { expect } from './customExpect'
6+
import { CONTROL_KEY } from './keyboard'
67
import * as locate from './locate'
78

89
test('Node can open and load visualization', async ({ page }) => {
@@ -50,10 +51,12 @@ test('Previewing visualization', async ({ page }) => {
5051

5152
test('Warnings visualization', async ({ page }) => {
5253
await actions.goToGraph(page)
53-
54+
// Without centering the graph, menu sometimes goes out of the view.
55+
await page.keyboard.press(`${CONTROL_KEY}+Shift+A`)
5456
// Create a node, attach a warning, open the warnings-visualization.
5557
await locate.addNewNodeButton(page).click()
5658
const input = locate.componentBrowserInput(page).locator('input')
59+
5760
await input.fill('Warning.attach "Uh oh" 42')
5861
await page.keyboard.press('Enter')
5962
await expect(locate.componentBrowser(page)).toBeHidden()

app/gui/src/App.vue

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<script setup lang="ts">
2+
import '@/assets/base.css'
3+
import TooltipDisplayer from '@/components/TooltipDisplayer.vue'
4+
import ProjectView from '@/ProjectView.vue'
5+
import { provideAppClassSet } from '@/providers/appClass'
6+
import { provideGuiConfig } from '@/providers/guiConfig'
7+
import { provideTooltipRegistry } from '@/providers/tooltipRegistry'
8+
import { registerAutoBlurHandler } from '@/util/autoBlur'
9+
import { baseConfig, configValue, mergeConfig, type ApplicationConfigValue } from '@/util/config'
10+
import { urlParams } from '@/util/urlParams'
11+
import { useQueryClient } from '@tanstack/vue-query'
12+
import { applyPureReactInVue } from 'veaury'
13+
import { computed, onMounted } from 'vue'
14+
import { ComponentProps } from 'vue-component-type-helpers'
15+
import ReactRoot from './ReactRoot'
16+
17+
const _props = defineProps<{
18+
// Used in Project View integration tests. Once both test projects will be merged, this should be
19+
// removed
20+
projectViewOnly?: { options: ComponentProps<typeof ProjectView> } | null
21+
onAuthenticated?: (accessToken: string | null) => void
22+
}>()
23+
24+
const classSet = provideAppClassSet()
25+
const appTooltips = provideTooltipRegistry()
26+
27+
const appConfig = computed(() => {
28+
const config = mergeConfig(baseConfig, urlParams(), {
29+
onUnrecognizedOption: (p) => console.warn('Unrecognized option:', p),
30+
})
31+
return config
32+
})
33+
const appConfigValue = computed((): ApplicationConfigValue => configValue(appConfig.value))
34+
35+
const ReactRootWrapper = applyPureReactInVue(ReactRoot)
36+
const queryClient = useQueryClient()
37+
38+
provideGuiConfig(appConfigValue)
39+
40+
registerAutoBlurHandler()
41+
42+
onMounted(() => {
43+
if (appConfigValue.value.window.vibrancy) {
44+
document.body.classList.add('vibrancy')
45+
}
46+
})
47+
</script>
48+
49+
<template>
50+
<div :class="['App', ...classSet.keys()]">
51+
<ProjectView v-if="projectViewOnly" v-bind="projectViewOnly.options" />
52+
<ReactRootWrapper
53+
v-else
54+
:config="appConfigValue"
55+
:queryClient="queryClient"
56+
@authenticated="onAuthenticated ?? (() => {})"
57+
/>
58+
</div>
59+
<TooltipDisplayer :registry="appTooltips" />
60+
</template>
61+
62+
<style>
63+
.App {
64+
width: 100%;
65+
height: 100%;
66+
display: flex;
67+
flex-direction: column;
68+
}
69+
70+
/*
71+
TODO [ao]: Veaury adds a wrapping elements which have `style="all: unset"`, which in turn breaks our layout.
72+
See https://github.com/gloriasoft/veaury/issues/158
73+
*/
74+
[__use_react_component_wrap],
75+
[data-use-vue-component-wrap] {
76+
display: contents !important;
77+
}
78+
</style>

0 commit comments

Comments
 (0)