Local Dashboard fixes (#10958)

- Fix most of https://github.com/enso-org/cloud-v2/issues/1459
- Prevent click + click from triggering rename on Windows and Linux. Behavior is preserved on macOS.
- Fix text in Drive when root folder is empty
- Properly remove the "Drop here to upload box" after a file is dropped
- "Copy as path" now unconditionally uses `/` for path delimiters, even on Windows
- Duplicating a project in the root folder on Windows no longer errors
- Extra folders in the sidebar now (correctly) show folder name, rather than path, on Windows
- Mouse pointer when dragging to a folder is now move, not copy

Not addressed:
- [no-repro] Tooltips should have some latency before showing up
- This should already be the case, although it may work weirdly (once the tooltip opens, there is no delay on subsequent tooltips opening until the last tooltip closes.)
- [no-repro] Ctrl-click should add to selection
- Column width should be resizable
- This requires a refactor and therefore is considered out of scope for this PR
- [no-repro] Choosing root folder needs a file browser
- [no-repro] Choosing root folder doesn't do anything get the same list as we had before
- [no-repro] Open in explorer didn't work in a file but did on project - possibly fixed by path changes.
- [no-repro] Opening an enso-project by double clicking resulted on it being renamed with a (2)

Related changes:
- Make "root directory" picker's file browser default to the current root directory

# Important Notes
None
This commit is contained in:
somebody1234 2024-09-08 16:54:41 +10:00 committed by GitHub
parent 6f97e8041b
commit 9ec60299e4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
33 changed files with 338 additions and 318 deletions

View File

@ -36,8 +36,8 @@
"@sentry/react": "^7.74.0", "@sentry/react": "^7.74.0",
"@stripe/react-stripe-js": "^2.7.1", "@stripe/react-stripe-js": "^2.7.1",
"@stripe/stripe-js": "^3.5.0", "@stripe/stripe-js": "^3.5.0",
"@tanstack/react-query": "5.45.1", "@tanstack/react-query": "5.55.0",
"@tanstack/vue-query": ">= 5.45.0 < 5.46.0", "@tanstack/vue-query": ">= 5.54.0 < 5.56.0",
"ajv": "^8.12.0", "ajv": "^8.12.0",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"enso-common": "workspace:*", "enso-common": "workspace:*",

View File

@ -86,15 +86,16 @@ import LocalBackend from '#/services/LocalBackend'
import ProjectManager, * as projectManager from '#/services/ProjectManager' import ProjectManager, * as projectManager from '#/services/ProjectManager'
import RemoteBackend from '#/services/RemoteBackend' import RemoteBackend from '#/services/RemoteBackend'
import { FeatureFlagsProvider } from '#/providers/FeatureFlagsProvider'
import * as appBaseUrl from '#/utilities/appBaseUrl' import * as appBaseUrl from '#/utilities/appBaseUrl'
import * as eventModule from '#/utilities/event' import * as eventModule from '#/utilities/event'
import LocalStorage from '#/utilities/LocalStorage' import LocalStorage from '#/utilities/LocalStorage'
import * as object from '#/utilities/object' import * as object from '#/utilities/object'
import { Path } from '#/utilities/path' import { Path } from '#/utilities/path'
import { STATIC_QUERY_OPTIONS } from '#/utilities/reactQuery'
import { useInitAuthService } from '#/authentication/service' import { useInitAuthService } from '#/authentication/service'
import { InvitedToOrganizationModal } from '#/modals/InvitedToOrganizationModal' import { InvitedToOrganizationModal } from '#/modals/InvitedToOrganizationModal'
import { FeatureFlagsProvider } from '#/providers/FeatureFlagsProvider'
// ============================ // ============================
// === Global configuration === // === Global configuration ===
@ -178,14 +179,8 @@ export default function App(props: AppProps) {
supportsLocalBackend: props.supportsLocalBackend, supportsLocalBackend: props.supportsLocalBackend,
}, },
] as const, ] as const,
meta: { persist: false },
networkMode: 'always', networkMode: 'always',
staleTime: Infinity, ...STATIC_QUERY_OPTIONS,
gcTime: Infinity,
refetchOnMount: false,
refetchInterval: false,
refetchOnReconnect: false,
refetchIntervalInBackground: false,
behavior: { behavior: {
onFetch: ({ state }) => { onFetch: ({ state }) => {
const instance = state.data?.projectManagerInstance ?? null const instance = state.data?.projectManagerInstance ?? null

View File

@ -252,23 +252,25 @@ export function Dialog(props: DialogProps) {
{(opts) => { {(opts) => {
return ( return (
<dialogProvider.DialogProvider value={{ close: opts.close, dialogId }}> <dialogProvider.DialogProvider value={{ close: opts.close, dialogId }}>
<aria.Header className={styles.header({ scrolledToTop: isScrolledToTop })}> {((!hideCloseButton && closeButton !== 'floating') || title != null) && (
<ariaComponents.CloseButton <aria.Header className={styles.header({ scrolledToTop: isScrolledToTop })}>
className={styles.closeButton()} <ariaComponents.CloseButton
onPress={opts.close} className={styles.closeButton()}
/> onPress={opts.close}
/>
{title != null && ( {title != null && (
<ariaComponents.Text.Heading <ariaComponents.Text.Heading
id={titleId} id={titleId}
level={2} level={2}
className={styles.heading()} className={styles.heading()}
weight="semibold" weight="semibold"
> >
{title} {title}
</ariaComponents.Text.Heading> </ariaComponents.Text.Heading>
)} )}
</aria.Header> </aria.Header>
)}
<div <div
ref={(ref) => { ref={(ref) => {

View File

@ -120,6 +120,7 @@ export default function AssetRow(props: AssetRowProps) {
driveStore, driveStore,
({ selectedKeys }) => selectedKeys.size === 0 || !selected || isSoleSelected, ({ selectedKeys }) => selectedKeys.size === 0 || !selected || isSoleSelected,
) )
const wasSoleSelectedRef = React.useRef(isSoleSelected)
const draggableProps = dragAndDropHooks.useDraggable() const draggableProps = dragAndDropHooks.useDraggable()
const { setModal, unsetModal } = modalProvider.useSetModal() const { setModal, unsetModal } = modalProvider.useSetModal()
const { getText } = textProvider.useText() const { getText } = textProvider.useText()
@ -563,7 +564,12 @@ export default function AssetRow(props: AssetRowProps) {
rootRef.current = element rootRef.current = element
requestAnimationFrame(() => { requestAnimationFrame(() => {
if (isSoleSelected && element != null && scrollContainerRef.current != null) { if (
isSoleSelected &&
!wasSoleSelectedRef.current &&
element != null &&
scrollContainerRef.current != null
) {
const rect = element.getBoundingClientRect() const rect = element.getBoundingClientRect()
const scrollRect = scrollContainerRef.current.getBoundingClientRect() const scrollRect = scrollContainerRef.current.getBoundingClientRect()
const scrollUp = rect.top - (scrollRect.top + HEADER_HEIGHT_PX) const scrollUp = rect.top - (scrollRect.top + HEADER_HEIGHT_PX)
@ -576,6 +582,7 @@ export default function AssetRow(props: AssetRowProps) {
}) })
} }
} }
wasSoleSelectedRef.current = isSoleSelected
}) })
if (isKeyboardSelected && element?.contains(document.activeElement) === false) { if (isKeyboardSelected && element?.contains(document.activeElement) === false) {

View File

@ -1,6 +1,4 @@
/** @file The icon and name of a {@link backendModule.SecretAsset}. */ /** @file The icon and name of a {@link backendModule.SecretAsset}. */
import * as React from 'react'
import DatalinkIcon from '#/assets/datalink.svg' import DatalinkIcon from '#/assets/datalink.svg'
import * as setAssetHooks from '#/hooks/setAssetHooks' import * as setAssetHooks from '#/hooks/setAssetHooks'
@ -16,6 +14,7 @@ import * as eventModule from '#/utilities/event'
import * as indent from '#/utilities/indent' import * as indent from '#/utilities/indent'
import * as object from '#/utilities/object' import * as object from '#/utilities/object'
import * as tailwindMerge from '#/utilities/tailwindMerge' import * as tailwindMerge from '#/utilities/tailwindMerge'
import { isOnMacOS } from 'enso-common/src/detect'
// ==================== // ====================
// === DatalinkName === // === DatalinkName ===
@ -69,7 +68,7 @@ export default function DatalinkNameColumn(props: DatalinkNameColumnProps) {
onClick={(event) => { onClick={(event) => {
if (handleClick(event)) { if (handleClick(event)) {
// Already handled. // Already handled.
} else if (eventModule.isSingleClick(event) && selected) { } else if (eventModule.isSingleClick(event) && isOnMacOS() && selected) {
setIsEditing(true) setIsEditing(true)
} else if (eventModule.isDoubleClick(event)) { } else if (eventModule.isDoubleClick(event)) {
event.stopPropagation() event.stopPropagation()

View File

@ -25,6 +25,7 @@ import * as object from '#/utilities/object'
import * as string from '#/utilities/string' import * as string from '#/utilities/string'
import * as tailwindMerge from '#/utilities/tailwindMerge' import * as tailwindMerge from '#/utilities/tailwindMerge'
import * as validation from '#/utilities/validation' import * as validation from '#/utilities/validation'
import { isOnMacOS } from 'enso-common/src/detect'
// ===================== // =====================
// === DirectoryName === // === DirectoryName ===
@ -108,6 +109,7 @@ export default function DirectoryNameColumn(props: DirectoryNameColumnProps) {
// Already handled. // Already handled.
} else if ( } else if (
eventModule.isSingleClick(event) && eventModule.isSingleClick(event) &&
isOnMacOS() &&
selected && selected &&
driveStore.getState().selectedKeys.size === 1 driveStore.getState().selectedKeys.size === 1
) { ) {

View File

@ -1,6 +1,4 @@
/** @file The icon and name of a {@link backendModule.FileAsset}. */ /** @file The icon and name of a {@link backendModule.FileAsset}. */
import * as React from 'react'
import { useMutation } from '@tanstack/react-query' import { useMutation } from '@tanstack/react-query'
import { backendMutationOptions } from '#/hooks/backendHooks' import { backendMutationOptions } from '#/hooks/backendHooks'
@ -21,6 +19,7 @@ import * as indent from '#/utilities/indent'
import * as object from '#/utilities/object' import * as object from '#/utilities/object'
import * as string from '#/utilities/string' import * as string from '#/utilities/string'
import * as tailwindMerge from '#/utilities/tailwindMerge' import * as tailwindMerge from '#/utilities/tailwindMerge'
import { isOnMacOS } from 'enso-common/src/detect'
// ================ // ================
// === FileName === // === FileName ===
@ -95,7 +94,7 @@ export default function FileNameColumn(props: FileNameColumnProps) {
onClick={(event) => { onClick={(event) => {
if (handleClick(event)) { if (handleClick(event)) {
// Already handled. // Already handled.
} else if (eventModule.isSingleClick(event) && selected) { } else if (eventModule.isSingleClick(event) && isOnMacOS() && selected) {
if (!isCloud) { if (!isCloud) {
setIsEditing(true) setIsEditing(true)
} }

View File

@ -1,6 +1,4 @@
/** @file The icon and name of a {@link backendModule.ProjectAsset}. */ /** @file The icon and name of a {@link backendModule.ProjectAsset}. */
import * as React from 'react'
import { useMutation } from '@tanstack/react-query' import { useMutation } from '@tanstack/react-query'
import NetworkIcon from '#/assets/network.svg' import NetworkIcon from '#/assets/network.svg'
@ -29,6 +27,7 @@ import * as permissions from '#/utilities/permissions'
import * as string from '#/utilities/string' import * as string from '#/utilities/string'
import * as tailwindMerge from '#/utilities/tailwindMerge' import * as tailwindMerge from '#/utilities/tailwindMerge'
import * as validation from '#/utilities/validation' import * as validation from '#/utilities/validation'
import { isOnMacOS } from 'enso-common/src/detect'
// =================== // ===================
// === ProjectName === // === ProjectName ===
@ -130,7 +129,7 @@ export default function ProjectNameColumn(props: ProjectNameColumnProps) {
indent.indentClass(item.depth), indent.indentClass(item.depth),
)} )}
onKeyDown={(event) => { onKeyDown={(event) => {
if (rowState.isEditingName && event.key === 'Enter') { if (rowState.isEditingName && isOnMacOS() && event.key === 'Enter') {
event.stopPropagation() event.stopPropagation()
} }
}} }}

View File

@ -1,6 +1,4 @@
/** @file The icon and name of a {@link backendModule.SecretAsset}. */ /** @file The icon and name of a {@link backendModule.SecretAsset}. */
import * as React from 'react'
import { useMutation } from '@tanstack/react-query' import { useMutation } from '@tanstack/react-query'
import KeyIcon from '#/assets/key.svg' import KeyIcon from '#/assets/key.svg'
@ -23,6 +21,7 @@ import * as eventModule from '#/utilities/event'
import * as indent from '#/utilities/indent' import * as indent from '#/utilities/indent'
import * as object from '#/utilities/object' import * as object from '#/utilities/object'
import * as tailwindMerge from '#/utilities/tailwindMerge' import * as tailwindMerge from '#/utilities/tailwindMerge'
import { isOnMacOS } from 'enso-common/src/detect'
// ===================== // =====================
// === ConnectorName === // === ConnectorName ===
@ -74,7 +73,7 @@ export default function SecretNameColumn(props: SecretNameColumnProps) {
onClick={(event) => { onClick={(event) => {
if (handleClick(event)) { if (handleClick(event)) {
// Already handled. // Already handled.
} else if (eventModule.isSingleClick(event) && selected) { } else if (eventModule.isSingleClick(event) && isOnMacOS() && selected) {
setIsEditing(true) setIsEditing(true)
} else if (eventModule.isDoubleClick(event) && isEditable) { } else if (eventModule.isDoubleClick(event) && isEditable) {
event.stopPropagation() event.stopPropagation()

View File

@ -35,7 +35,7 @@ interface BackendApi {
openedPath: string, openedPath: string,
directory: string | null, directory: string | null,
name: string, name: string,
) => Promise<string> ) => Promise<ProjectInfo>
} }
// ========================== // ==========================

View File

@ -37,6 +37,8 @@ import UpsertSecretModal from '#/modals/UpsertSecretModal'
import * as backendModule from '#/services/Backend' import * as backendModule from '#/services/Backend'
import * as localBackendModule from '#/services/LocalBackend' import * as localBackendModule from '#/services/LocalBackend'
import { normalizePath } from '#/utilities/fileInfo'
import { mapNonNullish } from '#/utilities/nullable'
import * as object from '#/utilities/object' import * as object from '#/utilities/object'
import * as permissions from '#/utilities/permissions' import * as permissions from '#/utilities/permissions'
@ -86,8 +88,8 @@ export default function AssetContextMenu(props: AssetContextMenuProps) {
category.type === 'recent' || category.type === 'trash' ? null category.type === 'recent' || category.type === 'trash' ? null
: isCloud ? `${item.path}${item.type === backendModule.AssetType.datalink ? '.datalink' : ''}` : isCloud ? `${item.path}${item.type === backendModule.AssetType.datalink ? '.datalink' : ''}`
: asset.type === backendModule.AssetType.project ? : asset.type === backendModule.AssetType.project ?
localBackend?.getProjectPath(asset.id) ?? null mapNonNullish(localBackend?.getProjectPath(asset.id) ?? null, normalizePath)
: localBackendModule.extractTypeAndId(asset.id).id : normalizePath(localBackendModule.extractTypeAndId(asset.id).id)
const copyMutation = copyHooks.useCopy({ copyText: path ?? '' }) const copyMutation = copyHooks.useCopy({ copyText: path ?? '' })
const { isFeatureUnderPaywall } = billingHooks.usePaywall({ plan: user.plan }) const { isFeatureUnderPaywall } = billingHooks.usePaywall({ plan: user.plan })

View File

@ -1731,7 +1731,12 @@ export default function AssetsTable(props: AssetsTableProps) {
// This non-standard property is defined in Electron. // This non-standard property is defined in Electron.
'path' in file 'path' in file
) { ) {
id = await window.backendApi.importProjectFromPath(file.path, directory, title) const projectInfo = await window.backendApi.importProjectFromPath(
file.path,
directory,
title,
)
id = projectInfo.id
} else { } else {
const searchParams = new URLSearchParams({ directory, name: title }).toString() const searchParams = new URLSearchParams({ directory, name: title }).toString()
// Ideally this would use `file.stream()`, to minimize RAM // Ideally this would use `file.stream()`, to minimize RAM
@ -2571,6 +2576,7 @@ export default function AssetsTable(props: AssetsTableProps) {
onDragStart={(event) => { onDragStart={(event) => {
startAutoScroll() startAutoScroll()
onMouseEvent(event) onMouseEvent(event)
event.dataTransfer.effectAllowed = 'move'
let newSelectedKeys = driveStore.getState().selectedKeys let newSelectedKeys = driveStore.getState().selectedKeys
if (!newSelectedKeys.has(item.key)) { if (!newSelectedKeys.has(item.key)) {
setMostRecentlySelectedIndex(visibleItems.indexOf(item)) setMostRecentlySelectedIndex(visibleItems.indexOf(item))
@ -2659,7 +2665,11 @@ export default function AssetsTable(props: AssetsTableProps) {
} }
} }
}} }}
onDragLeave={() => {
setIsDraggingFiles(false)
}}
onDragEnd={() => { onDragEnd={() => {
setIsDraggingFiles(false)
endAutoScroll() endAutoScroll()
lastSelectedIdsRef.current = null lastSelectedIdsRef.current = null
const { selectedKeys } = driveStore.getState() const { selectedKeys } = driveStore.getState()
@ -2670,6 +2680,7 @@ export default function AssetsTable(props: AssetsTableProps) {
}) })
}} }}
onDrop={(event) => { onDrop={(event) => {
setIsDraggingFiles(false)
endAutoScroll() endAutoScroll()
const { selectedKeys } = driveStore.getState() const { selectedKeys } = driveStore.getState()
const ids = new Set(selectedKeys.has(item.key) ? selectedKeys : [item.key]) const ids = new Set(selectedKeys.has(item.key) ? selectedKeys : [item.key])
@ -2946,8 +2957,13 @@ export default function AssetsTable(props: AssetsTableProps) {
className="flex items-center justify-center gap-3 rounded-default bg-selected-frame px-8 py-6 text-primary/50 backdrop-blur-3xl transition-all" className="flex items-center justify-center gap-3 rounded-default bg-selected-frame px-8 py-6 text-primary/50 backdrop-blur-3xl transition-all"
onDragEnter={onDropzoneDragOver} onDragEnter={onDropzoneDragOver}
onDragOver={onDropzoneDragOver} onDragOver={onDropzoneDragOver}
onDrop={(event) => { onDragLeave={() => {
setIsDraggingFiles(false) setIsDraggingFiles(false)
}}
onDragEnd={() => {
setIsDraggingFiles(false)
}}
onDrop={(event) => {
handleFileDrop(event) handleFileDrop(event)
}} }}
> >

View File

@ -40,6 +40,7 @@ import * as backend from '#/services/Backend'
import type LocalBackend from '#/services/LocalBackend' import type LocalBackend from '#/services/LocalBackend'
import type RemoteBackend from '#/services/RemoteBackend' import type RemoteBackend from '#/services/RemoteBackend'
import { normalizePath } from '#/utilities/fileInfo'
import * as object from '#/utilities/object' import * as object from '#/utilities/object'
// ========================= // =========================
@ -259,7 +260,7 @@ export const SETTINGS_TAB_DATA: Readonly<Record<SettingsTabType, SettingsTabData
const [newDirectory] = const [newDirectory] =
(await window.fileBrowserApi?.openFileBrowser('directory')) ?? [] (await window.fileBrowserApi?.openFileBrowser('directory')) ?? []
if (newDirectory != null) { if (newDirectory != null) {
context.updateLocalRootPath(newDirectory) context.updateLocalRootPath(normalizePath(newDirectory))
} }
}} }}
> >

View File

@ -2,7 +2,6 @@
* interactive components. */ * interactive components. */
import * as React from 'react' import * as React from 'react'
import * as validator from 'validator'
import * as z from 'zod' import * as z from 'zod'
import * as detect from 'enso-common/src/detect' import * as detect from 'enso-common/src/detect'
@ -56,9 +55,12 @@ import * as backendModule from '#/services/Backend'
import * as localBackendModule from '#/services/LocalBackend' import * as localBackendModule from '#/services/LocalBackend'
import * as projectManager from '#/services/ProjectManager' import * as projectManager from '#/services/ProjectManager'
import { baseName } from '#/utilities/fileInfo'
import LocalStorage from '#/utilities/LocalStorage' import LocalStorage from '#/utilities/LocalStorage'
import * as object from '#/utilities/object' import * as object from '#/utilities/object'
import { STATIC_QUERY_OPTIONS } from '#/utilities/reactQuery'
import * as sanitizedEventTargets from '#/utilities/sanitizedEventTargets' import * as sanitizedEventTargets from '#/utilities/sanitizedEventTargets'
import { usePrefetchQuery } from '@tanstack/react-query'
// ============================ // ============================
// === Global configuration === // === Global configuration ===
@ -113,11 +115,11 @@ function DashboardInner(props: DashboardProps) {
const dispatchAssetListEvent = eventListProvider.useDispatchAssetListEvent() const dispatchAssetListEvent = eventListProvider.useDispatchAssetListEvent()
const assetManagementApiRef = React.useRef<assetTable.AssetManagementApi | null>(null) const assetManagementApiRef = React.useRef<assetTable.AssetManagementApi | null>(null)
const initialLocalProjectId = const initialLocalProjectPath =
initialProjectNameRaw != null && validator.isUUID(initialProjectNameRaw) ? initialProjectNameRaw != null && initialProjectNameRaw.startsWith('file://') ?
localBackendModule.newProjectId(projectManager.UUID(initialProjectNameRaw)) projectManager.Path(decodeURI(new URL(initialProjectNameRaw).pathname))
: null : null
const initialProjectName = initialLocalProjectId ?? initialProjectNameRaw const initialProjectName = initialLocalProjectPath != null ? null : initialProjectNameRaw
const [category, setCategory] = searchParamsState.useSearchParamsState<categoryModule.Category>( const [category, setCategory] = searchParamsState.useSearchParamsState<categoryModule.Category>(
'driveCategory', 'driveCategory',
@ -140,6 +142,30 @@ function DashboardInner(props: DashboardProps) {
const openProjectMutation = projectHooks.useOpenProjectMutation() const openProjectMutation = projectHooks.useOpenProjectMutation()
const renameProjectMutation = projectHooks.useRenameProjectMutation() const renameProjectMutation = projectHooks.useRenameProjectMutation()
usePrefetchQuery({
queryKey: ['loadInitialLocalProject'],
networkMode: 'always',
...STATIC_QUERY_OPTIONS,
queryFn: async () => {
if (initialLocalProjectPath != null && window.backendApi && localBackend) {
const projectName = baseName(initialLocalProjectPath)
const { id } = await window.backendApi.importProjectFromPath(
initialLocalProjectPath,
localBackend.rootPath(),
projectName,
)
openProject({
type: backendModule.BackendType.local,
id: localBackendModule.newProjectId(projectManager.UUID(id)),
title: projectName,
parentId: localBackendModule.newDirectoryId(localBackend.rootPath()),
})
}
return null
},
staleTime: Infinity,
})
React.useEffect(() => { React.useEffect(() => {
window.projectManagementApi?.setOpenProjectHandler((project) => { window.projectManagementApi?.setOpenProjectHandler((project) => {
setCategory({ type: 'local' }) setCategory({ type: 'local' })

View File

@ -12,6 +12,7 @@ import * as localStorageProvider from '#/providers/LocalStorageProvider'
import * as backendModule from '#/services/Backend' import * as backendModule from '#/services/Backend'
import { useMounted } from '#/hooks/mountHooks'
import * as array from '#/utilities/array' import * as array from '#/utilities/array'
import LocalStorage from '#/utilities/LocalStorage' import LocalStorage from '#/utilities/LocalStorage'
@ -107,6 +108,28 @@ const ProjectsContext = React.createContext<ProjectsContextType | null>(null)
/** Props for a {@link ProjectsProvider}. */ /** Props for a {@link ProjectsProvider}. */
export interface ProjectsProviderProps extends Readonly<React.PropsWithChildren> {} export interface ProjectsProviderProps extends Readonly<React.PropsWithChildren> {}
const STORE = zustand.createStore<ProjectsStore>((set) => ({
page: TabType.drive,
setPage: (page) => {
set({ page })
},
launchedProjects: [],
updateLaunchedProjects: (update) => {
set(({ launchedProjects }) => ({ launchedProjects: update(launchedProjects) }))
},
addLaunchedProject: (project) => {
set(({ launchedProjects }) => ({ launchedProjects: [...launchedProjects, project] }))
},
removeLaunchedProject: (projectId) => {
set(({ launchedProjects }) => ({
launchedProjects: launchedProjects.filter(({ id }) => id !== projectId),
}))
},
clearLaunchedProjects: () => {
set({ launchedProjects: [] })
},
}))
// ======================== // ========================
// === ProjectsProvider === // === ProjectsProvider ===
// ======================== // ========================
@ -116,28 +139,15 @@ export interface ProjectsProviderProps extends Readonly<React.PropsWithChildren>
export default function ProjectsProvider(props: ProjectsProviderProps) { export default function ProjectsProvider(props: ProjectsProviderProps) {
const { children } = props const { children } = props
const { localStorage } = localStorageProvider.useLocalStorage() const { localStorage } = localStorageProvider.useLocalStorage()
const [store] = React.useState(() => { const store = STORE
return zustand.createStore<ProjectsStore>((set) => ({
page: TabType.drive, useMounted(() => {
setPage: (page) => { const launchedProjects = localStorage.get('launchedProjects')
set({ page }) if (launchedProjects) {
}, store
launchedProjects: localStorage.get('launchedProjects') ?? [], .getState()
updateLaunchedProjects: (update) => { .updateLaunchedProjects((projects) => (projects.length === 0 ? launchedProjects : projects))
set(({ launchedProjects }) => ({ launchedProjects: update(launchedProjects) })) }
},
addLaunchedProject: (project) => {
set(({ launchedProjects }) => ({ launchedProjects: [...launchedProjects, project] }))
},
removeLaunchedProject: (projectId) => {
set(({ launchedProjects }) => ({
launchedProjects: launchedProjects.filter(({ id }) => id !== projectId),
}))
},
clearLaunchedProjects: () => {
set({ launchedProjects: [] })
},
}))
}) })
return ( return (

View File

@ -401,12 +401,17 @@ export default class ProjectManager {
state: backend.ProjectState.openInProgress, state: backend.ProjectState.openInProgress,
data: promise, data: promise,
}) })
const result = await promise try {
this.internalProjects.set(params.projectId, { const result = await promise
state: backend.ProjectState.opened, this.internalProjects.set(params.projectId, {
data: result, state: backend.ProjectState.opened,
}) data: result,
return result })
return result
} catch (error) {
this.internalProjects.delete(params.projectId)
throw error
}
} }
} }

View File

@ -6,17 +6,22 @@
/** Return just the file name, including the extension. */ /** Return just the file name, including the extension. */
export function getFileName(filePath: string) { export function getFileName(filePath: string) {
return filePath.match(/(?:[/]|^)([^/]+)[/]?$/)?.[1] ?? filePath return filePath.match(/(?:[/\\]|^)([^/\\]+)[/\\]?$/)?.[1] ?? filePath
} }
/** Return the entire path, without the file name. */ /** Return the entire path, without the file name. */
export function getFolderPath(filePath: string) { export function getFolderPath(filePath: string) {
return filePath.match(/^.+[/]/)?.[0] ?? filePath return filePath.match(/^.+[/\\]/)?.[0] ?? filePath
} }
/** Return just the file name, without the path and without the extension. */ /** Return just the file name, without the path and without the extension. */
export function baseName(fileNameOrPath: string) { export function baseName(fileNameOrPath: string) {
return fileNameOrPath.match(/(?:[/]|^)([^./]+)(?:[.][^/]*)?$/)?.[1] ?? fileNameOrPath return fileNameOrPath.match(/(?:[\\/]|^)([^./\\]+)(?:[.][^/\\]*)?$/)?.[1] ?? fileNameOrPath
}
/** Normalize a path to use `/` instead of `\`. */
export function normalizePath(path: string) {
return path.replace(/\\/g, '/')
} }
/** Extract the file extension from a file name. */ /** Extract the file extension from a file name. */

View File

@ -0,0 +1,7 @@
/** @file Functions related to nullable types. */
/** Call a transformation function when the value is non-nullish. */
// eslint-disable-next-line @typescript-eslint/ban-types
export function mapNonNullish<T extends {}, R>(value: T | null | undefined, map: (value: T) => R) {
return value != null ? map(value) : value
}

View File

@ -0,0 +1,12 @@
/** @file Utilities related to the `react-query` library. */
import type { DefinedInitialDataOptions } from '@tanstack/react-query'
export const STATIC_QUERY_OPTIONS = {
meta: { persist: false },
staleTime: Infinity,
gcTime: Infinity,
refetchOnMount: false,
refetchInterval: false,
refetchOnReconnect: false,
refetchIntervalInBackground: false,
} as const satisfies Partial<DefinedInitialDataOptions>

View File

@ -61,7 +61,7 @@
"@lezer/common": "^1.1.0", "@lezer/common": "^1.1.0",
"@lezer/highlight": "^1.1.6", "@lezer/highlight": "^1.1.6",
"@noble/hashes": "^1.4.0", "@noble/hashes": "^1.4.0",
"@tanstack/vue-query": ">= 5.45.0 < 5.46.0", "@tanstack/vue-query": ">= 5.54.0 < 5.56.0",
"@vueuse/core": "^10.4.1", "@vueuse/core": "^10.4.1",
"ag-grid-community": "^30.2.1", "ag-grid-community": "^30.2.1",
"ag-grid-enterprise": "^30.2.1", "ag-grid-enterprise": "^30.2.1",

View File

@ -75,6 +75,9 @@ function main() {
const projectManagerUrl = config.engine.projectManagerUrl || PROJECT_MANAGER_URL const projectManagerUrl = config.engine.projectManagerUrl || PROJECT_MANAGER_URL
const ydocUrl = config.engine.ydocUrl === '' ? YDOC_SERVER_URL : config.engine.ydocUrl const ydocUrl = config.engine.ydocUrl === '' ? YDOC_SERVER_URL : config.engine.ydocUrl
const initialProjectName = config.startup.project || null const initialProjectName = config.startup.project || null
const urlWithoutStartupProject = new URL(location.toString())
urlWithoutStartupProject.searchParams.delete('startup.project')
history.replaceState(null, '', urlWithoutStartupProject)
const queryClient = commonQuery.createQueryClient() const queryClient = commonQuery.createQueryClient()
const registerPlugins = (app: App) => { const registerPlugins = (app: App) => {

View File

@ -38,6 +38,7 @@
"@types/semver": "^7.5.8", "@types/semver": "^7.5.8",
"@types/tar": "^6.1.4", "@types/tar": "^6.1.4",
"@types/yargs": "^17.0.30", "@types/yargs": "^17.0.30",
"cross-env": "^7.0.3",
"electron": "31.2.0", "electron": "31.2.0",
"electron-builder": "^24.13.3", "electron-builder": "^24.13.3",
"enso-common": "workspace:*", "enso-common": "workspace:*",
@ -59,8 +60,8 @@
"typecheck": "tsc --build", "typecheck": "tsc --build",
"build": "tsx bundle.ts", "build": "tsx bundle.ts",
"dist": "tsx dist.ts", "dist": "tsx dist.ts",
"watch:windows": "cross-env ENSO_BUILD_IDE=%LOCALAPPDATA%/Temp/enso/dist/ide ENSO_BUILD_PROJECT_MANAGER=%CD%/../../../dist/backend ENSO_BUILD_PROJECT_MANAGER_IN_BUNDLE_PATH=bin/project-manager.exe ENSO_BUILD_IDE_BUNDLED_ENGINE_VERSION=0 tsx watch.ts", "watch:windows": "cross-env ENSO_BUILD_IDE=%LOCALAPPDATA%\\Temp\\enso\\dist\\ide ENSO_BUILD_PROJECT_MANAGER=%CD%\\..\\..\\..\\dist\\backend ENSO_BUILD_PROJECT_MANAGER_IN_BUNDLE_PATH=bin\\project-manager.exe ENSO_BUILD_IDE_BUNDLED_ENGINE_VERSION=0 ENSO_POLYGLOT_YDOC_SERVER=wss://localhost:8080 tsx watch.ts",
"watch:linux": "ENSO_BUILD_IDE=\"${ENSO_BUILD_IDE:-/tmp/enso/dist/ide}\" ENSO_BUILD_PROJECT_MANAGER=\"${ENSO_BUILD_PROJECT_MANAGER:-\"$(pwd)/../../../dist/backend\"}\" ENSO_BUILD_PROJECT_MANAGER_IN_BUNDLE_PATH=\"${ENSO_BUILD_PROJECT_MANAGER_IN_BUNDLE_PATH:-bin/project-manager}\" ENSO_BUILD_IDE_BUNDLED_ENGINE_VERSION=\"${ENSO_BUILD_IDE_BUNDLED_ENGINE_VERSION:-0}\" tsx watch.ts \"$@\"", "watch:linux": "ENSO_BUILD_IDE=\"${ENSO_BUILD_IDE:-/tmp/enso/dist/ide}\" ENSO_BUILD_PROJECT_MANAGER=\"${ENSO_BUILD_PROJECT_MANAGER:-\"$(pwd)/../../../dist/backend\"}\" ENSO_BUILD_PROJECT_MANAGER_IN_BUNDLE_PATH=\"${ENSO_BUILD_PROJECT_MANAGER_IN_BUNDLE_PATH:-bin/project-manager}\" ENSO_BUILD_IDE_BUNDLED_ENGINE_VERSION=\"${ENSO_BUILD_IDE_BUNDLED_ENGINE_VERSION:-0}\" ENSO_POLYGLOT_YDOC_SERVER=\"${ENSO_POLYGLOT_YDOC_SERVER:-wss://localhost:8080}\" tsx watch.ts \"$@\"",
"watch:macos": "ENSO_BUILD_IDE=\"${ENSO_BUILD_IDE:-/tmp/enso/dist/ide}\" ENSO_BUILD_PROJECT_MANAGER=\"${ENSO_BUILD_PROJECT_MANAGER:-\"$(pwd)/../../../dist/backend\"}\" ENSO_BUILD_PROJECT_MANAGER_IN_BUNDLE_PATH=\"${ENSO_BUILD_PROJECT_MANAGER_IN_BUNDLE_PATH:-bin/project-manager}\" ENSO_BUILD_IDE_BUNDLED_ENGINE_VERSION=\"${ENSO_BUILD_IDE_BUNDLED_ENGINE_VERSION:-0}\" tsx watch.ts \"$@\"" "watch:macos": "ENSO_BUILD_IDE=\"${ENSO_BUILD_IDE:-/tmp/enso/dist/ide}\" ENSO_BUILD_PROJECT_MANAGER=\"${ENSO_BUILD_PROJECT_MANAGER:-\"$(pwd)/../../../dist/backend\"}\" ENSO_BUILD_PROJECT_MANAGER_IN_BUNDLE_PATH=\"${ENSO_BUILD_PROJECT_MANAGER_IN_BUNDLE_PATH:-bin/project-manager}\" ENSO_BUILD_IDE_BUNDLED_ENGINE_VERSION=\"${ENSO_BUILD_IDE_BUNDLED_ENGINE_VERSION:-0}\" ENSO_POLYGLOT_YDOC_SERVER=\"${ENSO_POLYGLOT_YDOC_SERVER:-wss://localhost:8080}\" tsx watch.ts \"$@\""
} }
} }

View File

@ -13,7 +13,6 @@ import electronIsDev from 'electron-is-dev'
import * as common from 'enso-common' import * as common from 'enso-common'
import type * as clientConfig from '@/config'
import * as contentConfig from '@/contentConfig' import * as contentConfig from '@/contentConfig'
import * as project from '@/projectManagement' import * as project from '@/projectManagement'
import * as fileAssociations from '../fileAssociations' import * as fileAssociations from '../fileAssociations'
@ -100,13 +99,13 @@ export function isFileOpenable(path: string): boolean {
} }
/** Callback called when a file is opened via the `open-file` event. */ /** Callback called when a file is opened via the `open-file` event. */
export function onFileOpened(event: electron.Event, path: string): project.ProjectInfo | null { export function onFileOpened(event: electron.Event, path: string): string | null {
logger.log(`Received 'open-file' event for path '${path}'.`) logger.log(`Received 'open-file' event for path '${path}'.`)
if (isFileOpenable(path)) { if (isFileOpenable(path)) {
logger.log(`The file '${path}' is openable.`) logger.log(`The file '${path}' is openable.`)
event.preventDefault() event.preventDefault()
logger.log(`Opening file '${path}'.`) logger.log(`Opening file '${path}'.`)
return handleOpenFile(path) return path
} else { } else {
logger.log(`The file '${path}' is not openable, ignoring the 'open-file' event.`) logger.log(`The file '${path}' is not openable, ignoring the 'open-file' event.`)
return null return null
@ -116,13 +115,10 @@ export function onFileOpened(event: electron.Event, path: string): project.Proje
/** Set up the `open-file` event handler that might import a project and invoke the given callback, /** Set up the `open-file` event handler that might import a project and invoke the given callback,
* if this IDE instance should load the project. See {@link onFileOpened} for more details. * if this IDE instance should load the project. See {@link onFileOpened} for more details.
* @param setProjectToOpen - A function that will be called with the ID of the project to open. */ * @param setProjectToOpen - A function that will be called with the ID of the project to open. */
export function setOpenFileEventHandler(setProjectToOpen: (info: project.ProjectInfo) => void) { export function setOpenFileEventHandler(setProjectToOpen: (path: string) => void) {
electron.app.on('open-file', (event, path) => { electron.app.on('open-file', (_event, path) => {
logger.log(`Opening file '${path}'.`) logger.log(`Opening file '${path}'.`)
const projectInfo = onFileOpened(event, path) setProjectToOpen(path)
if (projectInfo) {
setProjectToOpen(projectInfo)
}
}) })
electron.app.on('second-instance', (event, _argv, _workingDir, additionalData) => { electron.app.on('second-instance', (event, _argv, _workingDir, additionalData) => {
@ -140,9 +136,9 @@ export function setOpenFileEventHandler(setProjectToOpen: (info: project.Project
if (path != null) { if (path != null) {
logger.log(`Got path '${path.toString()}' from second instance.`) logger.log(`Got path '${path.toString()}' from second instance.`)
event.preventDefault() event.preventDefault()
const projectInfo = onFileOpened(event, path) const file = onFileOpened(event, path)
if (projectInfo) { if (file) {
setProjectToOpen(projectInfo) setProjectToOpen(file)
} }
} }
}) })
@ -171,24 +167,3 @@ export function handleOpenFile(openedFile: string): project.ProjectInfo {
throw error throw error
} }
} }
/** Handle the file to open, if any. See {@link handleOpenFile} for details.
*
* If no file to open is provided, does nothing.
*
* Handles all errors internally.
* @param openedFile - The file to open (null if none).
* @param args - The parsed application arguments. */
export function handleFileArguments(openedFile: string | null, args: clientConfig.Args): void {
if (openedFile != null) {
try {
// This makes the IDE open the relevant project. Also, this prevents us from using this
// method after IDE has been fully set up, as the initializing code would have already
// read the value of this argument.
args.groups.startup.options.project.value = handleOpenFile(openedFile).id
} catch (e) {
// If we failed to open the file, we should enter the usual welcome screen.
// The `handleOpenFile` function will have already displayed an error message.
}
}
}

View File

@ -33,7 +33,7 @@ interface BackendApi {
openedPath: string, openedPath: string,
directory: string | null, directory: string | null,
name: string, name: string,
) => Promise<string> ) => Promise<ProjectInfo>
} }
// ========================== // ==========================

View File

@ -58,11 +58,12 @@ class App {
log.addFileLog() log.addFileLog()
urlAssociations.registerAssociations() urlAssociations.registerAssociations()
// Register file associations for macOS. // Register file associations for macOS.
fileAssociations.setOpenFileEventHandler(project => { fileAssociations.setOpenFileEventHandler(path => {
if (electron.app.isReady()) { if (electron.app.isReady()) {
const project = fileAssociations.handleOpenFile(path)
this.window?.webContents.send(ipc.Channel.openProject, project) this.window?.webContents.send(ipc.Channel.openProject, project)
} else { } else {
this.setProjectToOpenOnStartup(project.id) this.setProjectToOpenOnStartup(path)
} }
}) })
@ -170,11 +171,9 @@ class App {
logger.log('Opening file or URL.', { fileToOpen, urlToOpen }) logger.log('Opening file or URL.', { fileToOpen, urlToOpen })
try { try {
if (fileToOpen != null) { if (fileToOpen != null) {
// This makes the IDE open the relevant project. Also, this prevents us from using // The IDE must receive the project path, otherwise if the IDE has a custom root directory
// this method after the IDE has been fully set up, as the initializing code // set then it is added to the (incorrect) default root directory.
// would have already read the value of this argument. this.setProjectToOpenOnStartup(`file://${encodeURI(fileToOpen)}`)
const projectInfo = fileAssociations.handleOpenFile(fileToOpen)
this.setProjectToOpenOnStartup(projectInfo.id)
} }
if (urlToOpen != null) { if (urlToOpen != null) {

View File

@ -14,6 +14,7 @@ import type * as projectManagement from '@/projectManagement'
// esbuild, we have to manually use "require". Switch this to an import once new electron version // esbuild, we have to manually use "require". Switch this to an import once new electron version
// actually honours ".mjs" files for sandboxed preloading (this will likely become an error at that time). // actually honours ".mjs" files for sandboxed preloading (this will likely become an error at that time).
// https://www.electronjs.org/fr/docs/latest/tutorial/esm#sandboxed-preload-scripts-cant-use-esm-imports // https://www.electronjs.org/fr/docs/latest/tutorial/esm#sandboxed-preload-scripts-cant-use-esm-imports
// eslint-disable-next-line no-restricted-syntax, @typescript-eslint/no-var-requires
const electron = require('electron') const electron = require('electron')
// ================= // =================
@ -45,12 +46,15 @@ function exposeInMainWorld<Key extends string & keyof typeof window>(
// === importProjectFromPath === // === importProjectFromPath ===
// ============================= // =============================
const IMPORT_PROJECT_RESOLVE_FUNCTIONS = new Map<string, (projectId: string) => void>() const IMPORT_PROJECT_RESOLVE_FUNCTIONS = new Map<
string,
(projectId: projectManagement.ProjectInfo) => void
>()
exposeInMainWorld(BACKEND_API_KEY, { exposeInMainWorld(BACKEND_API_KEY, {
importProjectFromPath: (projectPath: string, directory: string | null = null) => { importProjectFromPath: (projectPath: string, directory: string | null = null) => {
electron.ipcRenderer.send(ipc.Channel.importProjectFromPath, projectPath, directory) electron.ipcRenderer.send(ipc.Channel.importProjectFromPath, projectPath, directory)
return new Promise<string>(resolve => { return new Promise<projectManagement.ProjectInfo>(resolve => {
IMPORT_PROJECT_RESOLVE_FUNCTIONS.set(projectPath, resolve) IMPORT_PROJECT_RESOLVE_FUNCTIONS.set(projectPath, resolve)
}) })
}, },
@ -67,10 +71,10 @@ exposeInMainWorld(NAVIGATION_API_KEY, {
electron.ipcRenderer.on( electron.ipcRenderer.on(
ipc.Channel.importProjectFromPath, ipc.Channel.importProjectFromPath,
(_event, projectPath: string, projectId: string) => { (_event, projectPath: string, projectInfo: projectManagement.ProjectInfo) => {
const resolveFunction = IMPORT_PROJECT_RESOLVE_FUNCTIONS.get(projectPath) const resolveFunction = IMPORT_PROJECT_RESOLVE_FUNCTIONS.get(projectPath)
IMPORT_PROJECT_RESOLVE_FUNCTIONS.delete(projectPath) IMPORT_PROJECT_RESOLVE_FUNCTIONS.delete(projectPath)
resolveFunction?.(projectId) resolveFunction?.(projectInfo)
}, },
) )

View File

@ -22,7 +22,8 @@ import { pathToFileURL } from 'node:url'
const logger = contentConfig.logger const logger = contentConfig.logger
ydocServer.configureAllDebugLogs(process.env.ENSO_YDOC_LS_DEBUG === 'true', logger.log) // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
ydocServer.configureAllDebugLogs(process.env.ENSO_YDOC_LS_DEBUG === 'true', logger.log.bind(logger))
// ================= // =================
// === Constants === // === Constants ===
@ -94,7 +95,7 @@ export class Server {
/** Create a simple HTTP server. */ /** Create a simple HTTP server. */
constructor(public config: Config) { constructor(public config: Config) {
this.projectsRootDirectory = projectManagement.getProjectsDirectory() this.projectsRootDirectory = projectManagement.getProjectsDirectory().replace(/\\/g, '/')
} }
/** Server constructor. */ /** Server constructor. */
@ -147,8 +148,10 @@ export class Server {
logger.log(`Server started on port ${this.config.port}.`) logger.log(`Server started on port ${this.config.port}.`)
logger.log(`Serving files from '${path.join(process.cwd(), this.config.dir)}'.`) logger.log(`Serving files from '${path.join(process.cwd(), this.config.dir)}'.`)
if (process.env.ELECTRON_DEV_MODE === 'true') { if (process.env.ELECTRON_DEV_MODE === 'true') {
// eslint-disable-next-line no-restricted-syntax
const vite = (await import( const vite = (await import(
pathToFileURL(process.env.NODE_MODULES_PATH + '/vite/dist/node/index.js').href pathToFileURL(process.env.NODE_MODULES_PATH + '/vite/dist/node/index.js').href
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
)) as typeof import('vite') )) as typeof import('vite')
this.devServer = await vite.createServer({ this.devServer = await vite.createServer({
server: { server: {

View File

@ -94,7 +94,7 @@ const ELECTRON_ARGS = [
path.join(IDE_DIR_PATH, 'index.mjs'), path.join(IDE_DIR_PATH, 'index.mjs'),
...ELECTRON_FLAGS, ...ELECTRON_FLAGS,
'--', '--',
...process.argv.slice(2), ...process.argv.slice(2).map(arg => `'${arg}'`),
] ]
process.on('SIGINT', () => { process.on('SIGINT', () => {

View File

@ -28,14 +28,14 @@
"test": "vitest run" "test": "vitest run"
}, },
"peerDependencies": { "peerDependencies": {
"@tanstack/query-core": "5.45.0", "@tanstack/query-core": "5.54.1",
"@tanstack/vue-query": ">= 5.45.0 < 5.46.0" "@tanstack/vue-query": ">= 5.54.0 < 5.56.0"
}, },
"dependencies": { "dependencies": {
"idb-keyval": "^6.2.1", "idb-keyval": "^6.2.1",
"react": "^18.3.1", "react": "^18.3.1",
"@tanstack/query-persist-client-core": "^5.45.0", "@tanstack/query-persist-client-core": "^5.54.0",
"@tanstack/vue-query": ">= 5.45.0 < 5.46.0", "@tanstack/vue-query": ">= 5.54.0 < 5.56.0",
"vue": "^3.4.19", "vue": "^3.4.19",
"vitest": "^1.3.1" "vitest": "^1.3.1"
} }

View File

@ -6,8 +6,8 @@
import * as queryCore from '@tanstack/query-core' import * as queryCore from '@tanstack/query-core'
import * as persistClientCore from '@tanstack/query-persist-client-core' import * as persistClientCore from '@tanstack/query-persist-client-core'
import * as vueQuery from '@tanstack/vue-query'
import * as idbKeyval from 'idb-keyval' import * as idbKeyval from 'idb-keyval'
import * as vueQuery from './vueQuery'
declare module '@tanstack/query-core' { declare module '@tanstack/query-core' {
/** /**

View File

@ -410,7 +410,7 @@
"organizationProfilePictureWarning": "Your organizations profile picture is visible to everyone in your organization.", "organizationProfilePictureWarning": "Your organizations profile picture is visible to everyone in your organization.",
"noFilesMatchTheCurrentFilters": "No files match the current filters.", "noFilesMatchTheCurrentFilters": "No files match the current filters.",
"youHaveNoRecentProjects": "You have no recent projects. Switch to another category to create a project.", "youHaveNoRecentProjects": "You have no recent projects. Switch to another category to create a project.",
"youHaveNoFiles": "This folder is empty. Go ahead and create one using the buttons above, or open a template from the home screen.", "youHaveNoFiles": "This folder is empty. You can create a project using the buttons above.",
"placeholderChatPrompt": "Login or register to access live chat with our support team.", "placeholderChatPrompt": "Login or register to access live chat with our support team.",
"confirmPrompt": "Are you sure you want to $0?", "confirmPrompt": "Are you sure you want to $0?",
"couldNotInviteUser": "Could not invite user $0", "couldNotInviteUser": "Could not invite user $0",

View File

@ -1,98 +0,0 @@
/** @file QueryClient based on the '@tanstack/vue-query' implementation. */
import * as queryCore from '@tanstack/query-core'
import * as vueQuery from '@tanstack/vue-query'
import * as vue from 'vue'
/** The QueryClient from vue-query, but with immediate query invalidation. */
export class QueryClient extends vueQuery.QueryClient {
/** Like the `invalidateQueries` method of `vueQuery.QueryClient`, but invalidates queries immediately. */
// Workaround for https://github.com/TanStack/query/issues/7694
override invalidateQueries(
filters: MaybeRefDeep<queryCore.InvalidateQueryFilters> = {},
options: MaybeRefDeep<queryCore.InvalidateOptions> = {},
): Promise<void> {
const filtersValue = cloneDeepUnref(filters)
const optionsValue = cloneDeepUnref(options)
queryCore.notifyManager.batch(() => {
this.getQueryCache()
.findAll(filtersValue)
.forEach(query => {
query.invalidate()
})
})
if (filtersValue.refetchType === 'none') {
return Promise.resolve()
} else {
const refetchType = filtersValue.refetchType
return vue.nextTick(() =>
queryCore.notifyManager.batch(() => {
const refetchFilters: queryCore.RefetchQueryFilters = {
...filtersValue,
type: refetchType ?? filtersValue.type ?? 'active',
}
return this.refetchQueries(refetchFilters, optionsValue)
}),
)
}
}
}
/* eslint-disable */
function isPlainObject(value: unknown): value is Object {
if (Object.prototype.toString.call(value) !== '[object Object]') {
return false
}
const prototype = Object.getPrototypeOf(value)
return prototype === null || prototype === Object.prototype
}
function cloneDeep<T>(
value: MaybeRefDeep<T>,
customize?: (val: MaybeRefDeep<T>) => T | undefined,
): T {
if (customize) {
const result = customize(value)
// If it's a ref of undefined, return undefined
if (result === undefined && vue.isRef(value)) {
return result as T
}
if (result !== undefined) {
return result
}
}
if (Array.isArray(value)) {
return value.map(val => cloneDeep(val, customize)) as unknown as T
}
if (typeof value === 'object' && isPlainObject(value)) {
const entries = Object.entries(value).map(([key, val]) => [key, cloneDeep(val, customize)])
return Object.fromEntries(entries)
}
return value as T
}
function cloneDeepUnref<T>(obj: MaybeRefDeep<T>): T {
return cloneDeep(obj, val => {
if (vue.isRef(val)) {
return cloneDeepUnref(vue.unref(val))
}
return undefined
})
}
type MaybeRefDeep<T> = vue.MaybeRef<
T extends Function ? T
: T extends object ?
{
[Property in keyof T]: MaybeRefDeep<T[Property]>
}
: T
>
/* eslint-enable */

View File

@ -71,11 +71,11 @@ importers:
specifier: ^3.5.0 specifier: ^3.5.0
version: 3.5.0 version: 3.5.0
'@tanstack/react-query': '@tanstack/react-query':
specifier: 5.45.1 specifier: 5.55.0
version: 5.45.1(react@18.3.1) version: 5.55.0(react@18.3.1)
'@tanstack/vue-query': '@tanstack/vue-query':
specifier: '>= 5.45.0 < 5.46.0' specifier: '>= 5.54.0 < 5.56.0'
version: 5.45.0(vue@3.4.31(typescript@5.5.3)) version: 5.54.2(vue@3.4.31(typescript@5.5.3))
ajv: ajv:
specifier: ^8.12.0 specifier: ^8.12.0
version: 8.16.0 version: 8.16.0
@ -160,7 +160,7 @@ importers:
version: 3.23.1(react@18.3.1) version: 3.23.1(react@18.3.1)
'@tanstack/react-query-devtools': '@tanstack/react-query-devtools':
specifier: 5.45.1 specifier: 5.45.1
version: 5.45.1(@tanstack/react-query@5.45.1(react@18.3.1))(react@18.3.1) version: 5.45.1(@tanstack/react-query@5.55.0(react@18.3.1))(react@18.3.1)
'@types/eslint__js': '@types/eslint__js':
specifier: ^8.42.3 specifier: ^8.42.3
version: 8.42.3 version: 8.42.3
@ -315,8 +315,8 @@ importers:
specifier: ^1.4.0 specifier: ^1.4.0
version: 1.4.0 version: 1.4.0
'@tanstack/vue-query': '@tanstack/vue-query':
specifier: '>= 5.45.0 < 5.46.0' specifier: '>= 5.54.0 < 5.56.0'
version: 5.45.0(vue@3.4.31(typescript@5.5.3)) version: 5.54.2(vue@3.4.31(typescript@5.5.3))
'@vueuse/core': '@vueuse/core':
specifier: ^10.4.1 specifier: ^10.4.1
version: 10.11.0(vue@3.4.31(typescript@5.5.3)) version: 10.11.0(vue@3.4.31(typescript@5.5.3))
@ -640,6 +640,9 @@ importers:
'@types/yargs': '@types/yargs':
specifier: ^17.0.30 specifier: ^17.0.30
version: 17.0.32 version: 17.0.32
cross-env:
specifier: ^7.0.3
version: 7.0.3
electron: electron:
specifier: 31.2.0 specifier: 31.2.0
version: 31.2.0 version: 31.2.0
@ -680,14 +683,14 @@ importers:
app/ide-desktop/common: app/ide-desktop/common:
dependencies: dependencies:
'@tanstack/query-core': '@tanstack/query-core':
specifier: 5.45.0 specifier: 5.54.1
version: 5.45.0 version: 5.54.1
'@tanstack/query-persist-client-core': '@tanstack/query-persist-client-core':
specifier: ^5.45.0 specifier: ^5.54.0
version: 5.45.0 version: 5.54.1
'@tanstack/vue-query': '@tanstack/vue-query':
specifier: '>= 5.45.0 < 5.46.0' specifier: '>= 5.54.0 < 5.56.0'
version: 5.45.0(vue@3.4.31(typescript@5.5.3)) version: 5.54.2(vue@3.4.31(typescript@5.5.3))
idb-keyval: idb-keyval:
specifier: ^6.2.1 specifier: ^6.2.1
version: 6.2.1 version: 6.2.1
@ -1145,8 +1148,8 @@ packages:
resolution: {integrity: sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw==} resolution: {integrity: sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
'@babel/compat-data@7.25.2': '@babel/compat-data@7.25.4':
resolution: {integrity: sha512-bYcppcpKBvX4znYaPEeFau03bp89ShqNMLs+rmdptMw+heSZh9+z84d2YG+K7cYLbWwzdjtDoW/uqZmPjulClQ==} resolution: {integrity: sha512-+LGRog6RAsCJrrrg/IO6LGmpphNe5DiK30dGjCoxxeGv49B10/3XYGxPsAwrDlMFcFEvdAUavDT8r9k/hSyQqQ==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
'@babel/core@7.24.7': '@babel/core@7.24.7':
@ -1161,8 +1164,8 @@ packages:
resolution: {integrity: sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==} resolution: {integrity: sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
'@babel/generator@7.25.0': '@babel/generator@7.25.6':
resolution: {integrity: sha512-3LEEcj3PVW8pW2R1SR1M89g/qrYk/m/mB/tLqn7dn4sbBUQyTqnlod+II2U4dqiGtUmkcnAmkMDralTFZttRiw==} resolution: {integrity: sha512-VPC82gr1seXOpkjAAKoLhP50vx4vGNlF4msF64dSFq1P8RfB+QAuJWGHPXXPc8QyfVWwwB/TNNU4+ayZmHNbZw==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
'@babel/helper-annotate-as-pure@7.24.7': '@babel/helper-annotate-as-pure@7.24.7':
@ -1269,8 +1272,8 @@ packages:
resolution: {integrity: sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==} resolution: {integrity: sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
'@babel/helpers@7.25.0': '@babel/helpers@7.25.6':
resolution: {integrity: sha512-MjgLZ42aCm0oGjJj8CtSM3DB8NOOf8h2l7DCTePJs29u+v7yO/RBX9nShlKMgFnRks/Q4tBAe7Hxnov9VkGwLw==} resolution: {integrity: sha512-Xg0tn4HcfTijTwfDwYlvVCl43V6h4KyVVX2aEm4qdO/PC6L2YvzLHFdmxhoeSA3eslcE6+ZVXHgWwopXYLNq4Q==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
'@babel/highlight@7.24.7': '@babel/highlight@7.24.7':
@ -1282,8 +1285,8 @@ packages:
engines: {node: '>=6.0.0'} engines: {node: '>=6.0.0'}
hasBin: true hasBin: true
'@babel/parser@7.25.3': '@babel/parser@7.25.6':
resolution: {integrity: sha512-iLTJKDbJ4hMvFPgQwwsVoxtHyWpKKPBrxkANrSYewDPaPpT5py5yeVkgPIJ7XYXhndxJpaA3PyALSXQ7u8e/Dw==} resolution: {integrity: sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==}
engines: {node: '>=6.0.0'} engines: {node: '>=6.0.0'}
hasBin: true hasBin: true
@ -1356,16 +1359,16 @@ packages:
resolution: {integrity: sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==} resolution: {integrity: sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
'@babel/traverse@7.25.3': '@babel/traverse@7.25.6':
resolution: {integrity: sha512-HefgyP1x754oGCsKmV5reSmtV7IXj/kpaE1XYY+D9G5PvKKoFfSbiS4M77MdjuwlZKDIKFCffq9rPU+H/s3ZdQ==} resolution: {integrity: sha512-9Vrcx5ZW6UwK5tvqsj0nGpp/XzqthkT0dqIc9g1AdtygFToNtTF67XzYS//dm+SAK9cp3B9R4ZO/46p63SCjlQ==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
'@babel/types@7.24.7': '@babel/types@7.24.7':
resolution: {integrity: sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==} resolution: {integrity: sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
'@babel/types@7.25.2': '@babel/types@7.25.6':
resolution: {integrity: sha512-YTnYtra7W9e6/oAZEHj0bJehPRUlLH9/fbpT5LfB0NhQXyALCRkRs3zH9v07IYhkgpqX6Z78FnuccZr/l4Fs4Q==} resolution: {integrity: sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
'@bcoe/v8-coverage@0.2.3': '@bcoe/v8-coverage@0.2.3':
@ -2913,14 +2916,14 @@ packages:
resolution: {integrity: sha512-PnVV3d2poenUM31ZbZi/yXkBu3J7kd5k2u51CGwwNojag451AjTH9N6n41yjXz2fpLeewleyLBmNS6+HcGDlXw==} resolution: {integrity: sha512-PnVV3d2poenUM31ZbZi/yXkBu3J7kd5k2u51CGwwNojag451AjTH9N6n41yjXz2fpLeewleyLBmNS6+HcGDlXw==}
engines: {node: '>=12'} engines: {node: '>=12'}
'@tanstack/query-core@5.45.0': '@tanstack/query-core@5.54.1':
resolution: {integrity: sha512-RVfIZQmFUTdjhSAAblvueimfngYyfN6HlwaJUPK71PKd7yi43Vs1S/rdimmZedPWX/WGppcq/U1HOj7O7FwYxw==} resolution: {integrity: sha512-hKS+WRpT5zBFip21pB6Jx1C0hranWQrbv5EJ7qPoiV5MYI3C8rTCqWC9DdBseiPT1JgQWh8Y55YthuYZNiw3Xw==}
'@tanstack/query-devtools@5.37.1': '@tanstack/query-devtools@5.37.1':
resolution: {integrity: sha512-XcG4IIHIv0YQKrexTqo2zogQWR1Sz672tX2KsfE9kzB+9zhx44vRKH5si4WDILE1PIWQpStFs/NnrDQrBAUQpg==} resolution: {integrity: sha512-XcG4IIHIv0YQKrexTqo2zogQWR1Sz672tX2KsfE9kzB+9zhx44vRKH5si4WDILE1PIWQpStFs/NnrDQrBAUQpg==}
'@tanstack/query-persist-client-core@5.45.0': '@tanstack/query-persist-client-core@5.54.1':
resolution: {integrity: sha512-iAETglkRB6FR1//185TcWGnMxkisJPnCrlvIhLpHzT4eKLmh4n4vq1Cys42YIJzM2il4YWf8P4refzdq4D5Vdw==} resolution: {integrity: sha512-qmBkrC5HA3XHwwrx/pWjegncQFcmuoAaffwbrXm07OrsOxxeTGLt8aFl8RYbWAs75a8+9uneqVFQfgv5QRqxBA==}
'@tanstack/react-query-devtools@5.45.1': '@tanstack/react-query-devtools@5.45.1':
resolution: {integrity: sha512-4mrbk1g5jqlqh0pifZNsKzy7FtgeqgwzMICL4d6IJGayrrcrKq9K4N/OzRNbgRWrTn6YTY63qcAcKo+NJU2QMw==} resolution: {integrity: sha512-4mrbk1g5jqlqh0pifZNsKzy7FtgeqgwzMICL4d6IJGayrrcrKq9K4N/OzRNbgRWrTn6YTY63qcAcKo+NJU2QMw==}
@ -2928,13 +2931,13 @@ packages:
'@tanstack/react-query': ^5.45.1 '@tanstack/react-query': ^5.45.1
react: ^18 || ^19 react: ^18 || ^19
'@tanstack/react-query@5.45.1': '@tanstack/react-query@5.55.0':
resolution: {integrity: sha512-mYYfJujKg2kxmkRRjA6nn4YKG3ITsKuH22f1kteJ5IuVQqgKUgbaSQfYwVP0gBS05mhwxO03HVpD0t7BMN7WOA==} resolution: {integrity: sha512-2uYuxEbRQD8TORUiTUacEOwt1e8aoSqUOJFGY5TUrh6rQ3U85zrMS2wvbNhBhXGh6Vj69QDCP2yv8tIY7joo6Q==}
peerDependencies: peerDependencies:
react: ^18.0.0 react: ^18 || ^19
'@tanstack/vue-query@5.45.0': '@tanstack/vue-query@5.54.2':
resolution: {integrity: sha512-WogAH4+xDPWbiK9CUXAE4cQiCyvWeYZI3g3/onKbkb3tVnoEPRhbGHANgxpfAEFY165Vj4afKnI3hkVQvr7aHA==} resolution: {integrity: sha512-GYIYee9WkUbPDD28t1kdNNtLCioiIva0MhKCvODGWoEML5MNONCX4/i4y2GGFi8i9nSbcA8MpvD+nt/tdZ+yJw==}
peerDependencies: peerDependencies:
'@vue/composition-api': ^1.1.2 '@vue/composition-api': ^1.1.2
vue: ^2.6.0 || ^3.3.0 vue: ^2.6.0 || ^3.3.0
@ -3781,8 +3784,8 @@ packages:
caniuse-lite@1.0.30001639: caniuse-lite@1.0.30001639:
resolution: {integrity: sha512-eFHflNTBIlFwP2AIKaYuBQN/apnUoKNhBdza8ZnW/h2di4LCZ4xFqYlxUxo+LQ76KFI1PGcC1QDxMbxTZpSCAg==} resolution: {integrity: sha512-eFHflNTBIlFwP2AIKaYuBQN/apnUoKNhBdza8ZnW/h2di4LCZ4xFqYlxUxo+LQ76KFI1PGcC1QDxMbxTZpSCAg==}
caniuse-lite@1.0.30001651: caniuse-lite@1.0.30001658:
resolution: {integrity: sha512-9Cf+Xv1jJNe1xPZLGuUXLNkE1BoDkqRqYyFJ9TDYSqhduqA4hu4oR9HluGoWYQC/aj8WHjsGVV+bwkh0+tegRg==} resolution: {integrity: sha512-N2YVqWbJELVdrnsW5p+apoQyYt51aBMSsBZki1XZEfeBCexcM/sf4xiAHcXQBkuOwJBXtWF7aW1sYX6tKebPHw==}
capital-case@1.0.4: capital-case@1.0.4:
resolution: {integrity: sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==} resolution: {integrity: sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==}
@ -4200,6 +4203,15 @@ packages:
supports-color: supports-color:
optional: true optional: true
debug@4.3.7:
resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==}
engines: {node: '>=6.0'}
peerDependencies:
supports-color: '*'
peerDependenciesMeta:
supports-color:
optional: true
decimal.js@10.4.3: decimal.js@10.4.3:
resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==} resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==}
@ -4389,8 +4401,8 @@ packages:
electron-to-chromium@1.4.815: electron-to-chromium@1.4.815:
resolution: {integrity: sha512-OvpTT2ItpOXJL7IGcYakRjHCt8L5GrrN/wHCQsRB4PQa1X9fe+X9oen245mIId7s14xvArCGSTIq644yPUKKLg==} resolution: {integrity: sha512-OvpTT2ItpOXJL7IGcYakRjHCt8L5GrrN/wHCQsRB4PQa1X9fe+X9oen245mIId7s14xvArCGSTIq644yPUKKLg==}
electron-to-chromium@1.5.12: electron-to-chromium@1.5.16:
resolution: {integrity: sha512-tIhPkdlEoCL1Y+PToq3zRNehUaKp3wBX/sr7aclAWdIWjvqAe/Im/H0SiCM4c1Q8BLPHCdoJTol+ZblflydehA==} resolution: {integrity: sha512-2gQpi2WYobXmz2q23FrOBYTLcI1O/P4heW3eqX+ldmPVDQELRqhiebV380EhlGG12NtnX1qbK/FHpN0ba+7bLA==}
electron@31.2.0: electron@31.2.0:
resolution: {integrity: sha512-5w+kjOsGiTXytPSErBPNp/3znnuEMKc42RD41MqRoQkiYaR8x/Le2+qWk1cL60UwE/67oeKnOHnnol8xEuldGg==} resolution: {integrity: sha512-5w+kjOsGiTXytPSErBPNp/3znnuEMKc42RD41MqRoQkiYaR8x/Le2+qWk1cL60UwE/67oeKnOHnnol8xEuldGg==}
@ -4509,6 +4521,10 @@ packages:
resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==}
engines: {node: '>=6'} engines: {node: '>=6'}
escalade@3.2.0:
resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
engines: {node: '>=6'}
escape-html@1.0.3: escape-html@1.0.3:
resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==}
@ -5905,6 +5921,9 @@ packages:
ms@2.1.2: ms@2.1.2:
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
muggle-string@0.4.1: muggle-string@0.4.1:
resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==}
@ -6204,6 +6223,9 @@ packages:
picocolors@1.0.1: picocolors@1.0.1:
resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==}
picocolors@1.1.0:
resolution: {integrity: sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==}
picomatch@2.3.1: picomatch@2.3.1:
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
engines: {node: '>=8.6'} engines: {node: '>=8.6'}
@ -7472,6 +7494,17 @@ packages:
vue-component-type-helpers@2.0.29: vue-component-type-helpers@2.0.29:
resolution: {integrity: sha512-58i+ZhUAUpwQ+9h5Hck0D+jr1qbYl4voRt5KffBx8qzELViQ4XdT/Tuo+mzq8u63teAG8K0lLaOiL5ofqW38rg==} resolution: {integrity: sha512-58i+ZhUAUpwQ+9h5Hck0D+jr1qbYl4voRt5KffBx8qzELViQ4XdT/Tuo+mzq8u63teAG8K0lLaOiL5ofqW38rg==}
vue-demi@0.14.10:
resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==}
engines: {node: '>=12'}
hasBin: true
peerDependencies:
'@vue/composition-api': ^1.0.0-rc.1
vue: ^3.0.0-0 || ^2.6.0
peerDependenciesMeta:
'@vue/composition-api':
optional: true
vue-demi@0.14.8: vue-demi@0.14.8:
resolution: {integrity: sha512-Uuqnk9YE9SsWeReYqK2alDI5YzciATE0r2SkA6iMAtuXvNTMNACJLJEXNXaEy94ECuBe4Sk6RzRU80kjdbIo1Q==} resolution: {integrity: sha512-Uuqnk9YE9SsWeReYqK2alDI5YzciATE0r2SkA6iMAtuXvNTMNACJLJEXNXaEy94ECuBe4Sk6RzRU80kjdbIo1Q==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -8128,7 +8161,7 @@ snapshots:
'@babel/compat-data@7.24.7': {} '@babel/compat-data@7.24.7': {}
'@babel/compat-data@7.25.2': {} '@babel/compat-data@7.25.4': {}
'@babel/core@7.24.7': '@babel/core@7.24.7':
dependencies: dependencies:
@ -8154,16 +8187,16 @@ snapshots:
dependencies: dependencies:
'@ampproject/remapping': 2.3.0 '@ampproject/remapping': 2.3.0
'@babel/code-frame': 7.24.7 '@babel/code-frame': 7.24.7
'@babel/generator': 7.25.0 '@babel/generator': 7.25.6
'@babel/helper-compilation-targets': 7.25.2 '@babel/helper-compilation-targets': 7.25.2
'@babel/helper-module-transforms': 7.25.2(@babel/core@7.25.2) '@babel/helper-module-transforms': 7.25.2(@babel/core@7.25.2)
'@babel/helpers': 7.25.0 '@babel/helpers': 7.25.6
'@babel/parser': 7.25.3 '@babel/parser': 7.25.6
'@babel/template': 7.25.0 '@babel/template': 7.25.0
'@babel/traverse': 7.25.3 '@babel/traverse': 7.25.6
'@babel/types': 7.25.2 '@babel/types': 7.25.6
convert-source-map: 2.0.0 convert-source-map: 2.0.0
debug: 4.3.6 debug: 4.3.7
gensync: 1.0.0-beta.2 gensync: 1.0.0-beta.2
json5: 2.2.3 json5: 2.2.3
semver: 6.3.1 semver: 6.3.1
@ -8177,9 +8210,9 @@ snapshots:
'@jridgewell/trace-mapping': 0.3.25 '@jridgewell/trace-mapping': 0.3.25
jsesc: 2.5.2 jsesc: 2.5.2
'@babel/generator@7.25.0': '@babel/generator@7.25.6':
dependencies: dependencies:
'@babel/types': 7.25.2 '@babel/types': 7.25.6
'@jridgewell/gen-mapping': 0.3.5 '@jridgewell/gen-mapping': 0.3.5
'@jridgewell/trace-mapping': 0.3.25 '@jridgewell/trace-mapping': 0.3.25
jsesc: 2.5.2 jsesc: 2.5.2
@ -8198,7 +8231,7 @@ snapshots:
'@babel/helper-compilation-targets@7.25.2': '@babel/helper-compilation-targets@7.25.2':
dependencies: dependencies:
'@babel/compat-data': 7.25.2 '@babel/compat-data': 7.25.4
'@babel/helper-validator-option': 7.24.8 '@babel/helper-validator-option': 7.24.8
browserslist: 4.23.3 browserslist: 4.23.3
lru-cache: 5.1.1 lru-cache: 5.1.1
@ -8267,7 +8300,7 @@ snapshots:
'@babel/helper-module-imports': 7.24.7 '@babel/helper-module-imports': 7.24.7
'@babel/helper-simple-access': 7.24.7 '@babel/helper-simple-access': 7.24.7
'@babel/helper-validator-identifier': 7.24.7 '@babel/helper-validator-identifier': 7.24.7
'@babel/traverse': 7.25.3 '@babel/traverse': 7.25.6
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@ -8319,10 +8352,10 @@ snapshots:
'@babel/template': 7.24.7 '@babel/template': 7.24.7
'@babel/types': 7.24.7 '@babel/types': 7.24.7
'@babel/helpers@7.25.0': '@babel/helpers@7.25.6':
dependencies: dependencies:
'@babel/template': 7.25.0 '@babel/template': 7.25.0
'@babel/types': 7.25.2 '@babel/types': 7.25.6
'@babel/highlight@7.24.7': '@babel/highlight@7.24.7':
dependencies: dependencies:
@ -8335,9 +8368,9 @@ snapshots:
dependencies: dependencies:
'@babel/types': 7.24.7 '@babel/types': 7.24.7
'@babel/parser@7.25.3': '@babel/parser@7.25.6':
dependencies: dependencies:
'@babel/types': 7.25.2 '@babel/types': 7.25.6
'@babel/plugin-proposal-decorators@7.24.7(@babel/core@7.24.7)': '@babel/plugin-proposal-decorators@7.24.7(@babel/core@7.24.7)':
dependencies: dependencies:
@ -8411,8 +8444,8 @@ snapshots:
'@babel/template@7.25.0': '@babel/template@7.25.0':
dependencies: dependencies:
'@babel/code-frame': 7.24.7 '@babel/code-frame': 7.24.7
'@babel/parser': 7.25.3 '@babel/parser': 7.25.6
'@babel/types': 7.25.2 '@babel/types': 7.25.6
'@babel/traverse@7.24.7': '@babel/traverse@7.24.7':
dependencies: dependencies:
@ -8429,14 +8462,14 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@babel/traverse@7.25.3': '@babel/traverse@7.25.6':
dependencies: dependencies:
'@babel/code-frame': 7.24.7 '@babel/code-frame': 7.24.7
'@babel/generator': 7.25.0 '@babel/generator': 7.25.6
'@babel/parser': 7.25.3 '@babel/parser': 7.25.6
'@babel/template': 7.25.0 '@babel/template': 7.25.0
'@babel/types': 7.25.2 '@babel/types': 7.25.6
debug: 4.3.6 debug: 4.3.7
globals: 11.12.0 globals: 11.12.0
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@ -8447,7 +8480,7 @@ snapshots:
'@babel/helper-validator-identifier': 7.24.7 '@babel/helper-validator-identifier': 7.24.7
to-fast-properties: 2.0.0 to-fast-properties: 2.0.0
'@babel/types@7.25.2': '@babel/types@7.25.6':
dependencies: dependencies:
'@babel/helper-string-parser': 7.24.8 '@babel/helper-string-parser': 7.24.8
'@babel/helper-validator-identifier': 7.24.7 '@babel/helper-validator-identifier': 7.24.7
@ -9025,10 +9058,10 @@ snapshots:
'@ianvs/prettier-plugin-sort-imports@4.3.0(prettier@3.3.2)': '@ianvs/prettier-plugin-sort-imports@4.3.0(prettier@3.3.2)':
dependencies: dependencies:
'@babel/core': 7.25.2 '@babel/core': 7.25.2
'@babel/generator': 7.25.0 '@babel/generator': 7.25.6
'@babel/parser': 7.25.3 '@babel/parser': 7.25.6
'@babel/traverse': 7.25.3 '@babel/traverse': 7.25.6
'@babel/types': 7.25.2 '@babel/types': 7.25.6
prettier: 3.3.2 prettier: 3.3.2
semver: 7.6.3 semver: 7.6.3
transitivePeerDependencies: transitivePeerDependencies:
@ -10423,32 +10456,32 @@ snapshots:
dependencies: dependencies:
remove-accents: 0.5.0 remove-accents: 0.5.0
'@tanstack/query-core@5.45.0': {} '@tanstack/query-core@5.54.1': {}
'@tanstack/query-devtools@5.37.1': {} '@tanstack/query-devtools@5.37.1': {}
'@tanstack/query-persist-client-core@5.45.0': '@tanstack/query-persist-client-core@5.54.1':
dependencies: dependencies:
'@tanstack/query-core': 5.45.0 '@tanstack/query-core': 5.54.1
'@tanstack/react-query-devtools@5.45.1(@tanstack/react-query@5.45.1(react@18.3.1))(react@18.3.1)': '@tanstack/react-query-devtools@5.45.1(@tanstack/react-query@5.55.0(react@18.3.1))(react@18.3.1)':
dependencies: dependencies:
'@tanstack/query-devtools': 5.37.1 '@tanstack/query-devtools': 5.37.1
'@tanstack/react-query': 5.45.1(react@18.3.1) '@tanstack/react-query': 5.55.0(react@18.3.1)
react: 18.3.1 react: 18.3.1
'@tanstack/react-query@5.45.1(react@18.3.1)': '@tanstack/react-query@5.55.0(react@18.3.1)':
dependencies: dependencies:
'@tanstack/query-core': 5.45.0 '@tanstack/query-core': 5.54.1
react: 18.3.1 react: 18.3.1
'@tanstack/vue-query@5.45.0(vue@3.4.31(typescript@5.5.3))': '@tanstack/vue-query@5.54.2(vue@3.4.31(typescript@5.5.3))':
dependencies: dependencies:
'@tanstack/match-sorter-utils': 8.15.1 '@tanstack/match-sorter-utils': 8.15.1
'@tanstack/query-core': 5.45.0 '@tanstack/query-core': 5.54.1
'@vue/devtools-api': 6.6.3 '@vue/devtools-api': 6.6.3
vue: 3.4.31(typescript@5.5.3) vue: 3.4.31(typescript@5.5.3)
vue-demi: 0.14.8(vue@3.4.31(typescript@5.5.3)) vue-demi: 0.14.10(vue@3.4.31(typescript@5.5.3))
'@tootallnate/once@2.0.0': {} '@tootallnate/once@2.0.0': {}
@ -11461,8 +11494,8 @@ snapshots:
browserslist@4.23.3: browserslist@4.23.3:
dependencies: dependencies:
caniuse-lite: 1.0.30001651 caniuse-lite: 1.0.30001658
electron-to-chromium: 1.5.12 electron-to-chromium: 1.5.16
node-releases: 2.0.18 node-releases: 2.0.18
update-browserslist-db: 1.1.0(browserslist@4.23.3) update-browserslist-db: 1.1.0(browserslist@4.23.3)
@ -11567,7 +11600,7 @@ snapshots:
caniuse-lite@1.0.30001639: {} caniuse-lite@1.0.30001639: {}
caniuse-lite@1.0.30001651: {} caniuse-lite@1.0.30001658: {}
capital-case@1.0.4: capital-case@1.0.4:
dependencies: dependencies:
@ -12029,6 +12062,10 @@ snapshots:
dependencies: dependencies:
ms: 2.1.2 ms: 2.1.2
debug@4.3.7:
dependencies:
ms: 2.1.3
decimal.js@10.4.3: {} decimal.js@10.4.3: {}
decompress-response@6.0.0: decompress-response@6.0.0:
@ -12258,7 +12295,7 @@ snapshots:
electron-to-chromium@1.4.815: {} electron-to-chromium@1.4.815: {}
electron-to-chromium@1.5.12: {} electron-to-chromium@1.5.16: {}
electron@31.2.0: electron@31.2.0:
dependencies: dependencies:
@ -12499,6 +12536,8 @@ snapshots:
escalade@3.1.2: {} escalade@3.1.2: {}
escalade@3.2.0: {}
escape-html@1.0.3: {} escape-html@1.0.3: {}
escape-string-regexp@1.0.5: {} escape-string-regexp@1.0.5: {}
@ -14017,6 +14056,8 @@ snapshots:
ms@2.1.2: {} ms@2.1.2: {}
ms@2.1.3: {}
muggle-string@0.4.1: {} muggle-string@0.4.1: {}
murmurhash@2.0.1: {} murmurhash@2.0.1: {}
@ -14309,6 +14350,8 @@ snapshots:
picocolors@1.0.1: {} picocolors@1.0.1: {}
picocolors@1.1.0: {}
picomatch@2.3.1: {} picomatch@2.3.1: {}
pidtree@0.3.1: {} pidtree@0.3.1: {}
@ -15519,8 +15562,8 @@ snapshots:
update-browserslist-db@1.1.0(browserslist@4.23.3): update-browserslist-db@1.1.0(browserslist@4.23.3):
dependencies: dependencies:
browserslist: 4.23.3 browserslist: 4.23.3
escalade: 3.1.2 escalade: 3.2.0
picocolors: 1.0.1 picocolors: 1.1.0
upper-case-first@2.0.2: upper-case-first@2.0.2:
dependencies: dependencies:
@ -15751,6 +15794,10 @@ snapshots:
vue-component-type-helpers@2.0.29: {} vue-component-type-helpers@2.0.29: {}
vue-demi@0.14.10(vue@3.4.31(typescript@5.5.3)):
dependencies:
vue: 3.4.31(typescript@5.5.3)
vue-demi@0.14.8(vue@3.4.31(typescript@5.5.3)): vue-demi@0.14.8(vue@3.4.31(typescript@5.5.3)):
dependencies: dependencies:
vue: 3.4.31(typescript@5.5.3) vue: 3.4.31(typescript@5.5.3)