)
}
diff --git a/app/ide-desktop/lib/dashboard/src/components/dashboard/ProjectNameColumn.tsx b/app/ide-desktop/lib/dashboard/src/components/dashboard/ProjectNameColumn.tsx
index c4b39fd41761..ed40bb6723f4 100644
--- a/app/ide-desktop/lib/dashboard/src/components/dashboard/ProjectNameColumn.tsx
+++ b/app/ide-desktop/lib/dashboard/src/components/dashboard/ProjectNameColumn.tsx
@@ -9,7 +9,7 @@ import * as toastAndLogHooks from '#/hooks/toastAndLogHooks'
import * as authProvider from '#/providers/AuthProvider'
import * as backendProvider from '#/providers/BackendProvider'
-import * as shortcutManagerProvider from '#/providers/ShortcutManagerProvider'
+import * as inputBindingsProvider from '#/providers/InputBindingsProvider'
import AssetEventType from '#/events/AssetEventType'
import AssetListEventType from '#/events/AssetListEventType'
@@ -25,7 +25,6 @@ import * as eventModule from '#/utilities/event'
import * as indent from '#/utilities/indent'
import * as object from '#/utilities/object'
import * as permissions from '#/utilities/permissions'
-import * as shortcutManagerModule from '#/utilities/ShortcutManager'
import * as string from '#/utilities/string'
import * as validation from '#/utilities/validation'
import Visibility from '#/utilities/visibility'
@@ -47,7 +46,7 @@ export default function ProjectNameColumn(props: ProjectNameColumnProps) {
const toastAndLog = toastAndLogHooks.useToastAndLog()
const { backend } = backendProvider.useBackend()
const { user } = authProvider.useNonPartialUserSession()
- const { shortcutManager } = shortcutManagerProvider.useShortcutManager()
+ const inputBindings = inputBindingsProvider.useInputBindings()
const asset = item.item
if (asset.type !== backendModule.AssetType.project) {
// eslint-disable-next-line no-restricted-syntax
@@ -241,6 +240,28 @@ export default function ProjectNameColumn(props: ProjectNameColumnProps) {
}
})
+ const handleClick = inputBindings.handler({
+ open: () => {
+ dispatchAssetEvent({
+ type: AssetEventType.openProject,
+ id: asset.id,
+ shouldAutomaticallySwitchPage: true,
+ runInBackground: false,
+ })
+ },
+ run: () => {
+ dispatchAssetEvent({
+ type: AssetEventType.openProject,
+ id: asset.id,
+ shouldAutomaticallySwitchPage: false,
+ runInBackground: true,
+ })
+ },
+ editName: () => {
+ setRowState(object.merger({ isEditingName: true }))
+ },
+ })
+
return (
{
if (rowState.isEditingName || isOtherUserUsingProject) {
// The project should neither be edited nor opened in these cases.
- } else if (
- shortcutManager.matchesMouseAction(shortcutManagerModule.MouseAction.open, event)
- ) {
- // It is a double click; open the project.
- dispatchAssetEvent({
- type: AssetEventType.openProject,
- id: asset.id,
- shouldAutomaticallySwitchPage: true,
- runInBackground: false,
- })
- } else if (
- shortcutManager.matchesMouseAction(shortcutManagerModule.MouseAction.run, event)
- ) {
- dispatchAssetEvent({
- type: AssetEventType.openProject,
- id: asset.id,
- shouldAutomaticallySwitchPage: false,
- runInBackground: true,
- })
+ } else if (handleClick(event)) {
+ // Already handled.
} else if (
!isRunning &&
eventModule.isSingleClick(event) &&
- ((selected && selectedKeys.current.size === 1) ||
- shortcutManager.matchesMouseAction(shortcutManagerModule.MouseAction.editName, event))
+ selected &&
+ selectedKeys.current.size === 1
) {
setRowState(object.merger({ isEditingName: true }))
}
diff --git a/app/ide-desktop/lib/dashboard/src/components/dashboard/SecretNameColumn.tsx b/app/ide-desktop/lib/dashboard/src/components/dashboard/SecretNameColumn.tsx
index 8998874fcb9d..d8dbc70be74c 100644
--- a/app/ide-desktop/lib/dashboard/src/components/dashboard/SecretNameColumn.tsx
+++ b/app/ide-desktop/lib/dashboard/src/components/dashboard/SecretNameColumn.tsx
@@ -8,23 +8,22 @@ import * as setAssetHooks from '#/hooks/setAssetHooks'
import * as toastAndLogHooks from '#/hooks/toastAndLogHooks'
import * as backendProvider from '#/providers/BackendProvider'
+import * as inputBindingsProvider from '#/providers/InputBindingsProvider'
import * as modalProvider from '#/providers/ModalProvider'
-import * as shortcutManagerProvider from '#/providers/ShortcutManagerProvider'
import AssetEventType from '#/events/AssetEventType'
import AssetListEventType from '#/events/AssetListEventType'
-import UpsertSecretModal from '#/layouts/dashboard/UpsertSecretModal'
-
import type * as column from '#/components/dashboard/column'
import SvgMask from '#/components/SvgMask'
+import UpsertSecretModal from '#/modals/UpsertSecretModal'
+
import * as backendModule from '#/services/Backend'
import * as eventModule from '#/utilities/event'
import * as indent from '#/utilities/indent'
import * as object from '#/utilities/object'
-import * as shortcutManagerModule from '#/utilities/ShortcutManager'
import Visibility from '#/utilities/visibility'
// =====================
@@ -43,7 +42,7 @@ export default function SecretNameColumn(props: SecretNameColumnProps) {
const toastAndLog = toastAndLogHooks.useToastAndLog()
const { setModal } = modalProvider.useSetModal()
const { backend } = backendProvider.useBackend()
- const { shortcutManager } = shortcutManagerProvider.useShortcutManager()
+ const inputBindings = inputBindingsProvider.useInputBindings()
const asset = item.item
if (asset.type !== backendModule.AssetType.secret) {
// eslint-disable-next-line no-restricted-syntax
@@ -107,6 +106,12 @@ export default function SecretNameColumn(props: SecretNameColumnProps) {
}
})
+ const handleClick = inputBindings.handler({
+ editName: () => {
+ setRowState(object.merger({ isEditingName: true }))
+ },
+ })
+
return (
{
- if (
- eventModule.isSingleClick(event) &&
- (selected ||
- shortcutManager.matchesMouseAction(shortcutManagerModule.MouseAction.editName, event))
- ) {
+ if (handleClick(event)) {
+ // Already handled.
+ } else if (eventModule.isSingleClick(event) && selected) {
setRowState(object.merger({ isEditingName: true }))
} else if (eventModule.isDoubleClick(event)) {
event.stopPropagation()
diff --git a/app/ide-desktop/lib/dashboard/src/components/dashboard/column.ts b/app/ide-desktop/lib/dashboard/src/components/dashboard/column.ts
index 59ee46de6909..0d107e79c2d5 100644
--- a/app/ide-desktop/lib/dashboard/src/components/dashboard/column.ts
+++ b/app/ide-desktop/lib/dashboard/src/components/dashboard/column.ts
@@ -1,5 +1,5 @@
/** @file Column types and column display modes. */
-import type * as assetsTable from '#/layouts/dashboard/AssetsTable'
+import type * as assetsTable from '#/layouts/AssetsTable'
import * as columnUtils from '#/components/dashboard/column/columnUtils'
import DocsColumn from '#/components/dashboard/column/DocsColumn'
diff --git a/app/ide-desktop/lib/dashboard/src/components/dashboard/column/LabelsColumn.tsx b/app/ide-desktop/lib/dashboard/src/components/dashboard/column/LabelsColumn.tsx
index 1722c6254432..e61e2aedf0bd 100644
--- a/app/ide-desktop/lib/dashboard/src/components/dashboard/column/LabelsColumn.tsx
+++ b/app/ide-desktop/lib/dashboard/src/components/dashboard/column/LabelsColumn.tsx
@@ -9,8 +9,7 @@ import * as authProvider from '#/providers/AuthProvider'
import * as backendProvider from '#/providers/BackendProvider'
import * as modalProvider from '#/providers/ModalProvider'
-import Category from '#/layouts/dashboard/CategorySwitcher/Category'
-import ManageLabelsModal from '#/layouts/dashboard/ManageLabelsModal'
+import Category from '#/layouts/CategorySwitcher/Category'
import ContextMenu from '#/components/ContextMenu'
import ContextMenus from '#/components/ContextMenus'
@@ -19,12 +18,13 @@ import Label from '#/components/dashboard/Label'
import * as labelUtils from '#/components/dashboard/Label/labelUtils'
import MenuEntry from '#/components/MenuEntry'
+import ManageLabelsModal from '#/modals/ManageLabelsModal'
+
import type * as backendModule from '#/services/Backend'
import * as assetQuery from '#/utilities/AssetQuery'
import * as object from '#/utilities/object'
import * as permissions from '#/utilities/permissions'
-import * as shortcutManager from '#/utilities/ShortcutManager'
import * as uniqueString from '#/utilities/uniqueString'
// ====================
@@ -100,7 +100,7 @@ export default function LabelsColumn(props: column.AssetColumnProps) {
setModal(
-
+
)
diff --git a/app/ide-desktop/lib/dashboard/src/components/dashboard/column/SharedWithColumn.tsx b/app/ide-desktop/lib/dashboard/src/components/dashboard/column/SharedWithColumn.tsx
index 2849324aaba2..e752d9fabb43 100644
--- a/app/ide-desktop/lib/dashboard/src/components/dashboard/column/SharedWithColumn.tsx
+++ b/app/ide-desktop/lib/dashboard/src/components/dashboard/column/SharedWithColumn.tsx
@@ -8,12 +8,13 @@ import * as modalProvider from '#/providers/ModalProvider'
import AssetEventType from '#/events/AssetEventType'
-import Category from '#/layouts/dashboard/CategorySwitcher/Category'
-import ManagePermissionsModal from '#/layouts/dashboard/ManagePermissionsModal'
+import Category from '#/layouts/CategorySwitcher/Category'
import type * as column from '#/components/dashboard/column'
import PermissionDisplay from '#/components/dashboard/PermissionDisplay'
+import ManagePermissionsModal from '#/modals/ManagePermissionsModal'
+
import type * as backendModule from '#/services/Backend'
import * as object from '#/utilities/object'
diff --git a/app/ide-desktop/lib/dashboard/src/configurations/inputBindings.ts b/app/ide-desktop/lib/dashboard/src/configurations/inputBindings.ts
new file mode 100644
index 000000000000..156874ac0535
--- /dev/null
+++ b/app/ide-desktop/lib/dashboard/src/configurations/inputBindings.ts
@@ -0,0 +1,104 @@
+/** @file Shortcuts for the dashboard application. */
+
+import AddConnectorIcon from 'enso-assets/add_connector.svg'
+import AddFolderIcon from 'enso-assets/add_folder.svg'
+import AddKeyIcon from 'enso-assets/add_key.svg'
+import AddNetworkIcon from 'enso-assets/add_network.svg'
+import AppDownloadIcon from 'enso-assets/app_download.svg'
+import CameraIcon from 'enso-assets/camera.svg'
+import CloseIcon from 'enso-assets/close.svg'
+import CloudToIcon from 'enso-assets/cloud_to.svg'
+import CopyIcon from 'enso-assets/copy.svg'
+import DataDownloadIcon from 'enso-assets/data_download.svg'
+import DataUploadIcon from 'enso-assets/data_upload.svg'
+import DuplicateIcon from 'enso-assets/duplicate.svg'
+import OpenIcon from 'enso-assets/open.svg'
+import PasteIcon from 'enso-assets/paste.svg'
+import PenIcon from 'enso-assets/pen.svg'
+import PeopleIcon from 'enso-assets/people.svg'
+import Play2Icon from 'enso-assets/play2.svg'
+import ScissorsIcon from 'enso-assets/scissors.svg'
+import SettingsIcon from 'enso-assets/settings.svg'
+import SignInIcon from 'enso-assets/sign_in.svg'
+import SignOutIcon from 'enso-assets/sign_out.svg'
+import TagIcon from 'enso-assets/tag.svg'
+import TrashIcon from 'enso-assets/trash.svg'
+import UntrashIcon from 'enso-assets/untrash.svg'
+import * as detect from 'enso-common/src/detect'
+
+import * as inputBindings from '#/utilities/inputBindings'
+
+export type * from '#/utilities/inputBindings'
+
+// ======================
+// === Input bindings ===
+// ======================
+
+/** The type of the keybind and mousebind namespace for the dashboard. */
+export interface DashboardBindingNamespace extends ReturnType {}
+
+/** The nameof a dashboard binding */
+export type DashboardBindingKey = keyof typeof BINDINGS
+
+/** Create a keybind and mousebind namespace. */
+export function createBindings() {
+ return inputBindings.defineBindingNamespace('dashboard', BINDINGS)
+}
+
+export const BINDINGS = inputBindings.defineBindings({
+ settings: { name: 'Settings', bindings: ['Mod+,'], icon: SettingsIcon },
+ open: { name: 'Open', bindings: ['Enter'], icon: OpenIcon },
+ run: { name: 'Run', bindings: ['Shift+Enter'], icon: Play2Icon },
+ close: { name: 'Close', bindings: [], icon: CloseIcon },
+ uploadToCloud: { name: 'Upload To Cloud', bindings: [], icon: CloudToIcon },
+ rename: { name: 'Rename', bindings: ['Mod+R'], icon: PenIcon },
+ edit: { name: 'Edit', bindings: ['Mod+E'], icon: PenIcon },
+ snapshot: { name: 'Snapshot', bindings: ['Mod+S'], icon: CameraIcon },
+ delete: {
+ name: 'Delete',
+ bindings: ['OsDelete'],
+ icon: TrashIcon,
+ color: 'rgb(243 24 10 / 0.87)',
+ },
+ undelete: { name: 'Undelete', bindings: ['Mod+R'], icon: UntrashIcon },
+ share: { name: 'Share', bindings: ['Mod+Enter'], icon: PeopleIcon },
+ label: { name: 'Label', bindings: ['Mod+L'], icon: TagIcon },
+ duplicate: { name: 'Duplicate', bindings: ['Mod+D'], icon: DuplicateIcon },
+ copy: { name: 'Copy', bindings: ['Mod+C'], icon: CopyIcon },
+ cut: { name: 'Cut', bindings: ['Mod+X'], icon: ScissorsIcon },
+ paste: { name: 'Paste', bindings: ['Mod+V'], icon: PasteIcon },
+ download: { name: 'Download', bindings: ['Mod+Shift+S'], icon: DataDownloadIcon },
+ uploadFiles: { name: 'Upload Files', bindings: ['Mod+U'], icon: DataUploadIcon },
+ uploadProjects: { name: 'Upload Projects', bindings: ['Mod+U'], icon: DataUploadIcon },
+ newProject: { name: 'New Project', bindings: ['Mod+N'], icon: AddNetworkIcon },
+ newFolder: { name: 'New Folder', bindings: ['Mod+Shift+N'], icon: AddFolderIcon },
+ // FIXME [sb]: Platform detection should be handled directly in `shortcuts.ts`.
+ newSecret: {
+ name: 'New Secret',
+ bindings: !detect.isOnMacOS() ? ['Mod+Alt+N'] : ['Mod+Alt+N', 'Mod+Alt+~'],
+ icon: AddKeyIcon,
+ },
+ newDataLink: {
+ name: 'New Data Link',
+ bindings: !detect.isOnMacOS() ? ['Mod+Alt+Shift+N'] : ['Mod+Alt+Shift+N', 'Mod+Alt+Shift+~'],
+ icon: AddConnectorIcon,
+ },
+ signIn: { name: 'Login', bindings: [], icon: SignInIcon },
+ signOut: { name: 'Logout', bindings: [], icon: SignOutIcon, color: 'rgb(243 24 10 / 0.87)' },
+ // These should not appear in any menus.
+ closeModal: { name: 'Close Modal', bindings: ['Escape'], rebindable: false },
+ cancelEditName: { name: 'Cancel Editing', bindings: ['Escape'], rebindable: false },
+ downloadApp: { name: 'Download App', bindings: [], icon: AppDownloadIcon, rebindable: false },
+ cancelCut: { name: 'Cancel Cut', bindings: ['Escape'], rebindable: false },
+ // TODO: support handlers for double click; make single click handlers not work on double click events
+ // [MouseAction.open]: [mousebind(MouseAction.open, [], MouseButton.left, 2)],
+ // [MouseAction.run]: [mousebind(MouseAction.run, ['Shift'], MouseButton.left, 2)],
+ editName: { name: 'Edit Name', bindings: ['Mod+PointerMain'], rebindable: false },
+ selectAdditional: { name: 'Select Additional', bindings: ['Mod+PointerMain'], rebindable: false },
+ selectRange: { name: 'Select Range', bindings: ['Shift+PointerMain'], rebindable: false },
+ selectAdditionalRange: {
+ name: 'Select Additional Range',
+ bindings: ['Mod+Shift+PointerMain'],
+ rebindable: false,
+ },
+})
diff --git a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/AssetContextMenu.tsx b/app/ide-desktop/lib/dashboard/src/layouts/AssetContextMenu.tsx
similarity index 87%
rename from app/ide-desktop/lib/dashboard/src/layouts/dashboard/AssetContextMenu.tsx
rename to app/ide-desktop/lib/dashboard/src/layouts/AssetContextMenu.tsx
index 37e434b69662..975e6c0b72b7 100644
--- a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/AssetContextMenu.tsx
+++ b/app/ide-desktop/lib/dashboard/src/layouts/AssetContextMenu.tsx
@@ -13,26 +13,26 @@ import * as modalProvider from '#/providers/ModalProvider'
import AssetEventType from '#/events/AssetEventType'
import AssetListEventType from '#/events/AssetListEventType'
-import Category from '#/layouts/dashboard/CategorySwitcher/Category'
-import GlobalContextMenu from '#/layouts/dashboard/GlobalContextMenu'
-import ManageLabelsModal from '#/layouts/dashboard/ManageLabelsModal'
-import ManagePermissionsModal from '#/layouts/dashboard/ManagePermissionsModal'
-import UpsertSecretModal from '#/layouts/dashboard/UpsertSecretModal'
+import Category from '#/layouts/CategorySwitcher/Category'
+import GlobalContextMenu from '#/layouts/GlobalContextMenu'
import ContextMenu from '#/components/ContextMenu'
import ContextMenus from '#/components/ContextMenus'
import ContextMenuSeparator from '#/components/ContextMenuSeparator'
import type * as assetRow from '#/components/dashboard/AssetRow'
-import ConfirmDeleteModal from '#/components/dashboard/ConfirmDeleteModal'
import MenuEntry from '#/components/MenuEntry'
+import ConfirmDeleteModal from '#/modals/ConfirmDeleteModal'
+import ManageLabelsModal from '#/modals/ManageLabelsModal'
+import ManagePermissionsModal from '#/modals/ManagePermissionsModal'
+import UpsertSecretModal from '#/modals/UpsertSecretModal'
+
import * as backendModule from '#/services/Backend'
import RemoteBackend from '#/services/RemoteBackend'
import HttpClient from '#/utilities/HttpClient'
import * as object from '#/utilities/object'
import * as permissions from '#/utilities/permissions'
-import * as shortcutManager from '#/utilities/ShortcutManager'
// ========================
// === AssetContextMenu ===
@@ -104,7 +104,8 @@ export default function AssetContextMenu(props: AssetContextMenuProps) {
{
unsetModal()
dispatchAssetEvent({
@@ -125,7 +126,7 @@ export default function AssetContextMenu(props: AssetContextMenuProps) {
!isOtherUserUsingProject && (
{
unsetModal()
dispatchAssetEvent({
@@ -140,7 +141,7 @@ export default function AssetContextMenu(props: AssetContextMenuProps) {
{asset.type === backendModule.AssetType.project && isCloud && (
{
unsetModal()
dispatchAssetEvent({
@@ -158,7 +159,7 @@ export default function AssetContextMenu(props: AssetContextMenuProps) {
!isOtherUserUsingProject && (
{
unsetModal()
dispatchAssetEvent({
@@ -171,7 +172,7 @@ export default function AssetContextMenu(props: AssetContextMenuProps) {
{asset.type === backendModule.AssetType.project && !isCloud && (
{
unsetModal()
if (accessToken == null) {
@@ -211,7 +212,7 @@ export default function AssetContextMenu(props: AssetContextMenuProps) {
asset.type !== backendModule.AssetType.project &&
asset.type !== backendModule.AssetType.directory
}
- action={shortcutManager.KeyboardAction.rename}
+ action="rename"
doAction={() => {
setRowState(object.merger({ isEditingName: true }))
unsetModal()
@@ -221,7 +222,7 @@ export default function AssetContextMenu(props: AssetContextMenuProps) {
{asset.type === backendModule.AssetType.secret && canEditThisAsset && (
{
setModal(
{
// No backend support yet.
}}
@@ -252,11 +253,8 @@ export default function AssetContextMenu(props: AssetContextMenuProps) {
{ownsThisAsset && !isRunningProject && !isOtherUserUsingProject && (
{
if (backend.type === backendModule.BackendType.remote) {
unsetModal()
@@ -264,7 +262,7 @@ export default function AssetContextMenu(props: AssetContextMenuProps) {
} else {
setModal(
)
@@ -276,7 +274,7 @@ export default function AssetContextMenu(props: AssetContextMenuProps) {
{isCloud && managesThisAsset && self != null && (
{
setModal(
{
setModal(
{
unsetModal()
dispatchAssetListEvent({
@@ -327,20 +325,14 @@ export default function AssetContextMenu(props: AssetContextMenuProps) {
})
}}
/>
- {isCloud && (
-
- )}
+ {isCloud && }
{isCloud && !isOtherUserUsingProject && (
-
+
)}
{
unsetModal()
dispatchAssetEvent({
@@ -352,7 +344,7 @@ export default function AssetContextMenu(props: AssetContextMenuProps) {
{hasPasteData && (
{
const [directoryKey, directoryId] =
item.item.type === backendModule.AssetType.directory
diff --git a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/AssetPanel.tsx b/app/ide-desktop/lib/dashboard/src/layouts/AssetPanel.tsx
similarity index 94%
rename from app/ide-desktop/lib/dashboard/src/layouts/dashboard/AssetPanel.tsx
rename to app/ide-desktop/lib/dashboard/src/layouts/AssetPanel.tsx
index 2658908980fc..3eee2ad87021 100644
--- a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/AssetPanel.tsx
+++ b/app/ide-desktop/lib/dashboard/src/layouts/AssetPanel.tsx
@@ -5,11 +5,11 @@ import * as localStorageProvider from '#/providers/LocalStorageProvider'
import type * as assetEvent from '#/events/assetEvent'
-import AssetProperties from '#/layouts/dashboard/AssetProperties'
-import AssetVersions from '#/layouts/dashboard/AssetVersions'
-import type Category from '#/layouts/dashboard/CategorySwitcher/Category'
-import type * as pageSwitcher from '#/layouts/dashboard/PageSwitcher'
-import UserBar from '#/layouts/dashboard/UserBar'
+import AssetProperties from '#/layouts/AssetProperties'
+import AssetVersions from '#/layouts/AssetVersions'
+import type Category from '#/layouts/CategorySwitcher/Category'
+import type * as pageSwitcher from '#/layouts/PageSwitcher'
+import UserBar from '#/layouts/UserBar'
import AssetInfoBar from '#/components/dashboard/AssetInfoBar'
diff --git a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/AssetProperties.tsx b/app/ide-desktop/lib/dashboard/src/layouts/AssetProperties.tsx
similarity index 98%
rename from app/ide-desktop/lib/dashboard/src/layouts/dashboard/AssetProperties.tsx
rename to app/ide-desktop/lib/dashboard/src/layouts/AssetProperties.tsx
index 257487ca45b6..6109a8c49a96 100644
--- a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/AssetProperties.tsx
+++ b/app/ide-desktop/lib/dashboard/src/layouts/AssetProperties.tsx
@@ -12,8 +12,8 @@ import * as backendProvider from '#/providers/BackendProvider'
import type * as assetEvent from '#/events/assetEvent'
-import type Category from '#/layouts/dashboard/CategorySwitcher/Category'
-import DataLinkInput from '#/layouts/dashboard/DataLinkInput'
+import type Category from '#/layouts/CategorySwitcher/Category'
+import DataLinkInput from '#/layouts/DataLinkInput'
import Button from '#/components/Button'
import SharedWithColumn from '#/components/dashboard/column/SharedWithColumn'
diff --git a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/AssetSearchBar.tsx b/app/ide-desktop/lib/dashboard/src/layouts/AssetSearchBar.tsx
similarity index 97%
rename from app/ide-desktop/lib/dashboard/src/layouts/dashboard/AssetSearchBar.tsx
rename to app/ide-desktop/lib/dashboard/src/layouts/AssetSearchBar.tsx
index 976e4884c3e3..223168fc4c0c 100644
--- a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/AssetSearchBar.tsx
+++ b/app/ide-desktop/lib/dashboard/src/layouts/AssetSearchBar.tsx
@@ -2,6 +2,7 @@
import * as React from 'react'
import FindIcon from 'enso-assets/find.svg'
+import * as detect from 'enso-common/src/detect'
import Label from '#/components/dashboard/Label'
@@ -9,7 +10,7 @@ import type * as backend from '#/services/Backend'
import * as array from '#/utilities/array'
import AssetQuery, * as assetQuery from '#/utilities/AssetQuery'
-import * as shortcutManager from '#/utilities/ShortcutManager'
+import * as eventModule from '#/utilities/event'
// =============
// === Types ===
@@ -169,15 +170,16 @@ export default function AssetSearchBar(props: AssetSearchBarProps) {
!(event.target instanceof HTMLTextAreaElement) &&
(!(event.target instanceof HTMLElement) || !event.target.isContentEditable) &&
(!(event.target instanceof Node) || rootRef.current?.contains(event.target) !== true) &&
- shortcutManager.isTextInputEvent(event) &&
- event.key !== ' '
+ eventModule.isTextInputEvent(event) &&
+ event.key !== ' ' &&
+ (!detect.isOnMacOS() || event.key !== 'Delete')
) {
searchRef.current?.focus()
}
if (
event.target instanceof Node &&
rootRef.current?.contains(event.target) === true &&
- shortcutManager.isPotentiallyShortcut(event)
+ eventModule.isPotentiallyShortcut(event)
) {
searchRef.current?.focus()
}
diff --git a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/AssetVersion.tsx b/app/ide-desktop/lib/dashboard/src/layouts/AssetVersion.tsx
similarity index 100%
rename from app/ide-desktop/lib/dashboard/src/layouts/dashboard/AssetVersion.tsx
rename to app/ide-desktop/lib/dashboard/src/layouts/AssetVersion.tsx
diff --git a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/AssetVersions.tsx b/app/ide-desktop/lib/dashboard/src/layouts/AssetVersions.tsx
similarity index 96%
rename from app/ide-desktop/lib/dashboard/src/layouts/dashboard/AssetVersions.tsx
rename to app/ide-desktop/lib/dashboard/src/layouts/AssetVersions.tsx
index 80851c1c967e..21d28d1fd09c 100644
--- a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/AssetVersions.tsx
+++ b/app/ide-desktop/lib/dashboard/src/layouts/AssetVersions.tsx
@@ -5,7 +5,7 @@ import * as toastAndLogHooks from '#/hooks/toastAndLogHooks'
import * as backendProvider from '#/providers/BackendProvider'
-import AssetVersion from '#/layouts/dashboard/AssetVersion'
+import AssetVersion from '#/layouts/AssetVersion'
import type * as backend from '#/services/Backend'
diff --git a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/AssetsTable.tsx b/app/ide-desktop/lib/dashboard/src/layouts/AssetsTable.tsx
similarity index 96%
rename from app/ide-desktop/lib/dashboard/src/layouts/dashboard/AssetsTable.tsx
rename to app/ide-desktop/lib/dashboard/src/layouts/AssetsTable.tsx
index 152a33e9bfe2..78e1a5b82e25 100644
--- a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/AssetsTable.tsx
+++ b/app/ide-desktop/lib/dashboard/src/layouts/AssetsTable.tsx
@@ -9,21 +9,19 @@ import * as toastAndLogHooks from '#/hooks/toastAndLogHooks'
import * as authProvider from '#/providers/AuthProvider'
import * as backendProvider from '#/providers/BackendProvider'
+import * as inputBindingsProvider from '#/providers/InputBindingsProvider'
import * as localStorageProvider from '#/providers/LocalStorageProvider'
import * as modalProvider from '#/providers/ModalProvider'
-import * as shortcutManagerProvider from '#/providers/ShortcutManagerProvider'
import type * as assetEvent from '#/events/assetEvent'
import AssetEventType from '#/events/AssetEventType'
import type * as assetListEvent from '#/events/assetListEvent'
import AssetListEventType from '#/events/AssetListEventType'
-import type * as assetPanel from '#/layouts/dashboard/AssetPanel'
-import type * as assetSearchBar from '#/layouts/dashboard/AssetSearchBar'
-import AssetsTableContextMenu from '#/layouts/dashboard/AssetsTableContextMenu'
-import Category from '#/layouts/dashboard/CategorySwitcher/Category'
-import DuplicateAssetsModal from '#/layouts/dashboard/DuplicateAssetsModal'
-import UpsertSecretModal from '#/layouts/dashboard/UpsertSecretModal'
+import type * as assetPanel from '#/layouts/AssetPanel'
+import type * as assetSearchBar from '#/layouts/AssetSearchBar'
+import AssetsTableContextMenu from '#/layouts/AssetsTableContextMenu'
+import Category from '#/layouts/CategorySwitcher/Category'
import Button from '#/components/Button'
import type * as assetRow from '#/components/dashboard/AssetRow'
@@ -33,10 +31,13 @@ import * as columnUtils from '#/components/dashboard/column/columnUtils'
import NameColumn from '#/components/dashboard/column/NameColumn'
import * as columnHeading from '#/components/dashboard/columnHeading'
import Label from '#/components/dashboard/Label'
-import DragModal from '#/components/DragModal'
import SelectionBrush from '#/components/SelectionBrush'
import Spinner, * as spinner from '#/components/Spinner'
+import DragModal from '#/modals/DragModal'
+import DuplicateAssetsModal from '#/modals/DuplicateAssetsModal'
+import UpsertSecretModal from '#/modals/UpsertSecretModal'
+
import * as backendModule from '#/services/Backend'
import * as array from '#/utilities/array'
@@ -47,12 +48,13 @@ import * as dateTime from '#/utilities/dateTime'
import * as drag from '#/utilities/drag'
import * as fileInfo from '#/utilities/fileInfo'
import type * as geometry from '#/utilities/geometry'
+import * as inputBindingsModule from '#/utilities/inputBindings'
import LocalStorage from '#/utilities/LocalStorage'
import type * as pasteDataModule from '#/utilities/pasteData'
import PasteType from '#/utilities/PasteType'
import * as permissions from '#/utilities/permissions'
+import * as sanitizedEventTargets from '#/utilities/sanitizedEventTargets'
import * as set from '#/utilities/set'
-import * as shortcutManagerModule from '#/utilities/ShortcutManager'
import SortDirection from '#/utilities/SortDirection'
import * as string from '#/utilities/string'
import * as uniqueString from '#/utilities/uniqueString'
@@ -378,7 +380,7 @@ export default function AssetsTable(props: AssetsTableProps) {
const { backend } = backendProvider.useBackend()
const { setModal, unsetModal } = modalProvider.useSetModal()
const { localStorage } = localStorageProvider.useLocalStorage()
- const { shortcutManager } = shortcutManagerProvider.useShortcutManager()
+ const inputBindings = inputBindingsProvider.useInputBindings()
const toastAndLog = toastAndLogHooks.useToastAndLog()
const [initialized, setInitialized] = React.useState(false)
const [isLoading, setIsLoading] = React.useState(true)
@@ -817,8 +819,8 @@ export default function AssetsTable(props: AssetsTableProps) {
}, [pasteData])
React.useEffect(() => {
- return shortcutManager.registerKeyboardHandlers({
- [shortcutManagerModule.KeyboardAction.cancelCut]: () => {
+ return inputBindings.attach(sanitizedEventTargets.document.body, 'keydown', {
+ cancelCut: () => {
if (pasteDataRef.current == null) {
return false
} else {
@@ -828,7 +830,7 @@ export default function AssetsTable(props: AssetsTableProps) {
}
},
})
- }, [/* should never change */ shortcutManager, /* should never change */ dispatchAssetEvent])
+ }, [/* should never change */ inputBindings, /* should never change */ dispatchAssetEvent])
React.useEffect(() => {
if (isLoading) {
@@ -1942,32 +1944,29 @@ export default function AssetsTable(props: AssetsTableProps) {
}
}, [/* should never change */ scrollContainerRef])
- React.useEffect(() => {
- const onDocumentClick = (event: MouseEvent) => {
- if (
- !shortcutManager.matchesMouseAction(
- shortcutManagerModule.MouseAction.selectAdditional,
- event
- ) &&
- !shortcutManager.matchesMouseAction(
- shortcutManagerModule.MouseAction.selectAdditionalRange,
- event
- ) &&
- selectedKeysRef.current.size !== 0
- ) {
- setSelectedKeys(new Set())
- setMostRecentlySelectedIndex(null)
- }
- }
- document.addEventListener('click', onDocumentClick)
- return () => {
- document.removeEventListener('click', onDocumentClick)
- }
- }, [
- shortcutManager,
- /* should never change */ setSelectedKeys,
- /* should never change */ setMostRecentlySelectedIndex,
- ])
+ React.useEffect(
+ () =>
+ inputBindings.attach(
+ sanitizedEventTargets.document.body,
+ 'click',
+ {
+ selectAdditional: () => {},
+ selectAdditionalRange: () => {},
+ [inputBindingsModule.DEFAULT_HANDLER]: () => {
+ if (selectedKeysRef.current.size !== 0) {
+ setSelectedKeys(new Set())
+ setMostRecentlySelectedIndex(null)
+ }
+ },
+ },
+ false
+ ),
+ [
+ setSelectedKeys,
+ /* should never change */ inputBindings,
+ /* should never change */ setMostRecentlySelectedIndex,
+ ]
+ )
React.useEffect(() => {
if (isLoading) {
@@ -1988,40 +1987,35 @@ export default function AssetsTable(props: AssetsTableProps) {
getRange: () => backendModule.AssetId[]
) => {
event.stopPropagation()
- if (
- shortcutManager.matchesMouseAction(shortcutManagerModule.MouseAction.selectRange, event)
- ) {
- return new Set(getRange())
- } else if (
- shortcutManager.matchesMouseAction(
- shortcutManagerModule.MouseAction.selectAdditionalRange,
- event
- )
- ) {
- return new Set([...selectedKeysRef.current, ...getRange()])
- } else if (
- shortcutManager.matchesMouseAction(
- shortcutManagerModule.MouseAction.selectAdditional,
- event
- )
- ) {
- const newSelectedKeys = new Set(selectedKeysRef.current)
- let count = 0
- for (const key of keys) {
- if (selectedKeysRef.current.has(key)) {
- count += 1
+ let result = new Set()
+ inputBindings.handler({
+ selectRange: () => {
+ result = new Set(getRange())
+ },
+ selectAdditionalRange: () => {
+ result = new Set([...selectedKeysRef.current, ...getRange()])
+ },
+ selectAdditional: () => {
+ const newSelectedKeys = new Set(selectedKeysRef.current)
+ let count = 0
+ for (const key of keys) {
+ if (selectedKeysRef.current.has(key)) {
+ count += 1
+ }
}
- }
- for (const key of keys) {
- const add = count * 2 < keys.length
- set.setPresence(newSelectedKeys, key, add)
- }
- return newSelectedKeys
- } else {
- return new Set(keys)
- }
+ for (const key of keys) {
+ const add = count * 2 < keys.length
+ set.setPresence(newSelectedKeys, key, add)
+ }
+ result = newSelectedKeys
+ },
+ [inputBindingsModule.DEFAULT_HANDLER]: () => {
+ result = new Set(keys)
+ },
+ })(event, false)
+ return result
},
- [shortcutManager]
+ [/* should never change */ inputBindings]
)
// Only non-`null` when it is different to`selectedKeys`.
@@ -2153,16 +2147,13 @@ export default function AssetsTable(props: AssetsTableProps) {
const columns = columnUtils.getColumnList(backend.type, extraColumns)
const headerRow = (
-
+
{columns.map(column => {
// This is a React component, even though it does not contain JSX.
// eslint-disable-next-line no-restricted-syntax
const Heading = columnHeading.COLUMN_HEADING[column]
return (
-
+
)
diff --git a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/AssetsTableContextMenu.tsx b/app/ide-desktop/lib/dashboard/src/layouts/AssetsTableContextMenu.tsx
similarity index 86%
rename from app/ide-desktop/lib/dashboard/src/layouts/dashboard/AssetsTableContextMenu.tsx
rename to app/ide-desktop/lib/dashboard/src/layouts/AssetsTableContextMenu.tsx
index 5b5a26dd50e5..6350a983863f 100644
--- a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/AssetsTableContextMenu.tsx
+++ b/app/ide-desktop/lib/dashboard/src/layouts/AssetsTableContextMenu.tsx
@@ -10,20 +10,20 @@ import type * as assetEvent from '#/events/assetEvent'
import AssetEventType from '#/events/AssetEventType'
import type * as assetListEvent from '#/events/assetListEvent'
-import Category from '#/layouts/dashboard/CategorySwitcher/Category'
-import GlobalContextMenu from '#/layouts/dashboard/GlobalContextMenu'
+import Category from '#/layouts/CategorySwitcher/Category'
+import GlobalContextMenu from '#/layouts/GlobalContextMenu'
import ContextMenu from '#/components/ContextMenu'
import ContextMenus from '#/components/ContextMenus'
-import ConfirmDeleteModal from '#/components/dashboard/ConfirmDeleteModal'
import MenuEntry from '#/components/MenuEntry'
+import ConfirmDeleteModal from '#/modals/ConfirmDeleteModal'
+
import * as backendModule from '#/services/Backend'
import type AssetTreeNode from '#/utilities/AssetTreeNode'
import type * as pasteDataModule from '#/utilities/pasteData'
import * as permissions from '#/utilities/permissions'
-import * as shortcutManager from '#/utilities/ShortcutManager'
import * as string from '#/utilities/string'
import * as uniqueString from '#/utilities/uniqueString'
@@ -95,7 +95,7 @@ export default function AssetsTableContextMenu(props: AssetsTableContextMenuProp
} else {
setModal(
{
clearSelectedKeys()
dispatchAssetEvent({ type: AssetEventType.delete, ids: selectedKeys })
@@ -121,7 +121,8 @@ export default function AssetsTableContextMenu(props: AssetsTableContextMenuProp
@@ -130,34 +131,29 @@ export default function AssetsTableContextMenu(props: AssetsTableContextMenuProp
} else if (category !== Category.home) {
return null
} else {
- const deleteAction = isCloud
- ? shortcutManager.KeyboardAction.moveAllToTrash
- : shortcutManager.KeyboardAction.deleteAll
return (
{selectedKeys.size !== 0 && (
{ownsAllSelectedAssets && (
-
- )}
- {isCloud && (
)}
+ {isCloud && (
+
+ )}
{isCloud && ownsAllSelectedAssets && (
-
+
)}
{pasteData != null && pasteData.data.size > 0 && (
{
const [firstKey] = selectedKeys
const selectedNode =
diff --git a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/BackendSwitcher.tsx b/app/ide-desktop/lib/dashboard/src/layouts/BackendSwitcher.tsx
similarity index 100%
rename from app/ide-desktop/lib/dashboard/src/layouts/dashboard/BackendSwitcher.tsx
rename to app/ide-desktop/lib/dashboard/src/layouts/BackendSwitcher.tsx
diff --git a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/CategorySwitcher.tsx b/app/ide-desktop/lib/dashboard/src/layouts/CategorySwitcher.tsx
similarity index 98%
rename from app/ide-desktop/lib/dashboard/src/layouts/dashboard/CategorySwitcher.tsx
rename to app/ide-desktop/lib/dashboard/src/layouts/CategorySwitcher.tsx
index 48402d8fddfd..3c8ceb31012f 100644
--- a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/CategorySwitcher.tsx
+++ b/app/ide-desktop/lib/dashboard/src/layouts/CategorySwitcher.tsx
@@ -11,7 +11,7 @@ import * as modalProvider from '#/providers/ModalProvider'
import type * as assetEvent from '#/events/assetEvent'
import AssetEventType from '#/events/AssetEventType'
-import Category from '#/layouts/dashboard/CategorySwitcher/Category'
+import Category from '#/layouts/CategorySwitcher/Category'
import SvgMask from '#/components/SvgMask'
diff --git a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/CategorySwitcher/Category.ts b/app/ide-desktop/lib/dashboard/src/layouts/CategorySwitcher/Category.ts
similarity index 100%
rename from app/ide-desktop/lib/dashboard/src/layouts/dashboard/CategorySwitcher/Category.ts
rename to app/ide-desktop/lib/dashboard/src/layouts/CategorySwitcher/Category.ts
diff --git a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/Chat.tsx b/app/ide-desktop/lib/dashboard/src/layouts/Chat.tsx
similarity index 99%
rename from app/ide-desktop/lib/dashboard/src/layouts/dashboard/Chat.tsx
rename to app/ide-desktop/lib/dashboard/src/layouts/Chat.tsx
index 69831b49cfa8..49e978c01883 100644
--- a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/Chat.tsx
+++ b/app/ide-desktop/lib/dashboard/src/layouts/Chat.tsx
@@ -15,7 +15,7 @@ import * as gtagHooks from '#/hooks/gtagHooks'
import * as authProvider from '#/providers/AuthProvider'
import * as loggerProvider from '#/providers/LoggerProvider'
-import * as pageSwitcher from '#/layouts/dashboard/PageSwitcher'
+import * as pageSwitcher from '#/layouts/PageSwitcher'
import SvgMask from '#/components/SvgMask'
import Twemoji from '#/components/Twemoji'
diff --git a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/ChatPlaceholder.tsx b/app/ide-desktop/lib/dashboard/src/layouts/ChatPlaceholder.tsx
similarity index 95%
rename from app/ide-desktop/lib/dashboard/src/layouts/dashboard/ChatPlaceholder.tsx
rename to app/ide-desktop/lib/dashboard/src/layouts/ChatPlaceholder.tsx
index 5086b0b7c974..4758c47d09cc 100644
--- a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/ChatPlaceholder.tsx
+++ b/app/ide-desktop/lib/dashboard/src/layouts/ChatPlaceholder.tsx
@@ -12,8 +12,8 @@ import * as navigateHooks from '#/hooks/navigateHooks'
import * as loggerProvider from '#/providers/LoggerProvider'
-import * as chat from '#/layouts/dashboard/Chat'
-import * as pageSwitcher from '#/layouts/dashboard/PageSwitcher'
+import * as chat from '#/layouts/Chat'
+import * as pageSwitcher from '#/layouts/PageSwitcher'
/** Props for a {@link ChatPlaceholder}. */
export interface ChatPlaceholderProps {
diff --git a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/DataLinkInput.tsx b/app/ide-desktop/lib/dashboard/src/layouts/DataLinkInput.tsx
similarity index 100%
rename from app/ide-desktop/lib/dashboard/src/layouts/dashboard/DataLinkInput.tsx
rename to app/ide-desktop/lib/dashboard/src/layouts/DataLinkInput.tsx
diff --git a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/Drive.tsx b/app/ide-desktop/lib/dashboard/src/layouts/Drive.tsx
similarity index 97%
rename from app/ide-desktop/lib/dashboard/src/layouts/dashboard/Drive.tsx
rename to app/ide-desktop/lib/dashboard/src/layouts/Drive.tsx
index 83a96cb2fd00..f961b7f8c5cf 100644
--- a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/Drive.tsx
+++ b/app/ide-desktop/lib/dashboard/src/layouts/Drive.tsx
@@ -17,13 +17,13 @@ import AssetEventType from '#/events/AssetEventType'
import type * as assetListEvent from '#/events/assetListEvent'
import AssetListEventType from '#/events/AssetListEventType'
-import type * as assetPanel from '#/layouts/dashboard/AssetPanel'
-import type * as assetSearchBar from '#/layouts/dashboard/AssetSearchBar'
-import AssetsTable from '#/layouts/dashboard/AssetsTable'
-import CategorySwitcher from '#/layouts/dashboard/CategorySwitcher'
-import Category from '#/layouts/dashboard/CategorySwitcher/Category'
-import DriveBar from '#/layouts/dashboard/DriveBar'
-import Labels from '#/layouts/dashboard/Labels'
+import type * as assetPanel from '#/layouts/AssetPanel'
+import type * as assetSearchBar from '#/layouts/AssetSearchBar'
+import AssetsTable from '#/layouts/AssetsTable'
+import CategorySwitcher from '#/layouts/CategorySwitcher'
+import Category from '#/layouts/CategorySwitcher/Category'
+import DriveBar from '#/layouts/DriveBar'
+import Labels from '#/layouts/Labels'
import type * as spinner from '#/components/Spinner'
diff --git a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/DriveBar.tsx b/app/ide-desktop/lib/dashboard/src/layouts/DriveBar.tsx
similarity index 89%
rename from app/ide-desktop/lib/dashboard/src/layouts/dashboard/DriveBar.tsx
rename to app/ide-desktop/lib/dashboard/src/layouts/DriveBar.tsx
index a46d43e01c3d..e070e311cedd 100644
--- a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/DriveBar.tsx
+++ b/app/ide-desktop/lib/dashboard/src/layouts/DriveBar.tsx
@@ -9,21 +9,22 @@ import DataDownloadIcon from 'enso-assets/data_download.svg'
import DataUploadIcon from 'enso-assets/data_upload.svg'
import * as backendProvider from '#/providers/BackendProvider'
+import * as inputBindingsProvider from '#/providers/InputBindingsProvider'
import * as modalProvider from '#/providers/ModalProvider'
-import * as shortcutManagerProvider from '#/providers/ShortcutManagerProvider'
import type * as assetEvent from '#/events/assetEvent'
import AssetEventType from '#/events/AssetEventType'
-import Category from '#/layouts/dashboard/CategorySwitcher/Category'
-import UpsertDataLinkModal from '#/layouts/dashboard/UpsertDataLinkModal'
-import UpsertSecretModal from '#/layouts/dashboard/UpsertSecretModal'
+import Category from '#/layouts/CategorySwitcher/Category'
import Button from '#/components/Button'
+import UpsertDataLinkModal from '#/modals/UpsertDataLinkModal'
+import UpsertSecretModal from '#/modals/UpsertSecretModal'
+
import * as backendModule from '#/services/Backend'
-import * as shortcutManagerModule from '#/utilities/ShortcutManager'
+import * as sanitizedEventTargets from '#/utilities/sanitizedEventTargets'
// ================
// === DriveBar ===
@@ -48,28 +49,28 @@ export default function DriveBar(props: DriveBarProps) {
const { doCreateSecret, doCreateDataLink, doUploadFiles, dispatchAssetEvent } = props
const { backend } = backendProvider.useBackend()
const { setModal, unsetModal } = modalProvider.useSetModal()
- const { shortcutManager } = shortcutManagerProvider.useShortcutManager()
+ const inputBindings = inputBindingsProvider.useInputBindings()
const uploadFilesRef = React.useRef(null)
const isCloud = backend.type === backendModule.BackendType.remote
const isHomeCategory = category === Category.home || !isCloud
React.useEffect(() => {
- return shortcutManager.registerKeyboardHandlers({
+ return inputBindings.attach(sanitizedEventTargets.document.body, 'keydown', {
...(backend.type !== backendModule.BackendType.local
? {
- [shortcutManagerModule.KeyboardAction.newFolder]: () => {
+ newFolder: () => {
doCreateDirectory()
},
}
: {}),
- [shortcutManagerModule.KeyboardAction.newProject]: () => {
+ newProject: () => {
doCreateProject()
},
- [shortcutManagerModule.KeyboardAction.uploadFiles]: () => {
+ uploadFiles: () => {
uploadFilesRef.current?.click()
},
})
- }, [backend.type, doCreateDirectory, doCreateProject, /* should never change */ shortcutManager])
+ }, [backend.type, doCreateDirectory, doCreateProject, /* should never change */ inputBindings])
return (
diff --git a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/Editor.tsx b/app/ide-desktop/lib/dashboard/src/layouts/Editor.tsx
similarity index 100%
rename from app/ide-desktop/lib/dashboard/src/layouts/dashboard/Editor.tsx
rename to app/ide-desktop/lib/dashboard/src/layouts/Editor.tsx
diff --git a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/GlobalContextMenu.tsx b/app/ide-desktop/lib/dashboard/src/layouts/GlobalContextMenu.tsx
similarity index 88%
rename from app/ide-desktop/lib/dashboard/src/layouts/dashboard/GlobalContextMenu.tsx
rename to app/ide-desktop/lib/dashboard/src/layouts/GlobalContextMenu.tsx
index a0c3bd0ff847..e0bde26604da 100644
--- a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/GlobalContextMenu.tsx
+++ b/app/ide-desktop/lib/dashboard/src/layouts/GlobalContextMenu.tsx
@@ -8,15 +8,13 @@ import * as modalProvider from '#/providers/ModalProvider'
import type * as assetListEventModule from '#/events/assetListEvent'
import AssetListEventType from '#/events/AssetListEventType'
-import UpsertDataLinkModal from '#/layouts/dashboard/UpsertDataLinkModal'
-import UpsertSecretModal from '#/layouts/dashboard/UpsertSecretModal'
-
import ContextMenu from '#/components/ContextMenu'
import MenuEntry from '#/components/MenuEntry'
-import * as backendModule from '#/services/Backend'
+import UpsertDataLinkModal from '#/modals/UpsertDataLinkModal'
+import UpsertSecretModal from '#/modals/UpsertSecretModal'
-import * as shortcutManager from '#/utilities/ShortcutManager'
+import * as backendModule from '#/services/Backend'
/** Props for a {@link GlobalContextMenu}. */
export interface GlobalContextMenuProps {
@@ -69,11 +67,7 @@ export default function GlobalContextMenu(props: GlobalContextMenuProps) {
)}
{
if (filesInputRef.current?.isConnected === true) {
filesInputRef.current.click()
@@ -101,7 +95,7 @@ export default function GlobalContextMenu(props: GlobalContextMenuProps) {
{isCloud && (
{
unsetModal()
dispatchAssetListEvent({
@@ -118,7 +112,7 @@ export default function GlobalContextMenu(props: GlobalContextMenuProps) {
{isCloud && (
{
unsetModal()
dispatchAssetListEvent({
@@ -132,7 +126,7 @@ export default function GlobalContextMenu(props: GlobalContextMenuProps) {
{isCloud && (
{
setModal(
{
setModal(
{
unsetModal()
doPaste(rootDirectoryId, rootDirectoryId)
diff --git a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/Home.tsx b/app/ide-desktop/lib/dashboard/src/layouts/Home.tsx
similarity index 92%
rename from app/ide-desktop/lib/dashboard/src/layouts/dashboard/Home.tsx
rename to app/ide-desktop/lib/dashboard/src/layouts/Home.tsx
index 807228503ae2..9c3f287cb49e 100644
--- a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/Home.tsx
+++ b/app/ide-desktop/lib/dashboard/src/layouts/Home.tsx
@@ -1,8 +1,8 @@
/** @file Home screen. */
import * as React from 'react'
-import Samples from '#/layouts/dashboard/Samples'
-import WhatsNew from '#/layouts/dashboard/WhatsNew'
+import Samples from '#/layouts/Samples'
+import WhatsNew from '#/layouts/WhatsNew'
import type * as spinner from '#/components/Spinner'
diff --git a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/Labels.tsx b/app/ide-desktop/lib/dashboard/src/layouts/Labels.tsx
similarity index 95%
rename from app/ide-desktop/lib/dashboard/src/layouts/dashboard/Labels.tsx
rename to app/ide-desktop/lib/dashboard/src/layouts/Labels.tsx
index f435e156e29a..4305f65a5dd6 100644
--- a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/Labels.tsx
+++ b/app/ide-desktop/lib/dashboard/src/layouts/Labels.tsx
@@ -6,14 +6,14 @@ import Trash2Icon from 'enso-assets/trash2.svg'
import * as modalProvider from '#/providers/ModalProvider'
-import NewLabelModal from '#/layouts/dashboard/NewLabelModal'
-
-import ConfirmDeleteModal from '#/components/dashboard/ConfirmDeleteModal'
import Label from '#/components/dashboard/Label'
import * as labelUtils from '#/components/dashboard/Label/labelUtils'
-import DragModal from '#/components/DragModal'
import SvgMask from '#/components/SvgMask'
+import ConfirmDeleteModal from '#/modals/ConfirmDeleteModal'
+import DragModal from '#/modals/DragModal'
+import NewLabelModal from '#/modals/NewLabelModal'
+
import type * as backend from '#/services/Backend'
import * as array from '#/utilities/array'
@@ -99,7 +99,7 @@ export default function Labels(props: LabelsProps) {
event.stopPropagation()
setModal(
{
doDeleteLabel(label.id, label.value)
}}
diff --git a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/PageSwitcher.tsx b/app/ide-desktop/lib/dashboard/src/layouts/PageSwitcher.tsx
similarity index 100%
rename from app/ide-desktop/lib/dashboard/src/layouts/dashboard/PageSwitcher.tsx
rename to app/ide-desktop/lib/dashboard/src/layouts/PageSwitcher.tsx
diff --git a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/Samples.tsx b/app/ide-desktop/lib/dashboard/src/layouts/Samples.tsx
similarity index 100%
rename from app/ide-desktop/lib/dashboard/src/layouts/dashboard/Samples.tsx
rename to app/ide-desktop/lib/dashboard/src/layouts/Samples.tsx
diff --git a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/Settings.tsx b/app/ide-desktop/lib/dashboard/src/layouts/Settings.tsx
similarity index 78%
rename from app/ide-desktop/lib/dashboard/src/layouts/dashboard/Settings.tsx
rename to app/ide-desktop/lib/dashboard/src/layouts/Settings.tsx
index 54f20458c2a3..c830bfe470d8 100644
--- a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/Settings.tsx
+++ b/app/ide-desktop/lib/dashboard/src/layouts/Settings.tsx
@@ -4,11 +4,12 @@ import * as React from 'react'
import * as authProvider from '#/providers/AuthProvider'
import * as backendProvider from '#/providers/BackendProvider'
-import AccountSettingsTab from '#/layouts/dashboard/Settings/AccountSettingsTab'
-import MembersSettingsTab from '#/layouts/dashboard/Settings/MembersSettingsTab'
-import OrganizationSettingsTab from '#/layouts/dashboard/Settings/OrganizationSettingsTab'
-import SettingsTab from '#/layouts/dashboard/Settings/SettingsTab'
-import SettingsSidebar from '#/layouts/dashboard/SettingsSidebar'
+import AccountSettingsTab from '#/layouts/Settings/AccountSettingsTab'
+import KeyboardShortcutsSettingsTab from '#/layouts/Settings/KeyboardShortcutsSettingsTab'
+import MembersSettingsTab from '#/layouts/Settings/MembersSettingsTab'
+import OrganizationSettingsTab from '#/layouts/Settings/OrganizationSettingsTab'
+import SettingsTab from '#/layouts/Settings/SettingsTab'
+import SettingsSidebar from '#/layouts/SettingsSidebar'
import * as backendModule from '#/services/Backend'
@@ -61,6 +62,10 @@ export default function Settings() {
content =
break
}
+ case SettingsTab.keyboardShortcuts: {
+ content =
+ break
+ }
default: {
// This case should be removed when all settings tabs are implemented.
content = <>>
@@ -69,7 +74,7 @@ export default function Settings() {
}
return (
-
+ )
+}
diff --git a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/Settings/MembersSettingsTab.tsx b/app/ide-desktop/lib/dashboard/src/layouts/Settings/MembersSettingsTab.tsx
similarity index 97%
rename from app/ide-desktop/lib/dashboard/src/layouts/dashboard/Settings/MembersSettingsTab.tsx
rename to app/ide-desktop/lib/dashboard/src/layouts/Settings/MembersSettingsTab.tsx
index b774fef354ce..719539d3115e 100644
--- a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/Settings/MembersSettingsTab.tsx
+++ b/app/ide-desktop/lib/dashboard/src/layouts/Settings/MembersSettingsTab.tsx
@@ -6,10 +6,10 @@ import * as asyncEffectHooks from '#/hooks/asyncEffectHooks'
import * as backendProvider from '#/providers/BackendProvider'
import * as modalProvider from '#/providers/ModalProvider'
-import InviteUsersModal from '#/layouts/dashboard/InviteUsersModal'
-
import StatelessSpinner, * as statelessSpinner from '#/components/StatelessSpinner'
+import InviteUsersModal from '#/modals/InviteUsersModal'
+
// ==========================
// === MembersSettingsTab ===
// ==========================
diff --git a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/Settings/OrganizationSettingsTab.tsx b/app/ide-desktop/lib/dashboard/src/layouts/Settings/OrganizationSettingsTab.tsx
similarity index 100%
rename from app/ide-desktop/lib/dashboard/src/layouts/dashboard/Settings/OrganizationSettingsTab.tsx
rename to app/ide-desktop/lib/dashboard/src/layouts/Settings/OrganizationSettingsTab.tsx
diff --git a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/Settings/SettingsTab.ts b/app/ide-desktop/lib/dashboard/src/layouts/Settings/SettingsTab.ts
similarity index 100%
rename from app/ide-desktop/lib/dashboard/src/layouts/dashboard/Settings/SettingsTab.ts
rename to app/ide-desktop/lib/dashboard/src/layouts/Settings/SettingsTab.ts
diff --git a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/SettingsSidebar.tsx b/app/ide-desktop/lib/dashboard/src/layouts/SettingsSidebar.tsx
similarity index 91%
rename from app/ide-desktop/lib/dashboard/src/layouts/dashboard/SettingsSidebar.tsx
rename to app/ide-desktop/lib/dashboard/src/layouts/SettingsSidebar.tsx
index 920176b79873..1de61455ade8 100644
--- a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/SettingsSidebar.tsx
+++ b/app/ide-desktop/lib/dashboard/src/layouts/SettingsSidebar.tsx
@@ -2,12 +2,13 @@
import * as React from 'react'
import BellIcon from 'enso-assets/bell.svg'
+import KeyboardShortcutsIcon from 'enso-assets/keyboard_shortcuts.svg'
import PeopleSettingsIcon from 'enso-assets/people_settings.svg'
import PeopleIcon from 'enso-assets/people.svg'
import SettingsIcon from 'enso-assets/settings.svg'
import SlidersIcon from 'enso-assets/sliders.svg'
-import SettingsTab from '#/layouts/dashboard/Settings/SettingsTab'
+import SettingsTab from '#/layouts/Settings/SettingsTab'
import SvgMask from '#/components/SvgMask'
@@ -54,6 +55,17 @@ const SECTIONS: SettingsSectionData[] = [
},
],
},
+ {
+ name: 'Look and feel',
+ tabs: [
+ {
+ name: 'Keyboard shortcuts',
+ settingsTab: SettingsTab.keyboardShortcuts,
+ icon: KeyboardShortcutsIcon,
+ visible: true,
+ },
+ ],
+ },
]
// =============
diff --git a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/TopBar.tsx b/app/ide-desktop/lib/dashboard/src/layouts/TopBar.tsx
similarity index 91%
rename from app/ide-desktop/lib/dashboard/src/layouts/dashboard/TopBar.tsx
rename to app/ide-desktop/lib/dashboard/src/layouts/TopBar.tsx
index 6e1692aeb63a..5fbd90618f7a 100644
--- a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/TopBar.tsx
+++ b/app/ide-desktop/lib/dashboard/src/layouts/TopBar.tsx
@@ -1,11 +1,11 @@
/** @file The top-bar of dashboard. */
import * as React from 'react'
-import type * as assetSearchBar from '#/layouts/dashboard/AssetSearchBar'
-import AssetSearchBar from '#/layouts/dashboard/AssetSearchBar'
-import BackendSwitcher from '#/layouts/dashboard/BackendSwitcher'
-import PageSwitcher, * as pageSwitcher from '#/layouts/dashboard/PageSwitcher'
-import UserBar from '#/layouts/dashboard/UserBar'
+import type * as assetSearchBar from '#/layouts/AssetSearchBar'
+import AssetSearchBar from '#/layouts/AssetSearchBar'
+import BackendSwitcher from '#/layouts/BackendSwitcher'
+import PageSwitcher, * as pageSwitcher from '#/layouts/PageSwitcher'
+import UserBar from '#/layouts/UserBar'
import AssetInfoBar from '#/components/dashboard/AssetInfoBar'
diff --git a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/UserBar.tsx b/app/ide-desktop/lib/dashboard/src/layouts/UserBar.tsx
similarity index 94%
rename from app/ide-desktop/lib/dashboard/src/layouts/dashboard/UserBar.tsx
rename to app/ide-desktop/lib/dashboard/src/layouts/UserBar.tsx
index 8868728ba8a0..c1fed7b0ca54 100644
--- a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/UserBar.tsx
+++ b/app/ide-desktop/lib/dashboard/src/layouts/UserBar.tsx
@@ -8,13 +8,14 @@ import * as authProvider from '#/providers/AuthProvider'
import * as backendProvider from '#/providers/BackendProvider'
import * as modalProvider from '#/providers/ModalProvider'
-import InviteUsersModal from '#/layouts/dashboard/InviteUsersModal'
-import ManagePermissionsModal from '#/layouts/dashboard/ManagePermissionsModal'
-import * as pageSwitcher from '#/layouts/dashboard/PageSwitcher'
-import UserMenu from '#/layouts/dashboard/UserMenu'
+import * as pageSwitcher from '#/layouts/PageSwitcher'
+import UserMenu from '#/layouts/UserMenu'
import Button from '#/components/Button'
+import InviteUsersModal from '#/modals/InviteUsersModal'
+import ManagePermissionsModal from '#/modals/ManagePermissionsModal'
+
import * as backendModule from '#/services/Backend'
// ===============
diff --git a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/UserMenu.tsx b/app/ide-desktop/lib/dashboard/src/layouts/UserMenu.tsx
similarity index 91%
rename from app/ide-desktop/lib/dashboard/src/layouts/dashboard/UserMenu.tsx
rename to app/ide-desktop/lib/dashboard/src/layouts/UserMenu.tsx
index 42b048dd8896..c2d98f1a763d 100644
--- a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/UserMenu.tsx
+++ b/app/ide-desktop/lib/dashboard/src/layouts/UserMenu.tsx
@@ -11,14 +11,13 @@ import * as toastAndLogHooks from '#/hooks/toastAndLogHooks'
import * as authProvider from '#/providers/AuthProvider'
import * as modalProvider from '#/providers/ModalProvider'
-import * as pageSwitcher from '#/layouts/dashboard/PageSwitcher'
+import * as pageSwitcher from '#/layouts/PageSwitcher'
import MenuEntry from '#/components/MenuEntry'
import Modal from '#/components/Modal'
import * as download from '#/utilities/download'
import * as github from '#/utilities/github'
-import * as shortcutManager from '#/utilities/ShortcutManager'
// ================
// === UserMenu ===
@@ -69,7 +68,7 @@ export default function UserMenu(props: UserMenuProps) {