Skip to content
Merged
59 changes: 59 additions & 0 deletions src/LiveDevelopment/BrowserScripts/RemoteFunctions.js
Original file line number Diff line number Diff line change
Expand Up @@ -1573,16 +1573,75 @@
});
}

// Modifier shortcuts forwarded to the Phoenix KeyBindingManager. Clipboard
// and undo/redo are excluded so form inputs in the previewed page keep
// working normally.
const _KEYS_NOT_FORWARDED = { c:1, v:1, x:1, a:1, z:1, y:1, C:1, V:1, X:1, A:1, Z:1, Y:1 };

// Forwarding only makes sense when the page runs as an embedded LP iframe
// inside Phoenix; if the user popped the preview out into a real browser
// tab, the synthetic events would go to a window with no Phoenix UI.
// Default false, flip to true via __PHOENIX_EMBED_INFO (per its contract:
// guaranteed to fire in embedded iframes, not guaranteed otherwise).
let _isPhoenixEmbeddedIframe = false;
if (window.__PHOENIX_EMBED_INFO && window.__PHOENIX_EMBED_INFO.onPhoenixEmbeddedInfoAvailable) {

Check warning on line 1587 in src/LiveDevelopment/BrowserScripts/RemoteFunctions.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer `globalThis` over `window`.

See more on https://sonarcloud.io/project/issues?id=phcode-dev_phoenix&issues=AZ3XtSmJzI5R9QBPCqyA&open=AZ3XtSmJzI5R9QBPCqyA&pullRequest=2866

Check warning on line 1587 in src/LiveDevelopment/BrowserScripts/RemoteFunctions.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer `globalThis` over `window`.

See more on https://sonarcloud.io/project/issues?id=phcode-dev_phoenix&issues=AZ3XtSmJzI5R9QBPCqyC&open=AZ3XtSmJzI5R9QBPCqyC&pullRequest=2866

Check warning on line 1587 in src/LiveDevelopment/BrowserScripts/RemoteFunctions.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer using an optional chain expression instead, as it's more concise and easier to read.

See more on https://sonarcloud.io/project/issues?id=phcode-dev_phoenix&issues=AZ3XtSmJzI5R9QBPCqyB&open=AZ3XtSmJzI5R9QBPCqyB&pullRequest=2866
window.__PHOENIX_EMBED_INFO.onPhoenixEmbeddedInfoAvailable(function (isEmbedded) {

Check warning on line 1588 in src/LiveDevelopment/BrowserScripts/RemoteFunctions.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer `globalThis` over `window`.

See more on https://sonarcloud.io/project/issues?id=phcode-dev_phoenix&issues=AZ3XtSmJzI5R9QBPCqyD&open=AZ3XtSmJzI5R9QBPCqyD&pullRequest=2866
_isPhoenixEmbeddedIframe = !!isEmbedded;
});
}

function _isFunctionKey(event) {

Check warning on line 1593 in src/LiveDevelopment/BrowserScripts/RemoteFunctions.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Move function '_isFunctionKey' to the outer scope.

See more on https://sonarcloud.io/project/issues?id=phcode-dev_phoenix&issues=AZ3VVktVNd9f7yoDnFbw&open=AZ3VVktVNd9f7yoDnFbw&pullRequest=2866
return event.key.length >= 2 && event.key[0] === 'F' && !isNaN(event.key.slice(1));

Check warning on line 1594 in src/LiveDevelopment/BrowserScripts/RemoteFunctions.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer `Number.isNaN` over `isNaN`.

See more on https://sonarcloud.io/project/issues?id=phcode-dev_phoenix&issues=AZ3VVktVNd9f7yoDnFbx&open=AZ3VVktVNd9f7yoDnFbx&pullRequest=2866
}

function _forwardKeyEventToPhoenix(event) {
event.preventDefault();
event.stopImmediatePropagation();
MessageBroker.send({
keyForward: true,
key: event.key,
code: event.code,
ctrlKey: event.ctrlKey,
metaKey: event.metaKey,
shiftKey: event.shiftKey,
altKey: event.altKey
});
}

