Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
900b8d7
Always render WorkflowDiagram, Inspectors and Panels
stuartc Nov 14, 2025
4b4bdfb
Implement priority-based keyboard shortcuts system
stuartc Nov 14, 2025
15293db
Complete composable test helper infrastructure for collaborative editor
stuartc Nov 14, 2025
f24486c
Refactor Header tests to use composable helpers
stuartc Nov 14, 2025
f5de95b
Add comprehensive keyboard shortcut tests for collaborative editor
stuartc Nov 14, 2025
3586d0e
Remove debug logging and ignore coverage directory
stuartc Nov 14, 2025
4b47483
Fix React warnings in keyboard tests
stuartc Nov 17, 2025
5e4a426
Add comprehensive keyboard tests for ManualRunPanel Escape
stuartc Nov 18, 2025
1dd9969
Migrate Open Code Editor shortcut to KeyboardProvider
stuartc Nov 18, 2025
bfd5ac3
Migrate Save Workflow keyboard shortcut to KeyboardProvider
stuartc Nov 18, 2025
6da9dc7
Fix dynamic enabled option in keyboard shortcuts
stuartc Nov 18, 2025
b8d4021
Migrate Save & Sync keyboard shortcut to KeyboardProvider
stuartc Nov 18, 2025
78ca6ea
Migrate Open Run Panel shortcut to KeyboardProvider
stuartc Nov 19, 2025
1ba87bd
Migrate Close Run Panel shortcut to KeyboardProvider
stuartc Nov 19, 2025
d6e271b
Migrate Close Inspector Panel shortcut to KeyboardProvider
stuartc Nov 19, 2025
d6d27a3
Remove redundant respondToHotKey prop from Inspector
stuartc Nov 19, 2025
f3579d1
Migrate IDE Escape shortcut to KeyboardProvider
stuartc Nov 19, 2025
a5b7962
Migrate IDE Run/Retry shortcuts to KeyboardProvider (Handlers #8 & #9)
stuartc Nov 19, 2025
460e6ee
Fix keyboard shortcut tests after migration to KeyboardProvider
stuartc Nov 19, 2025
6a6e031
Extract Monaco keyboard overrides into reusable utility
stuartc Nov 19, 2025
f53e38f
Send only meta or ctrl as combo
stuartc Nov 19, 2025
f368272
Add debug logging and useKeyboardDebugInfo hook
stuartc Nov 19, 2025
22002aa
Fix ManualRunPanel keyboard tests
stuartc Nov 19, 2025
744e06b
Complete react-hotkeys-hook removal and finalize KeyboardProvider mig…
stuartc Nov 19, 2025
6effaa6
Remove git conflict marker from FullScreenIDE.tsx
stuartc Nov 19, 2025
52a2d83
Fix imports after rebase: remove HOTKEY_SCOPES and createRunStore ref…
stuartc Nov 19, 2025
6d7d87b
Fix test imports: replace HotkeysProvider with KeyboardProvider in Fu…
stuartc Nov 19, 2025
b9b8326
Migrate RunStore to HistoryStore in tests
stuartc Nov 19, 2025
e9e4565
Update keyboard test utilities.
stuartc Nov 20, 2025
c4a3aec
feat: collaborative pointers on canvas (#4010)
doc-han Nov 20, 2025
6ffd825
Reverts styling change in MiniHistory which blocked VersionMismatch b…
lmac-1 Nov 20, 2025
af9ccb9
Fix version switch rendering with store batching and diagram guard (#…
stuartc Nov 20, 2025
6b7714a
Fix phantom snapshot creation from virtual trigger field (#4029)
lmac-1 Nov 20, 2025
038eeb1
Merge remote-tracking branch 'origin/main' into diagram_remount
stuartc Nov 20, 2025
2f6ce16
Rethrow on keyhandlers and update README for KeyboardProvider
stuartc Nov 20, 2025
c334473
Update CHANGELOG
stuartc Nov 20, 2025
c99fa9a
Merge remote-tracking branch 'origin/main' into diagram_remount
stuartc Nov 20, 2025
11713d6
Organise imports
stuartc Nov 20, 2025
f3bb9c4
Merge remote-tracking branch 'origin/main' into diagram_remount
stuartc Nov 21, 2025
179c75b
Fix React useMemo warning with Immer structural sharing
stuartc Nov 21, 2025
775ea35
Fix IDE overlay positioning to respect layout boundaries
stuartc Nov 21, 2025
85b2ca4
Merge remote-tracking branch 'origin/main' into diagram_remount
stuartc Nov 21, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,4 @@ assets/test-results

.claude/settings.local.json
test-results
assets/coverage
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ and this project adheres to

### Changed

- Always keep the Diagram mounted even when the IDE is present
[#3981](https://github.com/OpenFn/lightning/issues/3981)
- Dropped react-hotkeys-hook for custom priority based key handler hook
[#3981](https://github.com/OpenFn/lightning/issues/3981)
- Simplified IDE by only letting users see the "Create a New Manual Run Panel"
when an existing Run isn't already loaded. Cleaned up the Run panel.
[#4006](https://github.com/OpenFn/lightning/issues/4006)
Expand Down
2 changes: 1 addition & 1 deletion assets/eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ const javascriptFiles = [
].map(ext => `**/*.${ext}`);
const nodeFiles = allExtensions.map(ext => `*.${ext}`);
const browserFiles = allExtensions.flatMap(ext =>
['js', 'vendor', 'dev-server'].map(dir => `${dir}/**/*.${ext}`)
['js', 'vendor', 'dev-server', 'test'].map(dir => `${dir}/**/*.${ext}`)
);
const reactFiles = [
...jsxExtensions,
Expand Down
29 changes: 12 additions & 17 deletions assets/js/collaborative-editor/CollaborativeEditor.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { useMemo } from 'react';
import { HotkeysProvider } from 'react-hotkeys-hook';

import { SocketProvider } from '../react/contexts/SocketProvider';
import { useURLState } from '../react/lib/use-url-state';
Expand All @@ -22,6 +21,7 @@ import {
import { useIsRunPanelOpen } from './hooks/useUI';
import { useVersionSelect } from './hooks/useVersionSelect';
import { useWorkflowState } from './hooks/useWorkflow';
import { KeyboardProvider } from './keyboard';

export interface CollaborativeEditorDataProps {
'data-workflow-id': string;
Expand Down Expand Up @@ -139,19 +139,14 @@ function BreadcrumbContent({
]);

return (
<>
{/* Only render Header for Canvas mode - IDE mode has its own Header in FullScreenIDE */}
{!isIDEOpen && (
<Header
key="canvas-header"
{...(projectId !== undefined && { projectId })}
workflowId={workflowId}
isRunPanelOpen={isRunPanelOpen}
>
{breadcrumbElements}
</Header>
)}
</>
<Header
key="canvas-header"
{...(projectId !== undefined && { projectId })}
workflowId={workflowId}
isRunPanelOpen={isRunPanelOpen}
>
{breadcrumbElements}
</Header>
);
}

Expand All @@ -178,9 +173,9 @@ export const CollaborativeEditor: WithActionProps<
};

return (
<HotkeysProvider>
<KeyboardProvider>
<div
className="collaborative-editor h-full flex flex-col"
className="collaborative-editor h-full flex flex-col relative"
data-testid="collaborative-editor"
>
<SocketProvider>
Expand Down Expand Up @@ -229,6 +224,6 @@ export const CollaborativeEditor: WithActionProps<
</SessionProvider>
</SocketProvider>
</div>
</HotkeysProvider>
</KeyboardProvider>
);
};
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { Dialog, DialogBackdrop, DialogPanel } from '@headlessui/react';
import { useEffect, useMemo, useState } from 'react';
import { useHotkeysContext } from 'react-hotkeys-hook';

import { HOTKEY_SCOPES } from '../constants/hotkeys';
import { useAdaptors } from '../hooks/useAdaptors';
import type { Adaptor } from '../types/adaptor';
import { getAdaptorDisplayName } from '../utils/adaptorUtils';
Expand Down Expand Up @@ -31,24 +29,6 @@ export function AdaptorSelectionModal({
const [searchQuery, setSearchQuery] = useState('');
const [focusedIndex, setFocusedIndex] = useState<number>(0);

// Keyboard scope management
const { enableScope, disableScope } = useHotkeysContext();

useEffect(() => {
if (isOpen) {
enableScope(HOTKEY_SCOPES.MODAL);
disableScope(HOTKEY_SCOPES.PANEL);
disableScope(HOTKEY_SCOPES.RUN_PANEL);
} else {
disableScope(HOTKEY_SCOPES.MODAL);
enableScope(HOTKEY_SCOPES.PANEL);
}

return () => {
disableScope(HOTKEY_SCOPES.MODAL);
};
}, [isOpen, enableScope, disableScope]);

// Reset state when modal closes
useEffect(() => {
if (!isOpen) {
Expand Down
22 changes: 0 additions & 22 deletions assets/js/collaborative-editor/components/AlertDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,6 @@ import {
DialogPanel,
DialogTitle,
} from '@headlessui/react';
import { useEffect } from 'react';
import { useHotkeysContext } from 'react-hotkeys-hook';

import { HOTKEY_SCOPES } from '#/collaborative-editor/constants/hotkeys';

interface AlertDialogProps {
isOpen: boolean;
Expand Down Expand Up @@ -52,24 +48,6 @@ export function AlertDialog({
? 'bg-red-600 hover:bg-red-500 focus-visible:outline-red-600'
: 'bg-primary-600 hover:bg-primary-500 focus-visible:outline-primary-600';

// Use HotkeysContext to control keyboard scope precedence
const { enableScope, disableScope } = useHotkeysContext();

useEffect(() => {
if (isOpen) {
enableScope(HOTKEY_SCOPES.MODAL);
disableScope(HOTKEY_SCOPES.PANEL);
disableScope(HOTKEY_SCOPES.RUN_PANEL);
} else {
disableScope(HOTKEY_SCOPES.MODAL);
enableScope(HOTKEY_SCOPES.PANEL);
}

return () => {
disableScope(HOTKEY_SCOPES.MODAL);
};
}, [isOpen, enableScope, disableScope]);

return (
<Dialog open={isOpen} onClose={onClose} className="relative z-50">
<DialogBackdrop
Expand Down
32 changes: 3 additions & 29 deletions assets/js/collaborative-editor/components/CollaborativeMonaco.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type * as Y from 'yjs';
import _logger from '#/utils/logger';

import { type Monaco, MonacoEditor, setTheme } from '../../monaco';
import { addKeyboardShortcutOverrides } from '../../monaco/keyboard-overrides';

import { Cursors } from './Cursors';

Expand Down Expand Up @@ -51,35 +52,8 @@ export function CollaborativeMonaco({
const language = getLanguageFromAdaptor(adaptor);
monaco.editor.setModelLanguage(editor.getModel()!, language);

// Override Monaco's CMD+Enter to allow react-hotkeys-hook to handle it
editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter, () => {
const event = new KeyboardEvent('keydown', {
key: 'Enter',
code: 'Enter',
metaKey: true,
ctrlKey: true,
bubbles: true,
cancelable: true,
});
document.dispatchEvent(event);
});

// Override Monaco's CMD+Shift+Enter to allow react-hotkeys-hook to handle it
editor.addCommand(
monaco.KeyMod.CtrlCmd | monaco.KeyMod.Shift | monaco.KeyCode.Enter,
() => {
const event = new KeyboardEvent('keydown', {
key: 'Enter',
code: 'Enter',
metaKey: true,
ctrlKey: true,
shiftKey: true,
bubbles: true,
cancelable: true,
});
document.dispatchEvent(event);
}
);
// Override Monaco shortcuts to allow KeyboardProvider to handle them
addKeyboardShortcutOverrides(editor, monaco);

// Create initial binding if ytext and awareness are available
if (ytext && awareness) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,17 @@ import {
DialogPanel,
DialogTitle,
} from '@headlessui/react';
import { useEffect, useMemo, useState, useRef } from 'react';
import { useHotkeysContext } from 'react-hotkeys-hook';
import { useEffect, useMemo, useRef, useState } from 'react';

import { HOTKEY_SCOPES } from '#/collaborative-editor/constants/hotkeys';
import {
useCredentials,
useCredentialQueries,
useCredentials,
} from '#/collaborative-editor/hooks/useCredentials';
import type { Adaptor } from '#/collaborative-editor/types/adaptor';
import type { CredentialWithType } from '#/collaborative-editor/types/credential';
import {
extractAdaptorName,
extractAdaptorDisplayName,
extractAdaptorName,
extractPackageName,
} from '#/collaborative-editor/utils/adaptorUtils';

Expand Down Expand Up @@ -65,24 +63,6 @@ export function ConfigureAdaptorModal({
const { projectCredentials, keychainCredentials } = useCredentials();
const { credentialExists, getCredentialId } = useCredentialQueries();

// Keyboard scope management
const { enableScope, disableScope } = useHotkeysContext();

useEffect(() => {
if (isOpen) {
enableScope(HOTKEY_SCOPES.MODAL);
disableScope(HOTKEY_SCOPES.PANEL);
disableScope(HOTKEY_SCOPES.RUN_PANEL);
} else {
disableScope(HOTKEY_SCOPES.MODAL);
enableScope(HOTKEY_SCOPES.PANEL);
}

return () => {
disableScope(HOTKEY_SCOPES.MODAL);
};
}, [isOpen, enableScope, disableScope]);

// When adaptor changes externally (from Y.Doc or adaptor picker),
// automatically update to newest version and clear invalid credentials
const prevAdaptorRef = useRef(currentAdaptor);
Expand Down
2 changes: 1 addition & 1 deletion assets/js/collaborative-editor/components/Cursors.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export function Cursors() {
}

return { __html: cursorStyles };
}, [cursorsMap.size, ...Array.from(cursorsMap.keys())]);
}, [cursorsMap]);

// Detect when a users cursor is near the top of the editor and flip the
// position of the label to below their position.
Expand Down
21 changes: 0 additions & 21 deletions assets/js/collaborative-editor/components/GitHubSyncModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@ import {
DialogTitle,
} from '@headlessui/react';
import { useEffect, useId, useState } from 'react';
import { useHotkeysContext } from 'react-hotkeys-hook';

import { HOTKEY_SCOPES } from '../constants/hotkeys';
import {
useProject,
useProjectRepoConnection,
Expand All @@ -27,7 +25,6 @@ import { GITHUB_BASE_URL } from '../utils/constants';
* - Textarea for commit message input
* - Save & Sync button that triggers workflow save and GitHub sync
* - Cancel button to close without syncing
* - Proper keyboard scope management
*
* @example
* // Add to WorkflowEditor or Header component
Expand All @@ -49,24 +46,6 @@ export function GitHubSyncModal() {
const [commitMessage, setCommitMessage] = useState('');
const [isSaving, setIsSaving] = useState(false);

// Use HotkeysContext to control keyboard scope precedence
const { enableScope, disableScope } = useHotkeysContext();

useEffect(() => {
if (isOpen) {
enableScope(HOTKEY_SCOPES.MODAL);
disableScope(HOTKEY_SCOPES.PANEL);
disableScope(HOTKEY_SCOPES.RUN_PANEL);
} else {
disableScope(HOTKEY_SCOPES.MODAL);
enableScope(HOTKEY_SCOPES.PANEL);
}

return () => {
disableScope(HOTKEY_SCOPES.MODAL);
};
}, [isOpen, enableScope, disableScope]);

// Set default commit message when modal opens
useEffect(() => {
if (isOpen && user) {
Expand Down
37 changes: 13 additions & 24 deletions assets/js/collaborative-editor/components/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/react';
import { XMarkIcon } from '@heroicons/react/24/outline';
import { useCallback, useMemo } from 'react';
import { useHotkeys } from 'react-hotkeys-hook';

import { useURLState } from '../../react/lib/use-url-state';
import { buildClassicalEditorUrl } from '../../utils/editorUrlConversion';
Expand All @@ -20,6 +19,7 @@ import {
useWorkflowSettingsErrors,
useWorkflowState,
} from '../hooks/useWorkflow';
import { useKeyboardShortcut } from '../keyboard';

import { ActiveCollaborators } from './ActiveCollaborators';
import { AIButton } from './AIButton';
Expand Down Expand Up @@ -244,33 +244,22 @@ export function Header({
return <ShortcutKeys keys={['mod', 'enter']} />; // Shortcut applies
}, [canRun, runTooltipMessage, isRunPanelOpen]);

useHotkeys(
'ctrl+s,meta+s',
event => {
event.preventDefault();
if (canSave) {
void saveWorkflow();
}
useKeyboardShortcut(
'Control+s, Meta+s',
() => {
void saveWorkflow();
},
{
enabled: true,
enableOnFormTags: true,
},
[saveWorkflow, canSave]
0,
{ enabled: canSave }
);

useHotkeys(
'ctrl+shift+s,meta+shift+s',
event => {
event.preventDefault();
if (canSave && repoConnection) {
openGitHubSyncModal();
}
},
{
enableOnFormTags: true,
useKeyboardShortcut(
'Control+Shift+s, Meta+Shift+s',
() => {
openGitHubSyncModal();
},
[openGitHubSyncModal, canSave, repoConnection]
0,
{ enabled: canSave && !!repoConnection }
);

return (
Expand Down
Loading