diff --git a/Flyoobe.ToolSpot/Flyoobe.Spot.sln b/Flyoobe.ToolSpot/Flyoobe.Spot.sln
new file mode 100644
index 0000000..cc097f1
--- /dev/null
+++ b/Flyoobe.ToolSpot/Flyoobe.Spot.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.14.36414.22
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Flyoobe.Spot", "Flyoobe.ToolSpot\Flyoobe.Spot.csproj", "{1133824D-6F42-48D4-8F18-A93AB7403DB7}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {1133824D-6F42-48D4-8F18-A93AB7403DB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1133824D-6F42-48D4-8F18-A93AB7403DB7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1133824D-6F42-48D4-8F18-A93AB7403DB7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1133824D-6F42-48D4-8F18-A93AB7403DB7}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {B22B36E3-C594-46AF-AF57-3C03B2F97541}
+ EndGlobalSection
+EndGlobal
diff --git a/Flyoobe.ToolSpot/Flyoobe.ToolSpot/App.config b/Flyoobe.ToolSpot/Flyoobe.ToolSpot/App.config
new file mode 100644
index 0000000..aee9adf
--- /dev/null
+++ b/Flyoobe.ToolSpot/Flyoobe.ToolSpot/App.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Flyoobe.ToolSpot/Flyoobe.ToolSpot/Flyoobe.Spot.csproj b/Flyoobe.ToolSpot/Flyoobe.ToolSpot/Flyoobe.Spot.csproj
new file mode 100644
index 0000000..b5eda88
--- /dev/null
+++ b/Flyoobe.ToolSpot/Flyoobe.ToolSpot/Flyoobe.Spot.csproj
@@ -0,0 +1,88 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {1133824D-6F42-48D4-8F18-A93AB7403DB7}
+ WinExe
+ Flyoobe.Spot
+ spot
+ v4.8.1
+ 512
+ true
+ true
+
+
+ AnyCPU
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ AnyCPU
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+ app.manifest
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Form
+
+
+ MainForm.cs
+
+
+
+
+ MainForm.cs
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+ Designer
+
+
+ True
+ Resources.resx
+ True
+
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+ True
+ Settings.settings
+ True
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Flyoobe.ToolSpot/Flyoobe.ToolSpot/MainForm.Designer.cs b/Flyoobe.ToolSpot/Flyoobe.ToolSpot/MainForm.Designer.cs
new file mode 100644
index 0000000..5c1d62a
--- /dev/null
+++ b/Flyoobe.ToolSpot/Flyoobe.ToolSpot/MainForm.Designer.cs
@@ -0,0 +1,105 @@
+namespace Flyoobe.ToolSpot
+{
+ partial class MainForm
+ {
+ ///
+ /// Erforderliche Designervariable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Verwendete Ressourcen bereinigen.
+ ///
+ /// True, wenn verwaltete Ressourcen gelöscht werden sollen; andernfalls False.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Vom Windows Form-Designer generierter Code
+
+ ///
+ /// Erforderliche Methode für die Designerunterstützung.
+ /// Der Inhalt der Methode darf nicht mit dem Code-Editor geändert werden.
+ ///
+ private void InitializeComponent()
+ {
+ this.listResults = new System.Windows.Forms.ListBox();
+ this.textSearch = new System.Windows.Forms.TextBox();
+ this.labelStatus = new System.Windows.Forms.Label();
+ this.SuspendLayout();
+ //
+ // listResults
+ //
+ this.listResults.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.listResults.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(249)))), ((int)(((byte)(243)))), ((int)(((byte)(240)))));
+ this.listResults.BorderStyle = System.Windows.Forms.BorderStyle.None;
+ this.listResults.Font = new System.Drawing.Font("Segoe UI Variable Small Semilig", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.listResults.FormattingEnabled = true;
+ this.listResults.ItemHeight = 17;
+ this.listResults.Location = new System.Drawing.Point(25, 72);
+ this.listResults.Name = "listResults";
+ this.listResults.Size = new System.Drawing.Size(504, 170);
+ this.listResults.TabIndex = 5;
+ this.listResults.TabStop = false;
+ this.listResults.DoubleClick += new System.EventHandler(this.listResults_DoubleClick);
+ this.listResults.KeyDown += new System.Windows.Forms.KeyEventHandler(this.listResults_KeyDown);
+ //
+ // textSearch
+ //
+ this.textSearch.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.textSearch.Font = new System.Drawing.Font("Segoe UI Variable Small Semilig", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.textSearch.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
+ this.textSearch.Location = new System.Drawing.Point(25, 26);
+ this.textSearch.Name = "textSearch";
+ this.textSearch.Size = new System.Drawing.Size(504, 29);
+ this.textSearch.TabIndex = 1;
+ this.textSearch.Text = "Search";
+ this.textSearch.Click += new System.EventHandler(this.textSearch_Click);
+ this.textSearch.TextChanged += new System.EventHandler(this.textSearch_TextChanged);
+ //
+ // labelStatus
+ //
+ this.labelStatus.Dock = System.Windows.Forms.DockStyle.Bottom;
+ this.labelStatus.Font = new System.Drawing.Font("Segoe UI Variable Small Semilig", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.labelStatus.Location = new System.Drawing.Point(0, 251);
+ this.labelStatus.Name = "labelStatus";
+ this.labelStatus.Size = new System.Drawing.Size(557, 21);
+ this.labelStatus.TabIndex = 2;
+ //
+ // MainForm
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(242)))), ((int)(((byte)(244)))), ((int)(((byte)(244)))));
+ this.ClientSize = new System.Drawing.Size(557, 272);
+ this.Controls.Add(this.labelStatus);
+ this.Controls.Add(this.textSearch);
+ this.Controls.Add(this.listResults);
+ this.Name = "MainForm";
+ this.ShowIcon = false;
+ this.ShowInTaskbar = false;
+ this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
+ this.Text = "Spot";
+ this.Load += new System.EventHandler(this.Form1_Load);
+ this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.Form1_KeyDown);
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.ListBox listResults;
+ private System.Windows.Forms.TextBox textSearch;
+ private System.Windows.Forms.Label labelStatus;
+ }
+}
+
diff --git a/Flyoobe.ToolSpot/Flyoobe.ToolSpot/MainForm.cs b/Flyoobe.ToolSpot/Flyoobe.ToolSpot/MainForm.cs
new file mode 100644
index 0000000..87a1a5f
--- /dev/null
+++ b/Flyoobe.ToolSpot/Flyoobe.ToolSpot/MainForm.cs
@@ -0,0 +1,209 @@
+/// This tool is part of my Flyoobe app
+
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Windows.Forms;
+
+namespace Flyoobe.ToolSpot
+{
+
+
+ public partial class MainForm : Form
+
+ { // Path of the text DB
+ public string DatabasePath { get; set; } =
+ Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "spot.txt");
+
+ // In-memory list of all tools
+ private readonly List _all = new List();
+
+ public MainForm()
+ {
+ InitializeComponent();
+ }
+
+ private void Form1_Load(object sender, EventArgs e)
+ {
+ LoadDatabase();
+ //ApplyFilter(); // show all initially
+ textSearch.Focus();
+ }
+
+ private void SetStatus(string text)
+ {
+ if (labelStatus != null) labelStatus.Text = text;
+ }
+
+ private void Form1_KeyDown(object sender, KeyEventArgs e)
+ {
+ if (e.KeyCode == Keys.Escape)
+ {
+ // close it
+ var form = this.FindForm();
+ form?.Close();
+ }
+ }
+
+ private void listResults_KeyDown(object sender, KeyEventArgs e)
+ {
+ if (e.KeyCode == Keys.Enter) { LaunchSelected(); e.Handled = true; }
+ }
+
+ private void listResults_DoubleClick(object sender, EventArgs e) => LaunchSelected();
+
+ private void textSearch_TextChanged(object sender, EventArgs e) => ApplyFilter();
+
+ ///
+ /// Reads toolspot.txt (Name;Command). Ignores empty and comment lines.
+ ///
+ private void LoadDatabase()
+ {
+ _all.Clear();
+
+ if (!File.Exists(DatabasePath))
+ {
+ SetStatus($"Database not found: {DatabasePath}");
+ return;
+ }
+
+ foreach (var line in File.ReadAllLines(DatabasePath))
+ {
+ var trimmed = line.Trim();
+ if (string.IsNullOrEmpty(trimmed)) continue;
+ if (trimmed.StartsWith("#")) continue;
+
+ var item = ParseLine(trimmed);
+ if (item != null) _all.Add(item);
+ }
+
+ SetStatus($"Loaded {_all.Count} tools.");
+ }
+
+ ///
+ /// Parses "Name;Command" lines. Returns null if malformed.
+ ///
+ private ToolItem ParseLine(string line)
+ {
+ var parts = line.Split(new[] { ';' }, 2);
+ if (parts.Length != 2) return null;
+
+ var name = parts[0].Trim();
+ var cmd = parts[1].Trim();
+ if (name.Length == 0 || cmd.Length == 0) return null;
+
+ return new ToolItem(name, cmd);
+ }
+
+ ///
+ /// Filters list by substring (case-insensitive) from textSearch.
+ ///
+ private void ApplyFilter()
+ {
+ var q = (textSearch.Text ?? string.Empty).Trim().ToLower();
+ var list = string.IsNullOrEmpty(q)
+ ? _all
+ : _all.Where(t => t.Name.ToLower().Contains(q)).ToList();
+
+ listResults.BeginUpdate();
+ listResults.Items.Clear();
+ foreach (var t in list) listResults.Items.Add(t);
+ listResults.EndUpdate();
+
+ if (listResults.Items.Count > 0) listResults.SelectedIndex = 0;
+ SetStatus(listResults.Items.Count > 0 ? $"{listResults.Items.Count} result(s)" : "No results");
+ }
+
+ ///
+ /// Launches the selected tool's command. Supports URIs, .msc/.cpl, exe + args.
+ ///
+ private void LaunchSelected()
+ {
+ if (!(listResults.SelectedItem is ToolItem t)) return;
+
+ try
+ {
+ // Split command into FileName + Arguments (handles quoted paths)
+ SplitCommand(t.Command, out var fileName, out var args);
+
+ var psi = new ProcessStartInfo
+ {
+ FileName = fileName,
+ Arguments = args,
+ UseShellExecute = true // required for ms-settings:, .msc/.cpl
+ };
+ Process.Start(psi);
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show("Could not launch: " + ex.Message, "Quick Tools",
+ MessageBoxButtons.OK, MessageBoxIcon.Error);
+ }
+ }
+
+ ///
+ /// Splits a command line into executable and arguments.
+ /// Examples:
+ /// notepad.exe
+ /// "C:\Path With Spaces\app.exe" --flag
+ /// chrome.exe --settings
+ /// For URIs / shell verbs (e.g., ms-settings:...), no split is needed.
+ ///
+ private void SplitCommand(string command, out string fileName, out string args)
+ {
+ command = command.Trim();
+
+ // If it's a URI-like command (ms-settings:, http:, etc.), pass as-is.
+ if (command.Contains(":") && !command.Contains("\\"))
+ {
+ fileName = command;
+ args = string.Empty;
+ return;
+ }
+
+ // Quoted path case: "C:\Program Files\App\app.exe" arg1 arg2
+ if (command.StartsWith("\""))
+ {
+ int end = command.IndexOf('"', 1);
+ if (end > 0)
+ {
+ fileName = command.Substring(1, end - 1);
+ args = command.Substring(end + 1).TrimStart();
+ return;
+ }
+ }
+
+ // Unquoted: split on first space
+ int space = command.IndexOf(' ');
+ if (space > 0)
+ {
+ fileName = command.Substring(0, space);
+ args = command.Substring(space + 1);
+ }
+ else
+ {
+ fileName = command;
+ args = string.Empty;
+ }
+ }
+
+ private class ToolItem
+ {
+ public string Name { get; }
+ public string Command { get; }
+
+ public ToolItem(string name, string command)
+ { Name = name; Command = command; }
+
+ public override string ToString() => Name; // shown in ListBox
+ }
+
+ private void textSearch_Click(object sender, EventArgs e)
+ {
+ textSearch.Clear();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Flyoobe.ToolSpot/Flyoobe.ToolSpot/MainForm.resx b/Flyoobe.ToolSpot/Flyoobe.ToolSpot/MainForm.resx
new file mode 100644
index 0000000..1af7de1
--- /dev/null
+++ b/Flyoobe.ToolSpot/Flyoobe.ToolSpot/MainForm.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 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
+
+
\ No newline at end of file
diff --git a/Flyoobe.ToolSpot/Flyoobe.ToolSpot/Program.cs b/Flyoobe.ToolSpot/Flyoobe.ToolSpot/Program.cs
new file mode 100644
index 0000000..71b863c
--- /dev/null
+++ b/Flyoobe.ToolSpot/Flyoobe.ToolSpot/Program.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace Flyoobe.ToolSpot
+{
+ internal static class Program
+ {
+ ///
+ /// Der Haupteinstiegspunkt für die Anwendung.
+ ///
+ [STAThread]
+ static void Main()
+ {
+ Application.EnableVisualStyles();
+ Application.SetCompatibleTextRenderingDefault(false);
+ Application.Run(new MainForm());
+ }
+ }
+}
diff --git a/Flyoobe.ToolSpot/Flyoobe.ToolSpot/Properties/AssemblyInfo.cs b/Flyoobe.ToolSpot/Flyoobe.ToolSpot/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..c2e62bb
--- /dev/null
+++ b/Flyoobe.ToolSpot/Flyoobe.ToolSpot/Properties/AssemblyInfo.cs
@@ -0,0 +1,33 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// Allgemeine Informationen über eine Assembly werden über die folgenden
+// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
+// die einer Assembly zugeordnet sind.
+[assembly: AssemblyTitle("Spot")]
+[assembly: AssemblyDescription("Flyoobe Spot")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Builtbybel")]
+[assembly: AssemblyProduct("Flyoobe Spot")]
+[assembly: AssemblyCopyright("Copyright © 2025")]
+[assembly: AssemblyTrademark("A Belim app creation")]
+[assembly: AssemblyCulture("")]
+
+// Durch Festlegen von ComVisible auf FALSE werden die Typen in dieser Assembly
+// für COM-Komponenten unsichtbar. Wenn Sie auf einen Typ in dieser Assembly von
+// COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen.
+[assembly: ComVisible(false)]
+
+// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird
+[assembly: Guid("1133824d-6f42-48d4-8f18-a93ab7403db7")]
+
+// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
+//
+// Hauptversion
+// Nebenversion
+// Buildnummer
+// Revision
+//
+[assembly: AssemblyVersion("0.2.0.0")]
+[assembly: AssemblyFileVersion("0.2.0.0")]
diff --git a/Flyoobe.ToolSpot/Flyoobe.ToolSpot/Properties/Resources.Designer.cs b/Flyoobe.ToolSpot/Flyoobe.ToolSpot/Properties/Resources.Designer.cs
new file mode 100644
index 0000000..b8a985c
--- /dev/null
+++ b/Flyoobe.ToolSpot/Flyoobe.ToolSpot/Properties/Resources.Designer.cs
@@ -0,0 +1,63 @@
+//------------------------------------------------------------------------------
+//
+// Dieser Code wurde von einem Tool generiert.
+// Laufzeitversion:4.0.30319.42000
+//
+// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
+// der Code erneut generiert wird.
+//
+//------------------------------------------------------------------------------
+
+namespace Flyoobe.Spot.Properties {
+ using System;
+
+
+ ///
+ /// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw.
+ ///
+ // Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert
+ // -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert.
+ // Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen
+ // mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu.
+ [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() {
+ }
+
+ ///
+ /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird.
+ ///
+ [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("Flyoobe.Spot.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle
+ /// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+ }
+}
diff --git a/Flyoobe.ToolSpot/Flyoobe.ToolSpot/Properties/Resources.resx b/Flyoobe.ToolSpot/Flyoobe.ToolSpot/Properties/Resources.resx
new file mode 100644
index 0000000..af7dbeb
--- /dev/null
+++ b/Flyoobe.ToolSpot/Flyoobe.ToolSpot/Properties/Resources.resx
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/Flyoobe.ToolSpot/Flyoobe.ToolSpot/Properties/Settings.Designer.cs b/Flyoobe.ToolSpot/Flyoobe.ToolSpot/Properties/Settings.Designer.cs
new file mode 100644
index 0000000..6ca8d3d
--- /dev/null
+++ b/Flyoobe.ToolSpot/Flyoobe.ToolSpot/Properties/Settings.Designer.cs
@@ -0,0 +1,26 @@
+//------------------------------------------------------------------------------
+//
+// Dieser Code wurde von einem Tool generiert.
+// Laufzeitversion:4.0.30319.42000
+//
+// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
+// der Code erneut generiert wird.
+//
+//------------------------------------------------------------------------------
+
+namespace Flyoobe.Spot.Properties {
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.14.0.0")]
+ internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default {
+ get {
+ return defaultInstance;
+ }
+ }
+ }
+}
diff --git a/Flyoobe.ToolSpot/Flyoobe.ToolSpot/Properties/Settings.settings b/Flyoobe.ToolSpot/Flyoobe.ToolSpot/Properties/Settings.settings
new file mode 100644
index 0000000..3964565
--- /dev/null
+++ b/Flyoobe.ToolSpot/Flyoobe.ToolSpot/Properties/Settings.settings
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/Flyoobe.ToolSpot/Flyoobe.ToolSpot/app.manifest b/Flyoobe.ToolSpot/Flyoobe.ToolSpot/app.manifest
new file mode 100644
index 0000000..f8a144b
--- /dev/null
+++ b/Flyoobe.ToolSpot/Flyoobe.ToolSpot/app.manifest
@@ -0,0 +1,79 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ true
+
+
+
+
+
+
+
+
diff --git a/Flyoobe/AboutForm.cs b/Flyoobe/AboutForm.cs
index 3202585..008a570 100644
--- a/Flyoobe/AboutForm.cs
+++ b/Flyoobe/AboutForm.cs
@@ -180,7 +180,8 @@ namespace Flyoobe
// Update label text
linkAppVersion.Text = text;
- // If clickable, show bold & underline and make cursor a hand
+ // If clickable,
+ // show bold ; underline ; make cursor a hand
if (clickable)
{
linkAppVersion.Font = new Font(linkAppVersion.Font, FontStyle.Bold | FontStyle.Underline);
diff --git a/Flyoobe/Features/FeatureLoader.cs b/Flyoobe/Features/FeatureLoader.cs
index 3efb353..7970f2e 100644
--- a/Flyoobe/Features/FeatureLoader.cs
+++ b/Flyoobe/Features/FeatureLoader.cs
@@ -1,6 +1,5 @@
using Flyoobe; // for FeatureNode
using Settings.Ads;
-using Settings.AI;
using Settings.Edge;
using Settings.Gaming;
using Settings.Issues;
@@ -118,16 +117,6 @@ namespace Features
new FeatureNode(new WelcomeExperienceAds())
}
},
-
- new FeatureNode("AI")
- {
- Children = new List
- {
- new FeatureNode(new CopilotTaskbar()),
- new FeatureNode(new Recall()),
- new FeatureNode(new ClickToDo())
- }
- }
};
}
}
diff --git a/Flyoobe/InstallView/BackupDriversProvider.cs b/Flyoobe/InstallView/BackupDriversProvider.cs
index bf5aa85..b5fa785 100644
--- a/Flyoobe/InstallView/BackupDriversProvider.cs
+++ b/Flyoobe/InstallView/BackupDriversProvider.cs
@@ -5,13 +5,19 @@ using System.Windows.Forms;
namespace Flyoobe
{
- // Native provider that exports all installed drivers to C:\DriversBackup using:
- // pnputil.exe /export-driver * C:\DriversBackup
+ // Provider that exports all installed drivers into a fixed subfolder named "DriversBackup"
+ // INSIDE the parent folder chosen by the user, using:
+ // pnputil.exe /export-driver * \DriversBackup
+ // Behavior:
+ // - Prompts user to choose a parent folder (defaults to C:\).
+ // - Always creates/uses "\DriversBackup".
+ // - Runs pnputil elevated.
+ // - Shows a completion summary with INF package count.
public sealed class BackupDriversProvider : IInstallProvider
{
public string Id => "drivers-backup";
- public string DisplayName => "Backup installed drivers (exports to C:\\DriversBackup)";
+ public string DisplayName => "Backup installed drivers (choose folder)";
public string HomepageUrl => null;
public string DirectDownloadUrl => null;
public string[] ExactExeNames => new[] { "pnputil.exe" };
@@ -20,18 +26,48 @@ namespace Flyoobe
public bool IsExternalTool => false;
public string Hint =>
- "Exports all currently installed device drivers (INF + binaries) to C:\\DriversBackup. " +
- "After a clean install, point Device Manager to this folder to restore drivers even without internet.";
+ "Exports all currently installed device drivers (INF + binaries) to a folder you choose. " +
+ "After a clean install, point Device Manager to that folder to restore drivers even without internet.";
public string ShowOptionsAndBuildArgs(IWin32Window owner, LastSelections last)
{
- const string target = @"C:\DriversBackup";
+ // Default suggestion for the parent, the actual export target will be \DriversBackup
+ var defaultParent = @"C:\";
+ string parentPath;
+ // pick the PARENT folder where the 'DriversBackup' subfolder will be created
+ using (var fbd = new FolderBrowserDialog
+ {
+ Description = "Choose the PARENT folder. A 'DriversBackup' subfolder will be created inside it.",
+ ShowNewFolderButton = true,
+ SelectedPath = defaultParent
+ })
+ {
+ var dlgResult = fbd.ShowDialog(owner);
+ if (dlgResult != DialogResult.OK || string.IsNullOrWhiteSpace(fbd.SelectedPath))
+ return null; //user cancelled
+
+ parentPath = fbd.SelectedPath.Trim();
+ try
+ {
+ //Normalize parent path
+ parentPath = Path.GetFullPath(parentPath);
+ }
+ catch
+ { //Keep raw selection if normalization fails (we'll error out later if invalid)
+ }
+ }
+
+ // Build the fixed subfolder path: \DriversBackup (always appended)
+ string target = Path.Combine(parentPath, "DriversBackup");
+
+ // Confirm before running the elevated export
if (!ToolHelpers.Confirm(owner,
"Export all installed drivers to:\r\n\r\n" + target +
- "\r\n\r\nThis runs 'pnputil /export-driver *' with administrative rights."))
+ "\r\n\r\nThis will run 'pnputil /export-driver *' with administrative rights."))
return null;
+ // Ensure the target DriversBackup folder exists
try
{
Directory.CreateDirectory(target);
@@ -47,7 +83,7 @@ namespace Flyoobe
try
{
- // Start pnputil as elevated process
+ // Start pnputil as an elevated process targeting the 'DriversBackup' subfolder
var psi = new ProcessStartInfo
{
FileName = "pnputil.exe",
@@ -69,15 +105,17 @@ namespace Flyoobe
proc.WaitForExit();
}
- // Now export is complete > count drivers
+ // Count exported INF files to provide a quick summary
int infCount = 0;
try
{
infCount = Directory.GetFiles(target, "*.inf", SearchOption.AllDirectories).Length;
}
- catch { /* ignore */ }
+ catch
+ { // Ignore counting errors; we can still show success without the count
+ }
- MessageBox.Show(owner,
+ MessageBox.Show(owner,
"Driver export completed.\r\n\r\n" +
$"Target: {target}\r\n" +
$"Exported driver packages: {infCount}\r\n\r\n" +
@@ -94,7 +132,7 @@ namespace Flyoobe
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
- return null;
+ return null; // returning null indicates no further action needed
}
}
}
diff --git a/Flyoobe/InstallView/InstallControlView.cs b/Flyoobe/InstallView/InstallControlView.cs
index f75890c..0d60fdc 100644
--- a/Flyoobe/InstallView/InstallControlView.cs
+++ b/Flyoobe/InstallView/InstallControlView.cs
@@ -282,7 +282,7 @@ namespace Flyoobe
return null;
}
- // Selects the provider in the combo (for UI consistency) and runs it via existing pipeline.
+ // Selects the provider in the combo (for UI consistency) and runs it via existing pipeline
private async System.Threading.Tasks.Task RunQuickAsync(string providerId)
{
var p = FindProviderById(providerId);
diff --git a/Flyoobe/MainForm.cs b/Flyoobe/MainForm.cs
index 6a45c76..f74b79e 100644
--- a/Flyoobe/MainForm.cs
+++ b/Flyoobe/MainForm.cs
@@ -35,6 +35,7 @@ namespace Flyoobe
_navigator.RegisterView("Device", () => new DeviceControlView());
_navigator.RegisterView("Personalization", () => new PersonalizationControlView());
_navigator.RegisterView("Browser", () => new DefaultsControlView());
+ _navigator.RegisterView("AI", () => new AiControlView());
_navigator.RegisterView("Network", () => new NetworkControlView());
_navigator.RegisterView("Account", () => new AccountControlView());
_navigator.RegisterView("Apps", () => new AppsControlView());
diff --git a/Flyoobe/UpgradeView/EsuEnrollmentHandler.cs b/Flyoobe/UpgradeView/EsuEnrollmentHandler.cs
new file mode 100644
index 0000000..cb65cbe
--- /dev/null
+++ b/Flyoobe/UpgradeView/EsuEnrollmentHandler.cs
@@ -0,0 +1,200 @@
+
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Net;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+public class EsuEnrollmentHandler
+{
+ ///
+ /// Keep Windows 10 secure until 2026 (Enroll in ESU) using abbodi1406's ConsumerESU scripts.
+ /// Downloads from GitHub if missing and executes.
+ ///
+ ///
+ public static async Task HandleEnrollInESU()
+ {
+ string repoBase = "https://raw.githubusercontent.com/abbodi1406/ConsumerESU/master/";
+ string cmdFile = "Consumer_ESU_Enrollment_run.cmd";
+ string psFile = "Consumer_ESU_Enrollment.ps1";
+
+ string appDir = AppDomain.CurrentDomain.BaseDirectory;
+ string cmdPath = Path.Combine(appDir, cmdFile);
+ string psPath = Path.Combine(appDir, psFile);
+
+ // Ensure scripts exist (download if missing)
+ if (!File.Exists(cmdPath) || !File.Exists(psPath))
+ {
+ var confirm = MessageBox.Show(
+ "The ESU enrollment scripts are missing.\n\n" +
+ "They will be downloaded directly from GitHub (abbodi1406/ConsumerESU).\n\n" +
+ "Do you want to continue?",
+ "Download Scripts",
+ MessageBoxButtons.YesNo,
+ MessageBoxIcon.Question);
+
+ if (confirm != DialogResult.Yes) return;
+
+ using (var wc = new WebClient())
+ {
+ try
+ {
+ if (!File.Exists(cmdPath))
+ await wc.DownloadFileTaskAsync(new Uri(repoBase + cmdFile), cmdPath);
+ if (!File.Exists(psPath))
+ await wc.DownloadFileTaskAsync(new Uri(repoBase + psFile), psPath);
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show("Failed to download ESU scripts:\n" + ex.Message,
+ "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ return;
+ }
+ }
+ }
+
+ // Show parameter selection dialog
+ string esuArgs = "";
+ using (var dlg = new EsuParamDialog())
+ {
+ if (dlg.ShowDialog() != DialogResult.OK)
+ return;
+
+ esuArgs = dlg.SelectedParam;
+ if (dlg.AddProceed) esuArgs += " -Proceed";
+ }
+
+ // Confirm and run
+ var result = MessageBox.Show(
+ "This will launch the Consumer ESU Enrollment Tool with parameters:\n\n" +
+ esuArgs + "\n\nContinue?",
+ "Enroll in ESU",
+ MessageBoxButtons.YesNo,
+ MessageBoxIcon.Information);
+
+ if (result != DialogResult.Yes) return;
+
+ try
+ {
+ var psi = new ProcessStartInfo
+ {
+ FileName = "cmd.exe",
+ Arguments = "/c \"" + cmdPath + "\" " + esuArgs,
+ WorkingDirectory = appDir,
+ UseShellExecute = true,
+ Verb = "runas" // run as admin
+ };
+ Process.Start(psi);
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show("Failed to run ESU enrollment script:\n" + ex.Message,
+ "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ }
+ }
+
+ ///
+ /// Dialog form to select ESU enrollment parameter (no-designer)
+ ///
+ private class EsuParamDialog : Form
+ {
+ private ComboBox combo;
+ private CheckBox chkProceed;
+ private Button btnOk;
+ private Button btnCancel;
+ private LinkLabel link;
+
+ public string SelectedParam { get; private set; }
+ public bool AddProceed { get; private set; }
+
+ public EsuParamDialog()
+ {
+ Text = "Select ESU Enrollment Parameter";
+ Width = 550;
+ Height = 250;
+ StartPosition = FormStartPosition.CenterParent;
+ FormBorderStyle = FormBorderStyle.FixedDialog;
+ MaximizeBox = false;
+ MinimizeBox = false;
+ ShowIcon = false;
+ ShowInTaskbar = false;
+
+ combo = new ComboBox
+ {
+ Left = 20,
+ Top = 20,
+ Width = 500,
+ DropDownStyle = ComboBoxStyle.DropDownList
+ };
+ combo.Items.AddRange(new object[]
+ {
+ "-Online : Only enroll using Microsoft user account token (exit if failed)",
+ "-Store : Only enroll using Microsoft store account token (exit if failed)",
+ "-Local : Only enroll using Local user account token (exit if failed)",
+ "-License : Force acquire Consumer ESU License regardless or without enrollment",
+ "-Remove : Remove Consumer ESU License if exists"
+ });
+ combo.SelectedIndex = 0;
+
+ chkProceed = new CheckBox
+ {
+ Left = 20,
+ Top = 60,
+ Text = "Add -Proceed (force running enrollment even if already enrolled)",
+ AutoSize = true
+ };
+
+ btnOk = new Button
+ {
+ Text = "OK",
+ Left = 340,
+ Width = 80,
+ Top = 150,
+ DialogResult = DialogResult.OK
+ };
+ btnCancel = new Button
+ {
+ Text = "Cancel",
+ Left = 430,
+ Width = 80,
+ Top = 150,
+ DialogResult = DialogResult.Cancel
+ };
+
+ link = new LinkLabel
+ {
+ Left = 20,
+ Top = 100,
+ AutoSize = true,
+ Text = "Help && Credits: https://github.com/abbodi1406/ConsumerESU"
+ };
+ link.LinkClicked += (s, e) =>
+ {
+ try { Process.Start(new ProcessStartInfo { FileName = "https://github.com/abbodi1406/ConsumerESU", UseShellExecute = true }); }
+ catch { }
+ };
+
+ Controls.Add(combo);
+ Controls.Add(chkProceed);
+ Controls.Add(link);
+ Controls.Add(btnOk);
+ Controls.Add(btnCancel);
+
+ AcceptButton = btnOk;
+ CancelButton = btnCancel;
+ }
+
+ protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
+ {
+ if (DialogResult == DialogResult.OK)
+ {
+ // Extract only the switch (first word, e.g. "-Online")
+ SelectedParam = combo.SelectedItem.ToString().Split(' ')[0];
+ AddProceed = chkProceed.Checked;
+ }
+ base.OnClosing(e);
+ }
+ }
+}
+
diff --git a/Flyoobe/UpgradeView/IsoHandler.cs b/Flyoobe/UpgradeView/IsoHandler.cs
index 8a6885a..51405a6 100644
--- a/Flyoobe/UpgradeView/IsoHandler.cs
+++ b/Flyoobe/UpgradeView/IsoHandler.cs
@@ -403,81 +403,6 @@ try {{
}
}
- ///
- /// Keep Windows 10 secure until 2026 (Enroll in ESU) using abbodi1406's ConsumerESU scripts.
- /// Downloads from GitHub if missing and executes.
- ///
- public async void HandleEnrollInESU()
- {
- string repoBase = "https://raw.githubusercontent.com/abbodi1406/ConsumerESU/master/";
- string cmdFile = "Consumer_ESU_Enrollment_run.cmd";
- string psFile = "Consumer_ESU_Enrollment.ps1";
-
- string appDir = AppDomain.CurrentDomain.BaseDirectory;
- string cmdPath = Path.Combine(appDir, cmdFile);
- string psPath = Path.Combine(appDir, psFile);
-
- // Check if scripts exist, otherwise download
- if (!System.IO.File.Exists(cmdPath) || !System.IO.File.Exists(psPath))
- {
- var confirm = MessageBox.Show(
- "The ESU enrollment scripts are missing.\n\n" +
- "They will now be downloaded directly from GitHub (abbodi1406/ConsumerESU).\n\n" +
- "Do you want to continue?",
- "Download Scripts",
- MessageBoxButtons.YesNo,
- MessageBoxIcon.Question);
-
- if (confirm != DialogResult.Yes)
- return;
-
- using (var wc = new System.Net.WebClient())
- {
- try
- {
- if (!System.IO.File.Exists(cmdPath))
- await wc.DownloadFileTaskAsync(new Uri(repoBase + cmdFile), cmdPath);
-
- if (!System.IO.File.Exists(psPath))
- await wc.DownloadFileTaskAsync(new Uri(repoBase + psFile), psPath);
- }
- catch (Exception ex)
- {
- MessageBox.Show("Failed to download ESU scripts:\n" + ex.Message,
- "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
- return;
- }
- }
- }
- var result = MessageBox.Show(
- "📦 This will now launch the Consumer ESU Enrollment Tool by abbodi1406.\n\n" +
- "✔ Extends Windows 10 support until October 2026\n" +
- "✔ Requires Windows 10 22H2 with KB5039299+ (19045.4598+)\n" +
- "✔ Requires Microsoft Account + Internet access\n\n" +
- "Do you want to continue?",
- "Enroll in ESU",
- MessageBoxButtons.YesNo,
- MessageBoxIcon.Information);
-
- if (result != DialogResult.Yes)
- return;
-
- // Start the .cmd (which internally calls the PowerShell)
- try
- {
- Process.Start(new ProcessStartInfo
- {
- FileName = cmdPath,
- Verb = "runas",
- UseShellExecute = true
- });
- }
- catch (Exception ex)
- {
- MessageBox.Show("Failed to run ESU enrollment script:\n" + ex.Message,
- "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
- }
- }
///
/// Handle unexpected exceptions by logging the error, updating the status, and optionally launching Copilot for help.
@@ -508,6 +433,7 @@ try {{
CopilotHelper.LaunchCopilot();
}
}
+
}
public static class ProcessExtensions
@@ -520,4 +446,5 @@ try {{
await tcs.Task;
}
}
-}
\ No newline at end of file
+}
+
diff --git a/Flyoobe/UpgradeView/UpgradeControlView.cs b/Flyoobe/UpgradeView/UpgradeControlView.cs
index 1e72221..7e4d9ad 100644
--- a/Flyoobe/UpgradeView/UpgradeControlView.cs
+++ b/Flyoobe/UpgradeView/UpgradeControlView.cs
@@ -48,7 +48,7 @@ namespace Flyoobe
var combo = sender as ComboBox;
int selectedIndex = combo.SelectedIndex;
- // Let user pick ISO from local disk (compatibility check is now in HandleIsoInput)
+ // Pick ISO from local disk (compatibility check is now in HandleIsoInput)
if (selectedIndex == 1)
{
await HandleIsoInput(sender, null);
@@ -63,7 +63,7 @@ namespace Flyoobe
break;
case 3:
- _isoHandler.HandleEnrollInESU();
+ await EsuEnrollmentHandler.HandleEnrollInESU();
break;
}
}