document.addEventListener('keydown', function(event) {
if (config.mode === 'edit' && (event.key === 'Escape' || event.key === 'Esc')) {
event.preventDefault();
_handleEscapeKeyPress();
}
});

// Forwarder for design mode: runs as late as we can manage in the standard
// event flow — bubble phase on `window` (latest target after document),
// and the listener itself is registered on `load` so it goes last among
// same-target listeners. This gives the previewed page's own handlers the
// best chance to call preventDefault (which we then honor) before we
// forward to Phoenix.
function _designModeKeyForwarder(event) {
if (!_isPhoenixEmbeddedIframe || !config.designMode) {
return;
}
if (event.defaultPrevented) {
return;
}
if (_isFunctionKey(event)) {
_forwardKeyEventToPhoenix(event);
return;
}
const isMod = event.metaKey || event.ctrlKey;
if (isMod && event.key && event.key.length === 1 && !_KEYS_NOT_FORWARDED[event.key]) {

Check warning on line 1636 in src/LiveDevelopment/BrowserScripts/RemoteFunctions.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer using an optional chain expression instead, as it's more concise and easier to read.

See more on https://sonarcloud.io/project/issues?id=phcode-dev_phoenix&issues=AZ3VVktVNd9f7yoDnFby&open=AZ3VVktVNd9f7yoDnFby&pullRequest=2866
_forwardKeyEventToPhoenix(event);
}
}

