diff --git a/.github/actions/spell-check/allow/code.txt b/.github/actions/spell-check/allow/code.txt
index d312ae7d54..2ef0425846 100644
--- a/.github/actions/spell-check/allow/code.txt
+++ b/.github/actions/spell-check/allow/code.txt
@@ -288,3 +288,6 @@ CACHEWRITE
MRUCMPPROC
MRUINFO
REGSTR
+
+# Misc Win32 APIs and PInvokes
+INVOKEIDLIST
\ No newline at end of file
diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Commands/ExecuteActionCommand.cs b/src/modules/cmdpal/Microsoft.CmdPal.Common/Commands/ExecuteActionCommand.cs
similarity index 89%
rename from src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Commands/ExecuteActionCommand.cs
rename to src/modules/cmdpal/Microsoft.CmdPal.Common/Commands/ExecuteActionCommand.cs
index 2a3c895e9b..bf523d5792 100644
--- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Commands/ExecuteActionCommand.cs
+++ b/src/modules/cmdpal/Microsoft.CmdPal.Common/Commands/ExecuteActionCommand.cs
@@ -9,11 +9,11 @@ using Windows.AI.Actions.Hosting;
namespace Microsoft.CmdPal.Ext.Indexer.Commands;
-internal sealed partial class ExecuteActionCommand : InvokableCommand
+public sealed partial class ExecuteActionCommand : InvokableCommand
{
private readonly ActionInstance actionInstance;
- internal ExecuteActionCommand(ActionInstance actionInstance)
+ public ExecuteActionCommand(ActionInstance actionInstance)
{
this.actionInstance = actionInstance;
this.Name = actionInstance.DisplayInfo.Description;
diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Commands/OpenInConsoleCommand.cs b/src/modules/cmdpal/Microsoft.CmdPal.Common/Commands/OpenInConsoleCommand.cs
similarity index 61%
rename from src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Commands/OpenInConsoleCommand.cs
rename to src/modules/cmdpal/Microsoft.CmdPal.Common/Commands/OpenInConsoleCommand.cs
index cd9c5ce94b..37b82422d0 100644
--- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Commands/OpenInConsoleCommand.cs
+++ b/src/modules/cmdpal/Microsoft.CmdPal.Common/Commands/OpenInConsoleCommand.cs
@@ -6,28 +6,29 @@ using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using ManagedCommon;
-using Microsoft.CmdPal.Ext.Indexer.Data;
-using Microsoft.CmdPal.Ext.Indexer.Properties;
+using Microsoft.CmdPal.Common.Properties;
using Microsoft.CommandPalette.Extensions.Toolkit;
-namespace Microsoft.CmdPal.Ext.Indexer.Commands;
+namespace Microsoft.CmdPal.Common.Commands;
-internal sealed partial class OpenInConsoleCommand : InvokableCommand
+public partial class OpenInConsoleCommand : InvokableCommand
{
- private readonly IndexerItem _item;
+ internal static IconInfo OpenInConsoleIcon { get; } = new("\uE756");
- internal OpenInConsoleCommand(IndexerItem item)
+ private readonly string _path;
+
+ public OpenInConsoleCommand(string fullPath)
{
- this._item = item;
+ this._path = fullPath;
this.Name = Resources.Indexer_Command_OpenPathInConsole;
- this.Icon = new IconInfo("\uE756");
+ this.Icon = OpenInConsoleIcon;
}
public override CommandResult Invoke()
{
using (var process = new Process())
{
- process.StartInfo.WorkingDirectory = Path.GetDirectoryName(_item.FullPath);
+ process.StartInfo.WorkingDirectory = Path.GetDirectoryName(_path);
process.StartInfo.FileName = "cmd.exe";
try
@@ -36,10 +37,10 @@ internal sealed partial class OpenInConsoleCommand : InvokableCommand
}
catch (Win32Exception ex)
{
- Logger.LogError($"Unable to open {_item.FullPath}", ex);
+ Logger.LogError($"Unable to open '{_path}'", ex);
}
}
- return CommandResult.GoHome();
+ return CommandResult.Dismiss();
}
}
diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Commands/OpenPropertiesCommand.cs b/src/modules/cmdpal/Microsoft.CmdPal.Common/Commands/OpenPropertiesCommand.cs
similarity index 70%
rename from src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Commands/OpenPropertiesCommand.cs
rename to src/modules/cmdpal/Microsoft.CmdPal.Common/Commands/OpenPropertiesCommand.cs
index d07bbdca80..b4833dc913 100644
--- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Commands/OpenPropertiesCommand.cs
+++ b/src/modules/cmdpal/Microsoft.CmdPal.Common/Commands/OpenPropertiesCommand.cs
@@ -6,17 +6,17 @@ using System;
using System.Runtime.InteropServices;
using ManagedCommon;
using ManagedCsWin32;
-using Microsoft.CmdPal.Ext.Indexer.Data;
-using Microsoft.CmdPal.Ext.Indexer.Indexer.Utils;
-using Microsoft.CmdPal.Ext.Indexer.Properties;
+using Microsoft.CmdPal.Common.Properties;
using Microsoft.CommandPalette.Extensions.Toolkit;
using Windows.Win32.UI.WindowsAndMessaging;
-namespace Microsoft.CmdPal.Ext.Indexer.Commands;
+namespace Microsoft.CmdPal.Common.Commands;
-internal sealed partial class OpenPropertiesCommand : InvokableCommand
+public partial class OpenPropertiesCommand : InvokableCommand
{
- private readonly IndexerItem _item;
+ internal static IconInfo OpenPropertiesIcon { get; } = new("\uE90F");
+
+ private readonly string _path;
private static unsafe bool ShowFileProperties(string filename)
{
@@ -31,7 +31,7 @@ internal sealed partial class OpenPropertiesCommand : InvokableCommand
LpVerb = propertiesPtr,
LpFile = filenamePtr,
Show = (int)SHOW_WINDOW_CMD.SW_SHOW,
- FMask = NativeHelpers.SEEMASKINVOKEIDLIST,
+ FMask = global::Windows.Win32.PInvoke.SEE_MASK_INVOKEIDLIST,
};
return Shell32.ShellExecuteEx(ref info);
@@ -43,24 +43,24 @@ internal sealed partial class OpenPropertiesCommand : InvokableCommand
}
}
- internal OpenPropertiesCommand(IndexerItem item)
+ public OpenPropertiesCommand(string fullPath)
{
- this._item = item;
+ this._path = fullPath;
this.Name = Resources.Indexer_Command_OpenProperties;
- this.Icon = new IconInfo("\uE90F");
+ this.Icon = OpenPropertiesIcon;
}
public override CommandResult Invoke()
{
try
{
- ShowFileProperties(_item.FullPath);
+ ShowFileProperties(_path);
}
catch (Exception ex)
{
Logger.LogError("Error showing file properties: ", ex);
}
- return CommandResult.GoHome();
+ return CommandResult.Dismiss();
}
}
diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Commands/OpenWithCommand.cs b/src/modules/cmdpal/Microsoft.CmdPal.Common/Commands/OpenWithCommand.cs
similarity index 70%
rename from src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Commands/OpenWithCommand.cs
rename to src/modules/cmdpal/Microsoft.CmdPal.Common/Commands/OpenWithCommand.cs
index 2c1875d3d7..33bd83a20c 100644
--- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Commands/OpenWithCommand.cs
+++ b/src/modules/cmdpal/Microsoft.CmdPal.Common/Commands/OpenWithCommand.cs
@@ -4,17 +4,17 @@
using System.Runtime.InteropServices;
using ManagedCsWin32;
-using Microsoft.CmdPal.Ext.Indexer.Data;
-using Microsoft.CmdPal.Ext.Indexer.Indexer.Utils;
-using Microsoft.CmdPal.Ext.Indexer.Properties;
+using Microsoft.CmdPal.Common.Properties;
using Microsoft.CommandPalette.Extensions.Toolkit;
using Windows.Win32.UI.WindowsAndMessaging;
-namespace Microsoft.CmdPal.Ext.Indexer.Commands;
+namespace Microsoft.CmdPal.Common.Commands;
-internal sealed partial class OpenWithCommand : InvokableCommand
+public partial class OpenWithCommand : InvokableCommand
{
- private readonly IndexerItem _item;
+ internal static IconInfo OpenWithIcon { get; } = new("\uE7AC");
+
+ private readonly string _path;
private static unsafe bool OpenWith(string filename)
{
@@ -29,7 +29,7 @@ internal sealed partial class OpenWithCommand : InvokableCommand
LpVerb = verbPtr,
LpFile = filenamePtr,
Show = (int)SHOW_WINDOW_CMD.SW_SHOWNORMAL,
- FMask = NativeHelpers.SEEMASKINVOKEIDLIST,
+ FMask = global::Windows.Win32.PInvoke.SEE_MASK_INVOKEIDLIST,
};
return Shell32.ShellExecuteEx(ref info);
@@ -41,16 +41,16 @@ internal sealed partial class OpenWithCommand : InvokableCommand
}
}
- internal OpenWithCommand(IndexerItem item)
+ public OpenWithCommand(string fullPath)
{
- this._item = item;
+ this._path = fullPath;
this.Name = Resources.Indexer_Command_OpenWith;
- this.Icon = new IconInfo("\uE7AC");
+ this.Icon = OpenWithIcon;
}
public override CommandResult Invoke()
{
- OpenWith(_item.FullPath);
+ OpenWith(_path);
return CommandResult.GoHome();
}
diff --git a/src/modules/cmdpal/Microsoft.CmdPal.Common/Microsoft.CmdPal.Common.csproj b/src/modules/cmdpal/Microsoft.CmdPal.Common/Microsoft.CmdPal.Common.csproj
index 0112da1b0b..27509d0e5b 100644
--- a/src/modules/cmdpal/Microsoft.CmdPal.Common/Microsoft.CmdPal.Common.csproj
+++ b/src/modules/cmdpal/Microsoft.CmdPal.Common/Microsoft.CmdPal.Common.csproj
@@ -28,7 +28,24 @@
+
+
+
+
+ True
+ True
+ Resources.resx
+
+
+
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+
diff --git a/src/modules/cmdpal/Microsoft.CmdPal.Common/NativeMethods.txt b/src/modules/cmdpal/Microsoft.CmdPal.Common/NativeMethods.txt
index 996bbd7153..61e89b68c4 100644
--- a/src/modules/cmdpal/Microsoft.CmdPal.Common/NativeMethods.txt
+++ b/src/modules/cmdpal/Microsoft.CmdPal.Common/NativeMethods.txt
@@ -9,3 +9,7 @@ GetWindowRect
GetMonitorInfo
SetWindowPos
MonitorFromWindow
+
+SHOW_WINDOW_CMD
+ShellExecuteEx
+SEE_MASK_INVOKEIDLIST
\ No newline at end of file
diff --git a/src/modules/cmdpal/Microsoft.CmdPal.Common/Properties/Resources.Designer.cs b/src/modules/cmdpal/Microsoft.CmdPal.Common/Properties/Resources.Designer.cs
new file mode 100644
index 0000000000..c2f81dd683
--- /dev/null
+++ b/src/modules/cmdpal/Microsoft.CmdPal.Common/Properties/Resources.Designer.cs
@@ -0,0 +1,99 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace Microsoft.CmdPal.Common.Properties {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.CmdPal.Common.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Open path in console.
+ ///
+ internal static string Indexer_Command_OpenPathInConsole {
+ get {
+ return ResourceManager.GetString("Indexer_Command_OpenPathInConsole", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Properties.
+ ///
+ internal static string Indexer_Command_OpenProperties {
+ get {
+ return ResourceManager.GetString("Indexer_Command_OpenProperties", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Open with.
+ ///
+ internal static string Indexer_Command_OpenWith {
+ get {
+ return ResourceManager.GetString("Indexer_Command_OpenWith", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Show in folder.
+ ///
+ internal static string Indexer_Command_ShowInFolder {
+ get {
+ return ResourceManager.GetString("Indexer_Command_ShowInFolder", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/src/modules/cmdpal/Microsoft.CmdPal.Common/Properties/Resources.resx b/src/modules/cmdpal/Microsoft.CmdPal.Common/Properties/Resources.resx
new file mode 100644
index 0000000000..14e62fb4c2
--- /dev/null
+++ b/src/modules/cmdpal/Microsoft.CmdPal.Common/Properties/Resources.resx
@@ -0,0 +1,132 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Open path in console
+
+
+ Properties
+
+
+ Open with
+
+
+ Show in folder
+
+
\ No newline at end of file
diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/UWPApplication.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/UWPApplication.cs
index c5270b355c..5c689545bd 100644
--- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/UWPApplication.cs
+++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/UWPApplication.cs
@@ -10,7 +10,6 @@ using System.Xml;
using ManagedCommon;
using Microsoft.CmdPal.Ext.Apps.Commands;
using Microsoft.CmdPal.Ext.Apps.Properties;
-using Microsoft.CmdPal.Ext.Apps.State;
using Microsoft.CmdPal.Ext.Apps.Utils;
using Microsoft.CommandPalette.Extensions;
using Microsoft.CommandPalette.Extensions.Toolkit;
@@ -96,7 +95,7 @@ public class UWPApplication : IProgram
commands.Add(
new CommandContextItem(
- new CopyPathCommand(Location))
+ new Commands.CopyPathCommand(Location))
{
RequestedShortcut = KeyChordHelpers.FromModifiers(ctrl: true, shift: true, vkey: VirtualKey.C),
});
diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/Win32Program.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/Win32Program.cs
index d8bebcd9a4..74819d87c7 100644
--- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/Win32Program.cs
+++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/Win32Program.cs
@@ -5,21 +5,15 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
-using System.ComponentModel.Design;
-using System.Diagnostics;
-using System.Globalization;
using System.IO;
using System.IO.Abstractions;
using System.Linq;
-using System.Reflection;
using System.Security;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
-using System.Windows.Input;
using ManagedCommon;
using Microsoft.CmdPal.Ext.Apps.Commands;
using Microsoft.CmdPal.Ext.Apps.Properties;
-using Microsoft.CmdPal.Ext.Apps.State;
using Microsoft.CmdPal.Ext.Apps.Utils;
using Microsoft.CommandPalette.Extensions;
using Microsoft.CommandPalette.Extensions.Toolkit;
@@ -190,7 +184,7 @@ public class Win32Program : IProgram
public List GetCommands()
{
- List commands = new List();
+ List commands = [];
if (AppType != ApplicationType.InternetShortcutApplication && AppType != ApplicationType.Folder && AppType != ApplicationType.GenericFile)
{
@@ -208,7 +202,7 @@ public class Win32Program : IProgram
}
commands.Add(new CommandContextItem(
- new CopyPathCommand(FullPath))
+ new Commands.CopyPathCommand(FullPath))
{
RequestedShortcut = KeyChordHelpers.FromModifiers(ctrl: true, shift: true, vkey: VirtualKey.C),
});
diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Commands/CopyPathCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Commands/CopyPathCommand.cs
deleted file mode 100644
index 77338e5d45..0000000000
--- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Commands/CopyPathCommand.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright (c) Microsoft Corporation
-// The Microsoft Corporation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using Microsoft.CmdPal.Ext.Indexer.Data;
-using Microsoft.CmdPal.Ext.Indexer.Properties;
-using Microsoft.CommandPalette.Extensions.Toolkit;
-
-namespace Microsoft.CmdPal.Ext.Indexer.Commands;
-
-internal sealed partial class CopyPathCommand : InvokableCommand
-{
- private readonly IndexerItem _item;
-
- internal CopyPathCommand(IndexerItem item)
- {
- this._item = item;
- this.Name = Resources.Indexer_Command_CopyPath;
- this.Icon = new IconInfo("\uE8c8");
- }
-
- public override CommandResult Invoke()
- {
- try
- {
- ClipboardHelper.SetText(_item.FullPath);
- }
- catch
- {
- }
-
- return CommandResult.KeepOpen();
- }
-}
diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Commands/OpenFileCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Commands/OpenFileCommand.cs
deleted file mode 100644
index 9d48c64376..0000000000
--- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Commands/OpenFileCommand.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright (c) Microsoft Corporation
-// The Microsoft Corporation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.ComponentModel;
-using System.Diagnostics;
-using ManagedCommon;
-using Microsoft.CmdPal.Ext.Indexer.Data;
-using Microsoft.CmdPal.Ext.Indexer.Properties;
-using Microsoft.CommandPalette.Extensions.Toolkit;
-
-namespace Microsoft.CmdPal.Ext.Indexer.Commands;
-
-internal sealed partial class OpenFileCommand : InvokableCommand
-{
- private readonly IndexerItem _item;
-
- internal OpenFileCommand(IndexerItem item)
- {
- this._item = item;
- this.Name = Resources.Indexer_Command_OpenFile;
- this.Icon = Icons.OpenFileIcon;
- }
-
- public override CommandResult Invoke()
- {
- using (var process = new Process())
- {
- process.StartInfo.FileName = _item.FullPath;
- process.StartInfo.UseShellExecute = true;
-
- try
- {
- process.Start();
- }
- catch (Win32Exception ex)
- {
- Logger.LogError($"Unable to open {_item.FullPath}", ex);
- }
- }
-
- return CommandResult.GoHome();
- }
-}
diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Data/IndexerItem.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Data/IndexerItem.cs
index 5b65cc9ef8..00222149f9 100644
--- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Data/IndexerItem.cs
+++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Data/IndexerItem.cs
@@ -12,6 +12,16 @@ internal sealed class IndexerItem
internal string FileName { get; init; }
+ internal IndexerItem()
+ {
+ }
+
+ internal IndexerItem(string fullPath)
+ {
+ FullPath = fullPath;
+ FileName = Path.GetFileName(fullPath);
+ }
+
internal bool IsDirectory()
{
if (!Path.Exists(FullPath))
diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Data/IndexerListItem.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Data/IndexerListItem.cs
index cd56f1c624..6cf0165e57 100644
--- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Data/IndexerListItem.cs
+++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Data/IndexerListItem.cs
@@ -3,10 +3,11 @@
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
-using Microsoft.CmdPal.Ext.Indexer.Commands;
+using System.IO;
+using System.Linq;
+using Microsoft.CmdPal.Common.Commands;
using Microsoft.CmdPal.Ext.Indexer.Pages;
using Microsoft.CmdPal.Ext.Indexer.Properties;
-using Microsoft.CommandPalette.Extensions;
using Microsoft.CommandPalette.Extensions.Toolkit;
using Windows.Foundation.Metadata;
@@ -28,51 +29,79 @@ internal sealed partial class IndexerListItem : ListItem
public IndexerListItem(
IndexerItem indexerItem,
IncludeBrowseCommand browseByDefault = IncludeBrowseCommand.Include)
- : base(new OpenFileCommand(indexerItem))
+ : base()
{
FilePath = indexerItem.FullPath;
Title = indexerItem.FileName;
Subtitle = indexerItem.FullPath;
- List context = [];
- if (indexerItem.IsDirectory())
+
+ var commands = FileCommands(indexerItem.FullPath, browseByDefault);
+ if (commands.Any())
{
- var directoryPage = new DirectoryPage(indexerItem.FullPath);
+ Command = commands.First().Command;
+ MoreCommands = commands.Skip(1).ToArray();
+ }
+ }
+
+ public static IEnumerable FileCommands(string fullPath)
+ {
+ return FileCommands(fullPath, IncludeBrowseCommand.Include);
+ }
+
+ internal static IEnumerable FileCommands(
+ string fullPath,
+ IncludeBrowseCommand browseByDefault = IncludeBrowseCommand.Include)
+ {
+ List commands = [];
+ if (!Path.Exists(fullPath))
+ {
+ return commands;
+ }
+
+ // detect whether it is a directory or file
+ var attr = File.GetAttributes(fullPath);
+ var isDir = (attr & FileAttributes.Directory) == FileAttributes.Directory;
+
+ var openCommand = new OpenFileCommand(fullPath) { Name = Resources.Indexer_Command_OpenFile };
+ if (isDir)
+ {
+ var directoryPage = new DirectoryPage(fullPath);
if (browseByDefault == IncludeBrowseCommand.AsDefault)
{
- // Swap the open file command into the context menu
- context.Add(new CommandContextItem(Command));
- Command = directoryPage;
+ // AsDefault: browse dir first, then open in explorer
+ commands.Add(new CommandContextItem(directoryPage));
+ commands.Add(new CommandContextItem(openCommand));
}
else if (browseByDefault == IncludeBrowseCommand.Include)
{
- context.Add(new CommandContextItem(directoryPage));
+ // AsDefault: open in explorer first, then browse
+ commands.Add(new CommandContextItem(openCommand));
+ commands.Add(new CommandContextItem(directoryPage));
+ }
+ else if (browseByDefault == IncludeBrowseCommand.Exclude)
+ {
+ // AsDefault: Just open in explorer
+ commands.Add(new CommandContextItem(openCommand));
}
}
- IContextItem[] moreCommands = [
- ..context,
- new CommandContextItem(new OpenWithCommand(indexerItem))];
+ commands.Add(new CommandContextItem(new OpenWithCommand(fullPath)));
+ commands.Add(new CommandContextItem(new ShowFileInFolderCommand(fullPath) { Name = Resources.Indexer_Command_ShowInFolder }));
+ commands.Add(new CommandContextItem(new CopyPathCommand(fullPath) { Name = Resources.Indexer_Command_CopyPath }));
+ commands.Add(new CommandContextItem(new OpenInConsoleCommand(fullPath)));
+ commands.Add(new CommandContextItem(new OpenPropertiesCommand(fullPath)));
if (IsActionsFeatureEnabled && ApiInformation.IsApiContractPresent("Windows.AI.Actions.ActionsContract", 4))
{
- var actionsListContextItem = new ActionsListContextItem(indexerItem.FullPath);
+ var actionsListContextItem = new ActionsListContextItem(fullPath);
if (actionsListContextItem.AnyActions())
{
- moreCommands = [
- .. moreCommands,
- actionsListContextItem
- ];
+ commands.Add(actionsListContextItem);
}
}
- MoreCommands = [
- .. moreCommands,
- new CommandContextItem(new ShowFileInFolderCommand(indexerItem.FullPath) { Name = Resources.Indexer_Command_ShowInFolder }),
- new CommandContextItem(new CopyPathCommand(indexerItem)),
- new CommandContextItem(new OpenInConsoleCommand(indexerItem)),
- new CommandContextItem(new OpenPropertiesCommand(indexerItem)),
- ];
+ return commands;
}
}
diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/FallbackOpenFileItem.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/FallbackOpenFileItem.cs
index 88c4f77cc2..7da8702f1e 100644
--- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/FallbackOpenFileItem.cs
+++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/FallbackOpenFileItem.cs
@@ -60,7 +60,7 @@ internal sealed partial class FallbackOpenFileItem : FallbackCommandItem, System
if (Path.Exists(query))
{
// Exit 1: The query is a direct path to a file. Great! Return it.
- var item = new IndexerItem() { FullPath = query, FileName = Path.GetFileName(query) };
+ var item = new IndexerItem(fullPath: query);
var listItemForUs = new IndexerListItem(item, IncludeBrowseCommand.AsDefault);
Command = listItemForUs.Command;
MoreCommands = listItemForUs.MoreCommands;
diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Microsoft.CmdPal.Ext.Indexer.csproj b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Microsoft.CmdPal.Ext.Indexer.csproj
index af8fcff41a..5bf4255308 100644
--- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Microsoft.CmdPal.Ext.Indexer.csproj
+++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Microsoft.CmdPal.Ext.Indexer.csproj
@@ -10,14 +10,15 @@
-
- all
- runtime; build; native; contentfiles; analyzers
-
-
-
+
+ all
+ runtime; build; native; contentfiles; analyzers
+
+
+
+
-
+
diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Pages/ExploreListItem.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Pages/ExploreListItem.cs
index 501e2b96f3..b440644dd7 100644
--- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Pages/ExploreListItem.cs
+++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Pages/ExploreListItem.cs
@@ -3,7 +3,7 @@
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
-using Microsoft.CmdPal.Ext.Indexer.Commands;
+using Microsoft.CmdPal.Common.Commands;
using Microsoft.CmdPal.Ext.Indexer.Data;
using Microsoft.CmdPal.Ext.Indexer.Properties;
using Microsoft.CommandPalette.Extensions.Toolkit;
@@ -41,16 +41,16 @@ internal sealed partial class ExploreListItem : ListItem
}
else
{
- Command = new OpenFileCommand(indexerItem);
+ Command = new OpenFileCommand(indexerItem.FullPath);
}
MoreCommands = [
..context,
- new CommandContextItem(new OpenWithCommand(indexerItem)),
+ new CommandContextItem(new OpenWithCommand(indexerItem.FullPath)),
new CommandContextItem(new ShowFileInFolderCommand(indexerItem.FullPath) { Name = Resources.Indexer_Command_ShowInFolder }),
- new CommandContextItem(new CopyPathCommand(indexerItem)),
- new CommandContextItem(new OpenInConsoleCommand(indexerItem)),
- new CommandContextItem(new OpenPropertiesCommand(indexerItem)),
+ new CommandContextItem(new CopyPathCommand(indexerItem.FullPath)),
+ new CommandContextItem(new OpenInConsoleCommand(indexerItem.FullPath)),
+ new CommandContextItem(new OpenPropertiesCommand(indexerItem.FullPath)),
];
}
}
diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/Microsoft.CmdPal.Ext.Shell.csproj b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/Microsoft.CmdPal.Ext.Shell.csproj
index c1792064f9..b1454d826e 100644
--- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/Microsoft.CmdPal.Ext.Shell.csproj
+++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/Microsoft.CmdPal.Ext.Shell.csproj
@@ -13,6 +13,7 @@
+
diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/Pages/RunExeItem.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/Pages/RunExeItem.cs
index 679607e312..ea98f2fe47 100644
--- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/Pages/RunExeItem.cs
+++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/Pages/RunExeItem.cs
@@ -7,6 +7,7 @@ using System.Threading.Tasks;
using Microsoft.CommandPalette.Extensions;
using Microsoft.CommandPalette.Extensions.Toolkit;
using Windows.Storage.Streams;
+using Windows.System;
namespace Microsoft.CmdPal.Ext.Shell.Pages;
@@ -54,13 +55,13 @@ internal sealed partial class RunExeItem : ListItem
{
Name = Properties.Resources.cmd_run_as_administrator,
Icon = Icons.AdminIcon,
- }),
+ }) { RequestedShortcut = KeyChordHelpers.FromModifiers(ctrl: true, shift: true, vkey: VirtualKey.Enter) },
new CommandContextItem(
new AnonymousCommand(RunAsOther)
{
Name = Properties.Resources.cmd_run_as_user,
Icon = Icons.UserIcon,
- }),
+ }) { RequestedShortcut = KeyChordHelpers.FromModifiers(ctrl: true, shift: true, vkey: VirtualKey.U) },
];
}
diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/Pages/ShellListPage.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/Pages/ShellListPage.cs
index c3b5a66bf2..8ecfe091c1 100644
--- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/Pages/ShellListPage.cs
+++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/Pages/ShellListPage.cs
@@ -26,7 +26,7 @@ internal sealed partial class ShellListPage : DynamicListPage, IDisposable
private readonly IRunHistoryService _historyService;
- private RunExeItem? _exeItem;
+ private ListItem? _exeItem;
private List _pathItems = [];
private ListItem? _uriItem;
@@ -319,20 +319,19 @@ internal sealed partial class ShellListPage : DynamicListPage, IDisposable
.ToArray();
}
- internal static RunExeItem CreateExeItem(string exe, string args, string fullExePath, Action? addToHistory)
+ internal static ListItem CreateExeItem(string exe, string args, string fullExePath, Action? addToHistory)
{
// PathToListItem will return a RunExeItem if it can find a executable.
// It will ALSO add the file search commands to the RunExeItem.
- return PathToListItem(fullExePath, exe, args, addToHistory) as RunExeItem ??
- new RunExeItem(exe, args, fullExePath, addToHistory);
+ return PathToListItem(fullExePath, exe, args, addToHistory);
}
private void CreateAndAddExeItems(string exe, string args, string fullExePath)
{
// If we already have an exe item, and the exe is the same, we can just update it
- if (_exeItem != null && _exeItem.FullExePath.Equals(fullExePath, StringComparison.OrdinalIgnoreCase))
+ if (_exeItem is RunExeItem exeItem && exeItem.FullExePath.Equals(fullExePath, StringComparison.OrdinalIgnoreCase))
{
- _exeItem.UpdateArgs(args);
+ exeItem.UpdateArgs(args);
}
else
{
@@ -345,7 +344,8 @@ internal sealed partial class ShellListPage : DynamicListPage, IDisposable
// Is this path an executable?
// check all the extensions in PATHEXT
var extensions = Environment.GetEnvironmentVariable("PATHEXT")?.Split(';') ?? Array.Empty();
- return extensions.Any(ext => string.Equals(Path.GetExtension(path), ext, StringComparison.OrdinalIgnoreCase));
+ var extension = Path.GetExtension(path);
+ return string.IsNullOrEmpty(extension) || extensions.Any(ext => string.Equals(extension, ext, StringComparison.OrdinalIgnoreCase));
}
private async Task CreatePathItemsAsync(string searchPath, string originalPath, CancellationToken cancellationToken)
diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/PathListItem.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/PathListItem.cs
index 0ecec3cb2d..2e1dd38349 100644
--- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/PathListItem.cs
+++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/PathListItem.cs
@@ -4,8 +4,10 @@
using System;
using System.IO;
+using Microsoft.CmdPal.Common.Commands;
using Microsoft.CommandPalette.Extensions;
using Microsoft.CommandPalette.Extensions.Toolkit;
+using Windows.System;
namespace Microsoft.CmdPal.Ext.Shell;
@@ -58,18 +60,15 @@ internal sealed partial class PathListItem : ListItem
}
TextToSuggest = suggestion;
- MoreCommands = [
- new CommandContextItem(new CopyTextCommand(path) { Name = Properties.Resources.copy_path_command_name }) { }
- ];
- // TODO: Follow-up during 0.4. Add the indexer commands here.
- // MoreCommands = [
- // new CommandContextItem(new OpenWithCommand(indexerItem)),
- // new CommandContextItem(new ShowFileInFolderCommand(indexerItem.FullPath) { Name = Resources.Indexer_Command_ShowInFolder }),
- // new CommandContextItem(new CopyPathCommand(indexerItem)),
- // new CommandContextItem(new OpenInConsoleCommand(indexerItem)),
- // new CommandContextItem(new OpenPropertiesCommand(indexerItem)),
- // ];
+ MoreCommands = [
+ new CommandContextItem(new OpenWithCommand(path)),
+ new CommandContextItem(new ShowFileInFolderCommand(path)) { RequestedShortcut = KeyChordHelpers.FromModifiers(ctrl: true, shift: true, vkey: VirtualKey.E) },
+ new CommandContextItem(new CopyPathCommand(path) { Name = Properties.Resources.copy_path_command_name }) { RequestedShortcut = KeyChordHelpers.FromModifiers(ctrl: true, shift: true, vkey: VirtualKey.C) },
+ new CommandContextItem(new OpenInConsoleCommand(path)) { RequestedShortcut = KeyChordHelpers.FromModifiers(ctrl: true, shift: true, vkey: VirtualKey.R) },
+ new CommandContextItem(new OpenPropertiesCommand(path)),
+ ];
+
_icon = new Lazy(() =>
{
var iconStream = ThumbnailHelper.GetThumbnail(path).Result;
diff --git a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/Commands/CopyPathCommand.cs b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/Commands/CopyPathCommand.cs
new file mode 100644
index 0000000000..6ace309bd1
--- /dev/null
+++ b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/Commands/CopyPathCommand.cs
@@ -0,0 +1,36 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.CommandPalette.Extensions.Toolkit.Properties;
+
+namespace Microsoft.CommandPalette.Extensions.Toolkit;
+
+public partial class CopyPathCommand : InvokableCommand
+{
+ internal static IconInfo CopyPath { get; } = new("\uE8c8"); // Copy
+
+ private readonly string _path;
+
+ public CommandResult Result { get; set; } = CommandResult.ShowToast(Resources.CopyPathTextCommand_Result);
+
+ public CopyPathCommand(string fullPath)
+ {
+ this._path = fullPath;
+ this.Name = Resources.CopyPathTextCommand_Name;
+ this.Icon = CopyPath;
+ }
+
+ public override CommandResult Invoke()
+ {
+ try
+ {
+ ClipboardHelper.SetText(_path);
+ }
+ catch
+ {
+ }
+
+ return Result;
+ }
+}
diff --git a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/CopyTextCommand.cs b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/Commands/CopyTextCommand.cs
similarity index 100%
rename from src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/CopyTextCommand.cs
rename to src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/Commands/CopyTextCommand.cs
diff --git a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/NoOpCommand.cs b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/Commands/NoOpCommand.cs
similarity index 100%
rename from src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/NoOpCommand.cs
rename to src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/Commands/NoOpCommand.cs
diff --git a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/Commands/OpenFileCommand.cs b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/Commands/OpenFileCommand.cs
new file mode 100644
index 0000000000..fff7950f3d
--- /dev/null
+++ b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/Commands/OpenFileCommand.cs
@@ -0,0 +1,44 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.ComponentModel;
+using System.Diagnostics;
+
+namespace Microsoft.CommandPalette.Extensions.Toolkit;
+
+public partial class OpenFileCommand : InvokableCommand
+{
+ internal static IconInfo OpenFile { get; } = new("\uE8E5"); // OpenFile
+
+ private readonly string _fullPath;
+
+ public CommandResult Result { get; set; } = CommandResult.Dismiss();
+
+ public OpenFileCommand(string fullPath)
+ {
+ this._fullPath = fullPath;
+ this.Name = "Open";
+ this.Icon = OpenFile;
+ }
+
+ public override CommandResult Invoke()
+ {
+ using (var process = new Process())
+ {
+ process.StartInfo.FileName = _fullPath;
+ process.StartInfo.UseShellExecute = true;
+
+ try
+ {
+ process.Start();
+ }
+ catch (Win32Exception ex)
+ {
+ ExtensionHost.LogMessage($"Unable to open {_fullPath}\n{ex}");
+ }
+ }
+
+ return Result;
+ }
+}
diff --git a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/OpenUrlCommand.cs b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/Commands/OpenUrlCommand.cs
similarity index 100%
rename from src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/OpenUrlCommand.cs
rename to src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/Commands/OpenUrlCommand.cs
diff --git a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/ShowFileInFolderCommand.cs b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/Commands/ShowFileInFolderCommand.cs
similarity index 100%
rename from src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/ShowFileInFolderCommand.cs
rename to src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/Commands/ShowFileInFolderCommand.cs
diff --git a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/NativeMethods.txt b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/NativeMethods.txt
index 942650356e..c48ffb158a 100644
--- a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/NativeMethods.txt
+++ b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/NativeMethods.txt
@@ -5,4 +5,4 @@ GetPackageFamilyNameFromToken
CoRevertToSelf
SHGetKnownFolderPath
KNOWN_FOLDER_FLAG
-GetCurrentPackageId
+GetCurrentPackageId
\ No newline at end of file
diff --git a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/Properties/Resources.Designer.cs b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/Properties/Resources.Designer.cs
index 2a3b916127..7289c704fb 100644
--- a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/Properties/Resources.Designer.cs
+++ b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/Properties/Resources.Designer.cs
@@ -69,6 +69,24 @@ namespace Microsoft.CommandPalette.Extensions.Toolkit.Properties {
}
}
+ ///
+ /// Looks up a localized string similar to Copy path.
+ ///
+ internal static string CopyPathTextCommand_Name {
+ get {
+ return ResourceManager.GetString("CopyPathTextCommand_Name", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Copied path to clipboard.
+ ///
+ internal static string CopyPathTextCommand_Result {
+ get {
+ return ResourceManager.GetString("CopyPathTextCommand_Result", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Copied to clipboard.
///
diff --git a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/Properties/Resources.resx b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/Properties/Resources.resx
index a27390811e..2472519d34 100644
--- a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/Properties/Resources.resx
+++ b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/Properties/Resources.resx
@@ -126,6 +126,12 @@
Copied to clipboard
+
+ Copy path
+
+
+ Copied path to clipboard
+
Open