// we need to refresh the config once the load is completed
// this is important because messageBroker gets ready for use only when load fires
window.addEventListener('load', function() {
window.addEventListener('keydown', _designModeKeyForwarder);

Check warning on line 1644 in src/LiveDevelopment/BrowserScripts/RemoteFunctions.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer `globalThis` over `window`.

See more on https://sonarcloud.io/project/issues?id=phcode-dev_phoenix&issues=AZ3XtSmJzI5R9QBPCqyE&open=AZ3XtSmJzI5R9QBPCqyE&pullRequest=2866
MessageBroker.send({
requestConfigRefresh: true
});
Expand Down
32 changes: 30 additions & 2 deletions src/LiveDevelopment/MultiBrowserImpl/protocol/LiveDevProtocol.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@
HTMLInstrumentation = require("LiveDevelopment/MultiBrowserImpl/language/HTMLInstrumentation"),
StringUtils = require("utils/StringUtils"),
FileViewController = require("project/FileViewController"),
MainViewManager = require("view/MainViewManager");
MainViewManager = require("view/MainViewManager"),
WorkspaceManager = require("view/WorkspaceManager");

const LIVE_DEV_REMOTE_SCRIPTS_FILE_NAME = `phoenix_live_preview_scripts_instrumented_${StringUtils.randomString(8)}.js`;
const LIVE_DEV_REMOTE_WORKER_SCRIPTS_FILE_NAME = `pageLoaderWorker_${StringUtils.randomString(8)}.js`;
Expand Down Expand Up @@ -188,7 +189,30 @@
* set focus to editor
* @private
*/
/**
* Re-dispatches a keyboard shortcut originating from the live preview iframe
* onto document.body so KeyBindingManager picks it up. Needed because key
* events fired inside an iframe don't bubble to the parent's document.
* @private
*/
function _forwardKeyboardShortcutFromIframe(data) {

Check warning on line 198 in src/LiveDevelopment/MultiBrowserImpl/protocol/LiveDevProtocol.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Move function '_forwardKeyboardShortcutFromIframe' to the outer scope.

See more on https://sonarcloud.io/project/issues?id=phcode-dev_phoenix&issues=AZ3VVkoKNd9f7yoDnFbv&open=AZ3VVkoKNd9f7yoDnFbv&pullRequest=2866
const event = new KeyboardEvent("keydown", {
key: data.key,
code: data.code,
ctrlKey: !!data.ctrlKey,
metaKey: !!data.metaKey,
shiftKey: !!data.shiftKey,
altKey: !!data.altKey,
bubbles: true,
cancelable: true
});
document.body.dispatchEvent(event);
}

function _focusEditorIfNeeded(editor, tagName, contentEditable) {
if (WorkspaceManager.isInDesignMode()) {
return;
}
const focusShouldBeInLivePreview = ['INPUT', 'TEXTAREA'].includes(tagName) || contentEditable;
if(focusShouldBeInLivePreview){
return;
Expand Down Expand Up @@ -247,7 +271,9 @@
activeEditorPath = activeEditor ? activeEditor.document.file.fullPath : null,
activeFullEditorPath = activeFullEditor ? activeFullEditor.document.file.fullPath : null;
if(!liveDocPath){
activeEditor && activeEditor.focus(); // restore focus from live preview
if (activeEditor && !WorkspaceManager.isInDesignMode()) {
activeEditor.focus(); // restore focus from live preview
}
return;
}
const allOpenFileCount = MainViewManager.getWorkingSetSize(MainViewManager.ALL_PANES);
Expand Down Expand Up @@ -341,6 +367,8 @@
pendingHandler.deferred.resolve(msg);
}
}
} else if (msg.keyForward) {
_forwardKeyboardShortcutFromIframe(msg);
} else if (msg.clicked && msg.tagId) {
// While previewing an html file, and if css related file is active in the editor, then clicking on the
// live preview, here we set the cursor position in the css file. but this will also trigger a css
Expand Down
10 changes: 10 additions & 0 deletions src/LiveDevelopment/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ define(function main(require, exports, module) {
Strings = require("strings"),
ExtensionUtils = require("utils/ExtensionUtils"),
StringUtils = require("utils/StringUtils"),
WorkspaceManager = require("view/WorkspaceManager"),
EventDispatcher = require("utils/EventDispatcher");

const LIVE_PREVIEW_MODE = CONSTANTS.LIVE_PREVIEW_MODE,
Expand Down Expand Up @@ -260,13 +261,22 @@ define(function main(require, exports, module) {
return getCurrentMode() === LIVE_PREVIEW_MODE;
}

function _designModeChanged() {
const config = MultiBrowserLiveDev.getConfig();
config.designMode = WorkspaceManager.isInDesignMode();
MultiBrowserLiveDev.updateConfig(config);
}

/** Initialize LiveDevelopment */
AppInit.appReady(function () {
params.parse();
const config = Object.assign({}, defaultConfig, MultiBrowserLiveDev.getConfig());
config.mode = getCurrentMode();
config.designMode = WorkspaceManager.isInDesignMode();
MultiBrowserLiveDev.init(config);

WorkspaceManager.on(WorkspaceManager.EVENT_WORKSPACE_DESIGN_MODE_CHANGE, _designModeChanged);

_loadStyles();

// update styles for UI status
Expand Down
16 changes: 16 additions & 0 deletions src/command/CommandManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,18 @@
return this._options || {};
};

/**
* Returns true if the command opted in to running while the workspace is in
* design mode (editor collapsed). KeyBindingManager uses this to decide
* whether a keyboard shortcut should fire; commands that don't opt in are
* swallowed in design mode.
*
* @return {boolean}
*/
Command.prototype.isSupportedInDesignMode = function () {
return !!(this._options && this._options.supportsDesignMode);

Check warning on line 169 in src/command/CommandManager.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer using an optional chain expression instead, as it's more concise and easier to read.

See more on https://sonarcloud.io/project/issues?id=phcode-dev_phoenix&issues=AZ3VVkwzNd9f7yoDnFb0&open=AZ3VVkwzNd9f7yoDnFb0&pullRequest=2866
};

/**
* Sets enabled state of Command and dispatches "enabledStateChange"
* when the enabled state changes.
Expand Down Expand Up @@ -250,6 +262,10 @@
* event.sourceType(Eg. Ctrl-K) parameter.
* @param {string} options.htmlName If set, this will be displayed in ui menus instead of the name given.
* Example: `"Phoenix menu<i class='fa fa-car' style='margin-left: 4px;'></i>"`
* @param {boolean} options.supportsDesignMode If true, this command's keyboard shortcut will still fire when
* the workspace is in design mode. Commands that don't opt in are swallowed in design mode because the
* editor area is collapsed and most shortcuts are nonsensical there. Reserve this flag for commands that
* remain useful with no editor visible (file open/save/close, Quick Open, Find in Files, etc.).
*
* @return {?Command}
*/
Expand Down
14 changes: 13 additions & 1 deletion src/command/KeyBindingManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -947,14 +947,26 @@
*/
function _handleKey(key) {
if (_enabled && _keyMap[key]) {
let command = CommandManager.get(_keyMap[key].commandID);
// In design mode, when focus is inside the live preview iframe (the
// user is interacting with their previewed page), swallow shortcuts
// that didn't opt in via Command.isSupportedInDesignMode(). When
// focus is elsewhere in the Phoenix UI (e.g. AI chat textarea), let
// shortcuts behave normally — design mode shouldn't break Phoenix's
// own UI interactions.
const focusInIframe = document.activeElement &&
document.activeElement.tagName === 'IFRAME';

Check warning on line 958 in src/command/KeyBindingManager.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer using an optional chain expression instead, as it's more concise and easier to read.

See more on https://sonarcloud.io/project/issues?id=phcode-dev_phoenix&issues=AZ3VVkwXNd9f7yoDnFbz&open=AZ3VVkwXNd9f7yoDnFbz&pullRequest=2866
if (Phoenix.isInDesignMode() && focusInIframe &&
command && !command.isSupportedInDesignMode()) {
return true;
}
Metrics.countEvent(Metrics.EVENT_TYPE.KEYBOARD, "shortcut", key);
Metrics.countEvent(Metrics.EVENT_TYPE.KEYBOARD, "command", _keyMap[key].commandID);
logger.leaveTrail("Keyboard shortcut: " + key + " command: " + _keyMap[key].commandID);
// If there is a registered and enabled key event except the swallowed key events,
// we always mark the event as processed and return true.
// We don't want multiple behavior tied to the same key event. For Instance, in browser, if `ctrl-k`
// is not handled by quick edit, it will open browser url bar if we return false here(which is bad ux).
let command = CommandManager.get(_keyMap[key].commandID);
let eventDetails = undefined;
if(command._options.eventSource){
eventDetails = {
Expand Down
2 changes: 1 addition & 1 deletion src/command/KeyboardOverlayMode.js
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ define(function (require, exports, module) {

AppInit.appReady(function () {
CommandManager.register(Strings.CMD_KEYBOARD_NAV_OVERLAY,
Commands.CMD_KEYBOARD_NAV_UI_OVERLAY, startOverlayMode);
Commands.CMD_KEYBOARD_NAV_UI_OVERLAY, startOverlayMode, { supportsDesignMode: true });
const viewMenu = Menus.getMenu(Menus.AppMenuBar.VIEW_MENU);
viewMenu.addMenuItem(Commands.CMD_KEYBOARD_NAV_UI_OVERLAY, 'Ctrl-P',
Menus.AFTER, Commands.VIEW_TOGGLE_INSPECTION);
Expand Down
44 changes: 24 additions & 20 deletions src/document/DocumentCommandHandlers.js
Original file line number Diff line number Diff line change
Expand Up @@ -2405,47 +2405,51 @@ define(function (require, exports, module) {
exports.APP_QUIT_CANCELLED = APP_QUIT_CANCELLED;


// Commands allowlisted to fire while in design mode (editor collapsed) so the
// user can still open / save / close files without leaving design mode.
const _designModeOpts = { supportsDesignMode: true };

// Deprecated commands
CommandManager.register(Strings.CMD_ADD_TO_WORKING_SET, Commands.FILE_ADD_TO_WORKING_SET, handleFileAddToWorkingSet);
CommandManager.register(Strings.CMD_FILE_OPEN, Commands.FILE_OPEN, handleDocumentOpen);
CommandManager.register(Strings.CMD_FILE_OPEN, Commands.FILE_OPEN, handleDocumentOpen, _designModeOpts);

// New commands
CommandManager.register(Strings.CMD_ADD_TO_WORKING_SET, Commands.CMD_ADD_TO_WORKINGSET_AND_OPEN, handleFileAddToWorkingSetAndOpen);
CommandManager.register(Strings.CMD_FILE_OPEN, Commands.CMD_OPEN, handleFileOpen);
CommandManager.register(Strings.CMD_ADD_TO_WORKING_SET, Commands.CMD_ADD_TO_WORKINGSET_AND_OPEN, handleFileAddToWorkingSetAndOpen, _designModeOpts);
CommandManager.register(Strings.CMD_FILE_OPEN, Commands.CMD_OPEN, handleFileOpen, _designModeOpts);

// File Commands
CommandManager.register(Strings.CMD_FILE_NEW_UNTITLED, Commands.FILE_NEW_UNTITLED, handleFileNew);
CommandManager.register(Strings.CMD_FILE_NEW, Commands.FILE_NEW, handleFileNewInProject);
CommandManager.register(Strings.CMD_FILE_NEW_FOLDER, Commands.FILE_NEW_FOLDER, handleNewFolderInProject);
CommandManager.register(Strings.CMD_FILE_SAVE, Commands.FILE_SAVE, handleFileSave);
CommandManager.register(Strings.CMD_FILE_SAVE_ALL, Commands.FILE_SAVE_ALL, handleFileSaveAll);
CommandManager.register(Strings.CMD_FILE_SAVE_AS, Commands.FILE_SAVE_AS, handleFileSaveAs);
CommandManager.register(Strings.CMD_FILE_SAVE, Commands.FILE_SAVE, handleFileSave, _designModeOpts);
CommandManager.register(Strings.CMD_FILE_SAVE_ALL, Commands.FILE_SAVE_ALL, handleFileSaveAll, _designModeOpts);
CommandManager.register(Strings.CMD_FILE_SAVE_AS, Commands.FILE_SAVE_AS, handleFileSaveAs, _designModeOpts);
CommandManager.register(Strings.CMD_FILE_RENAME, Commands.FILE_RENAME, handleFileRename);
CommandManager.register(Strings.CMD_FILE_DELETE, Commands.FILE_DELETE, handleFileDelete);

// Close Commands
CommandManager.register(Strings.CMD_FILE_CLOSE, Commands.FILE_CLOSE, handleFileClose);
CommandManager.register(Strings.CMD_FILE_CLOSE_ALL, Commands.FILE_CLOSE_ALL, handleFileCloseAll);
CommandManager.register(Strings.CMD_FILE_CLOSE_LIST, Commands.FILE_CLOSE_LIST, handleFileCloseList);
CommandManager.register(Strings.CMD_REOPEN_CLOSED, Commands.FILE_REOPEN_CLOSED, handleReopenClosed);
CommandManager.register(Strings.CMD_FILE_CLOSE, Commands.FILE_CLOSE, handleFileClose, _designModeOpts);
CommandManager.register(Strings.CMD_FILE_CLOSE_ALL, Commands.FILE_CLOSE_ALL, handleFileCloseAll, _designModeOpts);
CommandManager.register(Strings.CMD_FILE_CLOSE_LIST, Commands.FILE_CLOSE_LIST, handleFileCloseList, _designModeOpts);
CommandManager.register(Strings.CMD_REOPEN_CLOSED, Commands.FILE_REOPEN_CLOSED, handleReopenClosed, _designModeOpts);

// Traversal
CommandManager.register(Strings.CMD_NEXT_DOC, Commands.NAVIGATE_NEXT_DOC, handleGoNextDoc);
CommandManager.register(Strings.CMD_PREV_DOC, Commands.NAVIGATE_PREV_DOC, handleGoPrevDoc);
CommandManager.register(Strings.CMD_NEXT_DOC, Commands.NAVIGATE_NEXT_DOC, handleGoNextDoc, _designModeOpts);
CommandManager.register(Strings.CMD_PREV_DOC, Commands.NAVIGATE_PREV_DOC, handleGoPrevDoc, _designModeOpts);

CommandManager.register(Strings.CMD_NEXT_DOC_LIST_ORDER, Commands.NAVIGATE_NEXT_DOC_LIST_ORDER, handleGoNextDocListOrder);
CommandManager.register(Strings.CMD_PREV_DOC_LIST_ORDER, Commands.NAVIGATE_PREV_DOC_LIST_ORDER, handleGoPrevDocListOrder);
CommandManager.register(Strings.CMD_NEXT_DOC_LIST_ORDER, Commands.NAVIGATE_NEXT_DOC_LIST_ORDER, handleGoNextDocListOrder, _designModeOpts);
CommandManager.register(Strings.CMD_PREV_DOC_LIST_ORDER, Commands.NAVIGATE_PREV_DOC_LIST_ORDER, handleGoPrevDocListOrder, _designModeOpts);

// Special Commands
CommandManager.register(showInOS, Commands.NAVIGATE_SHOW_IN_OS, handleShowInOS);
CommandManager.register(defaultTerminal, Commands.NAVIGATE_OPEN_IN_TERMINAL, openDefaultTerminal);
CommandManager.register(showInOS, Commands.NAVIGATE_SHOW_IN_OS, handleShowInOS, _designModeOpts);
CommandManager.register(defaultTerminal, Commands.NAVIGATE_OPEN_IN_TERMINAL, openDefaultTerminal, _designModeOpts);
if (brackets.platform === "win") {
CommandManager.register(Strings.CMD_OPEN_IN_POWER_SHELL, Commands.NAVIGATE_OPEN_IN_POWERSHELL, openPowerShell);
}
CommandManager.register(Strings.CMD_OPEN_IN_DEFAULT_APP, Commands.NAVIGATE_OPEN_IN_DEFAULT_APP, openDefaultApp);
CommandManager.register(Strings.CMD_NEW_BRACKETS_WINDOW, Commands.FILE_NEW_WINDOW, handleFileNewWindow);
CommandManager.register(quitString, Commands.FILE_QUIT, handleFileCloseWindow);
CommandManager.register(Strings.CMD_SHOW_IN_TREE, Commands.NAVIGATE_SHOW_IN_FILE_TREE, handleShowInTree);
CommandManager.register(Strings.CMD_OPEN_IN_DEFAULT_APP, Commands.NAVIGATE_OPEN_IN_DEFAULT_APP, openDefaultApp, _designModeOpts);
CommandManager.register(Strings.CMD_NEW_BRACKETS_WINDOW, Commands.FILE_NEW_WINDOW, handleFileNewWindow, _designModeOpts);
CommandManager.register(quitString, Commands.FILE_QUIT, handleFileCloseWindow, _designModeOpts);
CommandManager.register(Strings.CMD_SHOW_IN_TREE, Commands.NAVIGATE_SHOW_IN_FILE_TREE, handleShowInTree, _designModeOpts);

// These commands have no UI representation and are only used internally
CommandManager.registerInternal(Commands.APP_ABORT_QUIT, handleAbortQuit);
Expand Down
2 changes: 1 addition & 1 deletion src/extensibility/ExtensionManagerDialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -429,7 +429,7 @@ define(function (require, exports, module) {
return new $.Deferred().resolve(dialog).promise();
}

CommandManager.register(Strings.CMD_EXTENSION_MANAGER, Commands.FILE_EXTENSION_MANAGER, _showDialog);
CommandManager.register(Strings.CMD_EXTENSION_MANAGER, Commands.FILE_EXTENSION_MANAGER, _showDialog, { supportsDesignMode: true });

AppInit.appReady(function () {
$("#toolbar-extension-manager").click(_showDialog);
Expand Down
6 changes: 3 additions & 3 deletions src/extensions/default/CloseOthers/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -157,13 +157,13 @@ define(function (require, exports, module) {

CommandManager.register(Strings.CMD_FILE_CLOSE_BELOW, closeBelow, function () {
handleClose(closeBelow);
});
}, { supportsDesignMode: true });
CommandManager.register(Strings.CMD_FILE_CLOSE_OTHERS, closeOthers, function () {
handleClose(closeOthers);
});
}, { supportsDesignMode: true });
CommandManager.register(Strings.CMD_FILE_CLOSE_ABOVE, closeAbove, function () {
handleClose(closeAbove);
});
}, { supportsDesignMode: true });

if (prefs.closeBelow) {
workingSetListCmenu.addMenuItem(closeBelow, "", Menus.AFTER, Commands.FILE_CLOSE);
Expand Down
Loading
Loading