diff --git a/KitX Contracts/KitX.Contract.CSharp/TriggerHelper.cs b/KitX Contracts/KitX.Contract.CSharp/TriggerHelper.cs new file mode 100644 index 0000000..7854215 --- /dev/null +++ b/KitX Contracts/KitX.Contract.CSharp/TriggerHelper.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using KitX.Shared.CSharp.WebCommand; +using KitX.Shared.CSharp.WebCommand.Infos; + +namespace KitX.Contract.CSharp; + +/// +/// 插件触发器辅助工具。插件通过此类发送触发信号到 Dashboard。 +/// 触发器是纯信号,不携带业务数据。工作流被触发后通过调用插件函数获取数据。 +/// +public static class TriggerHelper +{ + private static readonly JsonSerializerOptions _options = new() + { + WriteIndented = false, + IncludeFields = true, + PropertyNameCaseInsensitive = true, + }; + + /// + /// 触发一个信号事件 + /// + /// SetSendCommandAction 提供的发送回调 + /// 触发器名称 + public static void FireTrigger(Action? sendAction, string triggerName) + { + if (sendAction is null) return; + + var request = new Request + { + Type = RequestTypes.Command, + Version = RequestVersions.V1, + Content = JsonSerializer.Serialize(new Command + { + Request = CommandRequestInfo.TriggerFired, + Tags = new Dictionary { { "TriggerName", triggerName } } + }, _options) + }; + + sendAction.Invoke(request); + } +} diff --git a/KitX Core Contracts/KitX.Core.Contract/Activity/IActivityService.cs b/KitX Core Contracts/KitX.Core.Contract/Activity/IActivityService.cs new file mode 100644 index 0000000..fae97e4 --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Activity/IActivityService.cs @@ -0,0 +1,93 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Threading.Tasks; + +namespace KitX.Core.Contract.Activity; + +/// +/// Activity management service interface +/// +public interface IActivityService +{ + /// + /// Records app start event + /// + void RecordAppStart(); + + /// + /// Records app exit event + /// + void RecordAppExit(); + + /// + /// Records an activity + /// + /// The activity type + /// Optional details + void RecordActivity(string type, Dictionary? details = null); + + /// + /// Gets activities + /// + /// Optional start date + /// Optional end date + /// Maximum number of activities to return + /// List of activities + IList GetActivities(DateTime? startDate = null, DateTime? endDate = null, int limit = 100); + + /// + /// Gets activity statistics + /// + /// Start date + /// End date + /// Activity statistics + IActivityStatistics GetStatistics(DateTime startDate, DateTime endDate); + + /// + /// Event raised when activities are updated + /// + event EventHandler? ActivitiesUpdated; +} + +/// +/// Activity interface +/// +public interface IActivity +{ + /// + /// Gets the activity ID + /// + string Id { get; } + + /// + /// Gets the activity type + /// + string Type { get; } + + /// + /// Gets the timestamp + /// + DateTime Timestamp { get; } + + /// + /// Gets the details + /// + Dictionary Details { get; } +} + +/// +/// Activity statistics interface +/// +public interface IActivityStatistics +{ + /// + /// Gets the total number of activities + /// + int TotalActivities { get; } + + /// + /// Gets the activities grouped by type + /// + Dictionary ActivitiesByType { get; } +} diff --git a/KitX Core Contracts/KitX.Core.Contract/Announcement/IAnnouncementService.cs b/KitX Core Contracts/KitX.Core.Contract/Announcement/IAnnouncementService.cs new file mode 100644 index 0000000..676f850 --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Announcement/IAnnouncementService.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Threading.Tasks; +using KitX.Core.Contract.Configuration; + +namespace KitX.Core.Contract.Announcement; + +/// +/// Announcement service interface +/// +public interface IAnnouncementService +{ + /// + /// Gets the announcement configuration + /// + IAnnouncementConf AnnouncementConfig { get; } + + /// + /// Checks for new announcements + /// + /// List of new announcements + Task> CheckNewAnnouncementsAsync(); + + /// + /// Marks an announcement as read + /// + /// The announcement ID + void MarkAsRead(string announcementId); + + /// + /// Gets all read announcement IDs + /// + /// List of read announcement IDs + IReadOnlyList GetReadAnnouncementIds(); + + /// + /// Saves the announcement configuration + /// + void SaveAnnouncementConfig(); + + /// + /// Event raised when new announcements are available + /// + event EventHandler? NewAnnouncementsAvailable; +} + +/// +/// Announcement interface +/// +public interface IAnnouncement +{ + /// + /// Gets the announcement ID + /// + string Id { get; } + + /// + /// Gets the announcement title + /// + string Title { get; } + + /// + /// Gets the announcement content + /// + string Content { get; } + + /// + /// Gets the publish date + /// + DateTime PublishDate { get; } + + /// + /// Gets the version + /// + string Version { get; } +} + +/// +/// New announcements event arguments +/// +public class NewAnnouncementsEventArgs : EventArgs +{ + /// + /// Gets or sets the announcements + /// + public IReadOnlyList Announcements { get; set; } = Array.Empty(); + + /// + /// Gets or sets announcements as dictionary (date -> content) + /// + public Dictionary? AnnouncementsDict { get; set; } +} + +/// +/// Announcement error event arguments +/// +public class AnnouncementErrorEventArgs : EventArgs +{ + /// + /// Gets or sets the error message + /// + public string ErrorMessage { get; set; } = string.Empty; + + /// + /// Gets or sets the stack trace + /// + public string StackTrace { get; set; } = string.Empty; +} diff --git a/KitX Core Contracts/KitX.Core.Contract/Configuration/ConfigChangedEventArgs.cs b/KitX Core Contracts/KitX.Core.Contract/Configuration/ConfigChangedEventArgs.cs new file mode 100644 index 0000000..9e05817 --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Configuration/ConfigChangedEventArgs.cs @@ -0,0 +1,29 @@ +using System; + +namespace KitX.Core.Contract.Configuration; + +/// +/// Configuration changed event arguments +/// +public class ConfigChangedEventArgs : EventArgs +{ + /// + /// Gets or sets the configuration type (e.g., "App", "Plugins", "Security") + /// + public string ConfigType { get; set; } = string.Empty; + + /// + /// Gets or sets the property name that changed + /// + public string PropertyName { get; set; } = string.Empty; + + /// + /// Gets or sets the old value + /// + public object? OldValue { get; set; } + + /// + /// Gets or sets the new value + /// + public object? NewValue { get; set; } +} diff --git a/KitX Core Contracts/KitX.Core.Contract/Configuration/IActivityConf.cs b/KitX Core Contracts/KitX.Core.Contract/Configuration/IActivityConf.cs new file mode 100644 index 0000000..8c15a61 --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Configuration/IActivityConf.cs @@ -0,0 +1,9 @@ +namespace KitX.Core.Contract.Configuration; + +/// +/// Activity configuration section +/// +public interface IActivityConf +{ + int TotalRecorded { get; set; } +} diff --git a/KitX Core Contracts/KitX.Core.Contract/Configuration/IAnnouncementConf.cs b/KitX Core Contracts/KitX.Core.Contract/Configuration/IAnnouncementConf.cs new file mode 100644 index 0000000..b407a00 --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Configuration/IAnnouncementConf.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; + +namespace KitX.Core.Contract.Configuration; + +public interface IAnnouncementConf +{ + /// + /// Gets or sets the list of accepted announcement IDs + /// + List Accepted { get; set; } + + /// + /// Gets or sets the config file location + /// + string? ConfigFileLocation { get; set; } +} + +/// +/// Backward-compatible alias of . +/// Deprecated: use instead. +/// +[Obsolete("Use IAnnouncementConf instead.")] +public interface IAnnouncementConfig : IAnnouncementConf +{ +} diff --git a/KitX Core Contracts/KitX.Core.Contract/Configuration/IAppConf.cs b/KitX Core Contracts/KitX.Core.Contract/Configuration/IAppConf.cs new file mode 100644 index 0000000..02cfff6 --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Configuration/IAppConf.cs @@ -0,0 +1,84 @@ +using System.Collections.Generic; + +namespace KitX.Core.Contract.Configuration; + +/// +/// Application configuration interface (complete structure) +/// +/// Aggregate root of the configuration sections. Retains the *Config name +/// because the *Conf equivalent (, the App section) +/// is already taken; all section interfaces are unified under the *Conf +/// naming convention. +/// +/// +public interface IAppConfig +{ + /// + /// Gets or sets the application configuration + /// + IAppConf App { get; set; } + + /// + /// Gets or sets the windows configuration + /// + IWindowsConf Windows { get; set; } + + /// + /// Gets or sets the pages configuration + /// + IPagesConf Pages { get; set; } + + /// + /// Gets or sets the web configuration + /// + IWebConf Web { get; set; } + + /// + /// Gets or sets the log configuration + /// + ILogConf Log { get; set; } + + /// + /// Gets or sets the IO configuration + /// + IIOConf IO { get; set; } + + /// + /// Gets or sets the activity configuration + /// + IActivityConf Activity { get; set; } + + /// + /// Gets or sets the loaders configuration + /// + ILoadersConf Loaders { get; set; } +} + +/// +/// Application configuration section +/// +public interface IAppConf +{ + string IconFileName { get; set; } + string CoverIconFileName { get; set; } + string AppLanguage { get; set; } + string Theme { get; set; } + string ThemeColor { get; set; } + Dictionary SurpportLanguages { get; set; } + string LocalPluginsFileFolder { get; set; } + string LocalPluginsDataFolder { get; set; } + bool DeveloperSetting { get; set; } + bool ShowAnnouncementWhenStart { get; set; } + ulong RanTime { get; set; } + int LastBreakAfterExit { get; set; } + + /// + /// Default expand mode for blueprint nested (Block) nodes. + /// Values: "Embedded" (Picture-in-Picture inner editor, default) or + /// "SubEditor" (modal overlay with breadcrumb navigation). + /// Consumed by BlueprintEditorViewModel.InitializeBlockScopes when + /// creating BlueprintBlockNodeVM instances. Unknown values fall back + /// to "Embedded". + /// + string BlueprintNestedNodeExpandMode { get; set; } +} diff --git a/KitX Core Contracts/KitX.Core.Contract/Configuration/IConfigLoader.cs b/KitX Core Contracts/KitX.Core.Contract/Configuration/IConfigLoader.cs new file mode 100644 index 0000000..68fea69 --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Configuration/IConfigLoader.cs @@ -0,0 +1,21 @@ +namespace KitX.Core.Contract.Configuration; + +/// +/// Loads configuration from files +/// +public interface IConfigLoader +{ + /// + /// Loads a config file from the specified location + /// + /// Config type + /// Directory path + /// File name + /// The loaded config or default + T Load(string location, string fileName) where T : class, new(); + + /// + /// Loads SecurityConfig with special handling + /// + ISecurityConf LoadSecurityConfig(string location); +} diff --git a/KitX Core Contracts/KitX.Core.Contract/Configuration/IConfigSaver.cs b/KitX Core Contracts/KitX.Core.Contract/Configuration/IConfigSaver.cs new file mode 100644 index 0000000..e6b8df7 --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Configuration/IConfigSaver.cs @@ -0,0 +1,16 @@ +namespace KitX.Core.Contract.Configuration; + +/// +/// Saves configuration to files +/// +public interface IConfigSaver +{ + /// + /// Saves a config to the specified location + /// + /// Config type + /// Config to save + /// Directory path + /// File name + void Save(T config, string location, string fileName) where T : class; +} diff --git a/KitX Core Contracts/KitX.Core.Contract/Configuration/IConfigService.cs b/KitX Core Contracts/KitX.Core.Contract/Configuration/IConfigService.cs new file mode 100644 index 0000000..e06ae3c --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Configuration/IConfigService.cs @@ -0,0 +1,44 @@ +using System; + +namespace KitX.Core.Contract.Configuration; + +/// +/// Configuration management service interface +/// +public interface IConfigService +{ + /// + /// Gets the application configuration + /// + IAppConfig AppConfig { get; } + + /// + /// Gets the plugins configuration + /// + IPluginsConf PluginsConfig { get; } + + /// + /// Gets the security configuration + /// + ISecurityConf SecurityConfig { get; } + + /// + /// Loads all configurations from files + /// + void Load(); + + /// + /// Saves all configurations to files + /// + void SaveAll(); + + /// + /// Reloads all configurations from files + /// + void Reload(); + + /// + /// Event raised when configuration changes + /// + event EventHandler? ConfigChanged; +} diff --git a/KitX Core Contracts/KitX.Core.Contract/Configuration/IConfigWithMetadata.cs b/KitX Core Contracts/KitX.Core.Contract/Configuration/IConfigWithMetadata.cs new file mode 100644 index 0000000..b43f4f3 --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Configuration/IConfigWithMetadata.cs @@ -0,0 +1,24 @@ +using System; + +namespace KitX.Core.Contract.Configuration; + +/// +/// Interface for configurations with metadata fields +/// +public interface IConfigWithMetadata +{ + /// + /// Gets or sets the configuration file location + /// + string? ConfigFileLocation { get; set; } + + /// + /// Gets or sets the configuration file watcher name + /// + string? ConfigFileWatcherName { get; set; } + + /// + /// Gets or sets the configuration generated time + /// + DateTime? ConfigGeneratedTime { get; set; } +} diff --git a/KitX Core Contracts/KitX.Core.Contract/Configuration/IIOConf.cs b/KitX Core Contracts/KitX.Core.Contract/Configuration/IIOConf.cs new file mode 100644 index 0000000..bf766f8 --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Configuration/IIOConf.cs @@ -0,0 +1,10 @@ +namespace KitX.Core.Contract.Configuration; + +/// +/// IO configuration section +/// +public interface IIOConf +{ + int UpdatingCheckPerThreadFilesCount { get; set; } + int OperatingSystemVersionUpdateInterval { get; set; } +} diff --git a/KitX Core Contracts/KitX.Core.Contract/Configuration/ILoadersConf.cs b/KitX Core Contracts/KitX.Core.Contract/Configuration/ILoadersConf.cs new file mode 100644 index 0000000..c1ba09b --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Configuration/ILoadersConf.cs @@ -0,0 +1,9 @@ +namespace KitX.Core.Contract.Configuration; + +/// +/// Loaders configuration section +/// +public interface ILoadersConf +{ + string InstallPath { get; set; } +} diff --git a/KitX Core Contracts/KitX.Core.Contract/Configuration/ILogConf.cs b/KitX Core Contracts/KitX.Core.Contract/Configuration/ILogConf.cs new file mode 100644 index 0000000..9c59c63 --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Configuration/ILogConf.cs @@ -0,0 +1,14 @@ +namespace KitX.Core.Contract.Configuration; + +/// +/// Log configuration section +/// +public interface ILogConf +{ + long LogFileSingleMaxSize { get; set; } + string LogFilePath { get; set; } + string LogTemplate { get; set; } + int LogFileMaxCount { get; set; } + int LogFileFlushInterval { get; set; } + LogLevel LogLevel { get; set; } +} diff --git a/KitX Core Contracts/KitX.Core.Contract/Configuration/IPagesConf.cs b/KitX Core Contracts/KitX.Core.Contract/Configuration/IPagesConf.cs new file mode 100644 index 0000000..89d7898 --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Configuration/IPagesConf.cs @@ -0,0 +1,56 @@ +namespace KitX.Core.Contract.Configuration; + +/// +/// Pages configuration section +/// +public interface IPagesConf +{ + IHomePageConf Home { get; set; } + IDevicePageConf Device { get; set; } + IMarketPageConf Market { get; set; } + ISettingsPageConf Settings { get; set; } +} + +/// +/// Device page configuration +/// +public interface IDevicePageConf +{ +} + +/// +/// Market page configuration +/// +public interface IMarketPageConf +{ +} + +/// +/// Home page configuration +/// +public interface IHomePageConf +{ + NavigationViewPaneDisplayMode NavigationViewPaneDisplayMode { get; set; } + string SelectedViewName { get; set; } + bool IsNavigationViewPaneOpened { get; set; } + bool UseAreaExpanded { get; set; } +} + +/// +/// Settings page configuration +/// +public interface ISettingsPageConf +{ + NavigationViewPaneDisplayMode NavigationViewPaneDisplayMode { get; set; } + string SelectedViewName { get; set; } + bool PaletteAreaExpanded { get; set; } + bool WebRelatedAreaExpanded { get; set; } + bool WebRelatedAreaOfNetworkInterfacesExpanded { get; set; } + bool LogRelatedAreaExpanded { get; set; } + bool UpdateRelatedAreaExpanded { get; set; } + bool AboutAreaExpanded { get; set; } + bool AuthorsAreaExpanded { get; set; } + bool LinksAreaExpanded { get; set; } + bool ThirdPartyLicensesAreaExpanded { get; set; } + bool IsNavigationViewPaneOpened { get; set; } +} diff --git a/KitX Core Contracts/KitX.Core.Contract/Configuration/IPluginsConf.cs b/KitX Core Contracts/KitX.Core.Contract/Configuration/IPluginsConf.cs new file mode 100644 index 0000000..513fee5 --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Configuration/IPluginsConf.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using KitX.Shared.CSharp.Device; +using KitX.Shared.CSharp.Loader; +using KitX.Shared.CSharp.Plugin; + +namespace KitX.Core.Contract.Configuration; + +/// +/// Plugins configuration interface +/// +public interface IPluginsConf +{ + /// + /// Gets or sets the list of plugin installations + /// + IList Plugins { get; set; } +} + +/// +/// Backward-compatible alias of . +/// Deprecated: use instead. +/// +[Obsolete("Use IPluginsConf instead.")] +public interface IPluginsConfig : IPluginsConf +{ +} + +/// +/// Plugin installation interface +/// +public interface IPluginInstallation +{ + /// + /// Gets the unique identifier for this plugin installation + /// + Guid Id { get; } + + /// + /// Gets the installation path + /// + string? InstallPath { get; } + + /// + /// Gets or sets the plugin information + /// + PluginInfo? PluginInfo { get; set; } + + /// + /// Gets or sets the loader information + /// + LoaderInfo? LoaderInfo { get; set; } + + /// + /// Gets or sets the list of installed devices + /// + IList InstalledDevices { get; set; } +} diff --git a/KitX Core Contracts/KitX.Core.Contract/Configuration/ISecurityConf.cs b/KitX Core Contracts/KitX.Core.Contract/Configuration/ISecurityConf.cs new file mode 100644 index 0000000..af7f3ae --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Configuration/ISecurityConf.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using KitX.Shared.CSharp.Device; + +namespace KitX.Core.Contract.Configuration; + +/// +/// Security configuration interface +/// +public interface ISecurityConf +{ + /// + /// Gets or sets the device keys list + /// + IList DeviceKeys { get; set; } +} + +/// +/// Backward-compatible alias of . +/// Deprecated: use instead. +/// +[Obsolete("Use ISecurityConf instead.")] +public interface ISecurityConfig : ISecurityConf +{ +} + +/// +/// Device key interface +/// +public interface IDeviceKey +{ + /// + /// Gets the device locator + /// + DeviceLocator Device { get; } + + /// + /// Gets the RSA public key in PEM format + /// + string? RsaPublicKeyPem { get; } + + /// + /// Gets the MAC address + /// + string MacAddress { get; } + + /// + /// Gets the device name + /// + string DeviceName { get; } + + /// + /// Gets the public key + /// + string PublicKey { get; } + + /// + /// Gets the time when the key was added + /// + DateTime AddedAt { get; } +} \ No newline at end of file diff --git a/KitX Core Contracts/KitX.Core.Contract/Configuration/IWebConf.cs b/KitX Core Contracts/KitX.Core.Contract/Configuration/IWebConf.cs new file mode 100644 index 0000000..01b4503 --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Configuration/IWebConf.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; + +namespace KitX.Core.Contract.Configuration; + +/// +/// Web configuration section +/// +public interface IWebConf +{ + double DelayStartSeconds { get; set; } + string ApiServer { get; set; } + string ApiPath { get; set; } + int DevicesViewRefreshDelay { get; set; } + List? AcceptedNetworkInterfaces { get; set; } + int? UserSpecifiedDevicesServerPort { get; set; } + int? UserSpecifiedPluginsServerPort { get; set; } + int UdpPortSend { get; set; } + int UdpPortReceive { get; set; } + int UdpSendFrequency { get; set; } + string UdpBroadcastAddress { get; set; } + string IPFilter { get; set; } + int SocketBufferSize { get; set; } + int DeviceInfoTTLSeconds { get; set; } + bool DisableRemovingOfflineDeviceCard { get; set; } + string UpdateServer { get; set; } + string UpdatePath { get; set; } + string UpdateDownloadPath { get; set; } + string UpdateChannel { get; set; } + string UpdateSource { get; set; } +} + diff --git a/KitX Core Contracts/KitX.Core.Contract/Configuration/IWindowsConf.cs b/KitX Core Contracts/KitX.Core.Contract/Configuration/IWindowsConf.cs new file mode 100644 index 0000000..7374a8b --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Configuration/IWindowsConf.cs @@ -0,0 +1,41 @@ +using System.Collections.Generic; +using Common.BasicHelper.Graphics.Screen; + +namespace KitX.Core.Contract.Configuration; + +/// +/// Windows configuration section +/// +public interface IWindowsConf +{ + IMainWindowConf MainWindow { get; set; } + IAnnouncementWindowConf AnnouncementWindow { get; set; } +} + +/// +/// Main window configuration +/// +public interface IMainWindowConf +{ + Resolution Size { get; set; } + Distances Location { get; set; } + WindowState WindowState { get; set; } + bool IsHidden { get; set; } + Dictionary Tags { get; set; } + int GreetingTextCount_Morning { get; set; } + int GreetingTextCount_Noon { get; set; } + int GreetingTextCount_AfterNoon { get; set; } + int GreetingTextCount_Evening { get; set; } + int GreetingTextCount_Night { get; set; } + int GreetingUpdateInterval { get; set; } +} + +/// +/// Announcement window configuration +/// +public interface IAnnouncementWindowConf +{ + Resolution Size { get; set; } + Distances Location { get; set; } +} + diff --git a/KitX Core Contracts/KitX.Core.Contract/Configuration/LogLevel.cs b/KitX Core Contracts/KitX.Core.Contract/Configuration/LogLevel.cs new file mode 100644 index 0000000..5196aee --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Configuration/LogLevel.cs @@ -0,0 +1,33 @@ +namespace KitX.Core.Contract.Configuration; + +/// +/// Logging level for the KitX logger. +/// +/// This is the contract-level logging level used by . +/// It intentionally mirrors the numeric values of Serilog's +/// Serilog.Events.LogEventLevel (Verbose=0 … Fatal=5) so that pre-existing +/// config files containing the old integer values keep deserializing unchanged. +/// Implementations may map this enum onto their actual logging framework +/// (e.g., Serilog) without requiring a contract-level dependency on it. +/// +/// +public enum LogLevel +{ + /// Verbose level (most detailed, e.g. tracing) + Verbose = 0, + + /// Debug level + Debug = 1, + + /// Information level (default) + Information = 2, + + /// Warning level + Warning = 3, + + /// Error level + Error = 4, + + /// Fatal level (least detailed) + Fatal = 5 +} diff --git a/KitX Core Contracts/KitX.Core.Contract/Configuration/NavigationViewPaneDisplayMode.cs b/KitX Core Contracts/KitX.Core.Contract/Configuration/NavigationViewPaneDisplayMode.cs new file mode 100644 index 0000000..9732e4d --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Configuration/NavigationViewPaneDisplayMode.cs @@ -0,0 +1,19 @@ +namespace KitX.Core.Contract.Configuration; + +/// +/// Navigation view pane display mode enum. +/// +/// This is a UI-layer mapping enum: it mirrors the UI framework's navigation-view +/// pane display mode so the UI layer (KitX Dashboard) can serialize/deserialize +/// pane mode without leaking a UI framework reference into the contract layer. +/// KitX Dashboard has converters depending on this type — do not delete. +/// +/// +public enum NavigationViewPaneDisplayMode +{ + Auto = 0, + Left = 1, + Top = 2, + LeftCompact = 3, + LeftMinimal = 4 +} diff --git a/KitX Core Contracts/KitX.Core.Contract/Configuration/WindowState.cs b/KitX Core Contracts/KitX.Core.Contract/Configuration/WindowState.cs new file mode 100644 index 0000000..35cdca8 --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Configuration/WindowState.cs @@ -0,0 +1,22 @@ +using System; + +namespace KitX.Core.Contract.Configuration; + +/// +/// Window state enumeration (mirrors Avalonia.WindowState). +/// +/// This is a UI-layer mapping enum: it mirrors the Avalonia UI framework's +/// Avalonia.Controls.WindowState so the UI layer (KitX Dashboard) can +/// serialize/deserialize window state without leaking a UI framework reference +/// into the contract layer. KitX Dashboard has converters depending on this type +/// — do not delete. +/// +/// +public enum WindowState +{ + Normal, + Minimized, + Maximized, + FullScreen, + NonInteractive +} diff --git a/KitX Core Contracts/KitX.Core.Contract/Device/IDeviceService.cs b/KitX Core Contracts/KitX.Core.Contract/Device/IDeviceService.cs new file mode 100644 index 0000000..c1c6685 --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Device/IDeviceService.cs @@ -0,0 +1,179 @@ +using System; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using System.Collections.Generic; +using KitX.Shared.CSharp.Device; + +namespace KitX.Core.Contract.Device; + +/// +/// Device discovery service interface +/// +public interface IDeviceDiscoveryService +{ + /// + /// Gets the default device information + /// + DeviceInfo DefaultDeviceInfo { get; } + + /// + /// Gets the port the discovery service is running on + /// + int? Port { get; } + + /// + /// Starts the device discovery service + /// + /// The service instance + IDeviceDiscoveryService Run(); + + /// + /// Stops the device discovery service + /// + void Stop(); + + /// + /// Event raised when a device is discovered + /// + event EventHandler? DeviceDiscovered; + + /// + /// Event raised when a device goes offline + /// + event EventHandler? DeviceOffline; +} + +/// +/// Device server interface for HTTP API +/// +public interface IDeviceServer +{ + /// + /// Gets the port the server is running on + /// + int? Port { get; } + + /// + /// Starts the device server + /// + /// The server instance + IDeviceServer Run(); + + /// + /// Stops the device server + /// + void Stop(); + + /// + /// Checks if a device is signed in + /// + /// The device locator + /// True if the device is signed in + bool IsDeviceSignedIn(DeviceLocator locator); + + /// + /// Gets the signed device token for a device locator + /// + /// The device locator + /// The token or null if not found + string? GetDeviceToken(DeviceLocator locator); + + /// + /// Gets all signed-in device locators + /// + /// Read-only list of signed-in device locators + IReadOnlyList GetSignedInDevices(); +} + +/// +/// Devices organizer interface +/// +public interface IDevicesOrganizer +{ + /// + /// Event raised when a device is discovered + /// + event EventHandler? DeviceDiscovered; + + /// + /// Event raised when a device goes offline + /// + event EventHandler? DeviceOffline; +} + +/// +/// Device case interface +/// +public interface IDeviceCase +{ + /// + /// Gets or sets the device information + /// + DeviceInfo DeviceInfo { get; set; } + + /// + /// Gets a value indicating whether the device is authorized + /// + bool IsAuthorized { get; } + + /// + /// Gets a value indicating whether this is the main device + /// + bool IsMainDevice { get; } + + /// + /// Gets a value indicating whether the device is online + /// + bool IsOnline { get; } + + /// + /// Gets the last seen time + /// + DateTime LastSeen { get; } +} + +/// +/// Device discovered event arguments +/// +public class DeviceDiscoveredEventArgs : EventArgs +{ + /// + /// Gets or sets the device information + /// + public DeviceInfo? DeviceInfo { get; set; } +} + +/// +/// Device offline event arguments +/// +public class DeviceOfflineEventArgs : EventArgs +{ + /// + /// Gets or sets the device ID + /// + public string DeviceId { get; set; } = string.Empty; +} + +/// +/// Device HTTP client interface — sends requests to remote DevicesServer instances. +/// Used for cross-device plugin invocation via the /Api/V1/Plugin/Invoke endpoint. +/// (Moved to Contract so the Workflow library can depend on the abstraction without +/// referencing KitX.Core.) +/// +public interface IDeviceHttpClient +{ + /// + /// Invokes a plugin method on a remote device via HTTP POST to /Api/V1/Plugin/Invoke. + /// + /// Target device info (contains IPv4 and DevicesServerPort) + /// Valid session token for the target device + /// The Request object to send + /// Cancellation token + /// HTTP response from remote device, or null on network error + Task InvokePluginAsync( + DeviceInfo targetDevice, + string token, + KitX.Shared.CSharp.WebCommand.Request request, + CancellationToken ct = default); +} diff --git a/KitX Core Contracts/KitX.Core.Contract/Device/INetworkService.cs b/KitX Core Contracts/KitX.Core.Contract/Device/INetworkService.cs new file mode 100644 index 0000000..acb04e4 --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Device/INetworkService.cs @@ -0,0 +1,30 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace KitX.Core.Contract.Device; + +/// +/// Unified orchestrator for the device network stack (discovery UDP server, +/// device HTTP server, plugin WebSocket server). Owns startup ordering, port +/// configuration and shutdown. Implemented in KitX.Core. +/// +public interface INetworkService +{ + /// Starts all network servers asynchronously (honors DelayStartSeconds and SkipNetworkSystemOnStartup). + Task StartAsync(CancellationToken ct = default); + + /// Stops all running network servers. + Task StopAsync(CancellationToken ct = default); + + /// + /// Stops and restarts the device discovery + device HTTP servers only + /// (plugin server untouched). Waits for UDP sockets to settle before restarting. + /// + Task RestartDevicesServersAsync(CancellationToken ct = default); + + /// Stops the device discovery + device HTTP servers only (plugin server untouched). + Task StopDevicesServersAsync(CancellationToken ct = default); + + /// True when at least one server is running. + bool IsRunning { get; } +} diff --git a/KitX Core Contracts/KitX.Core.Contract/Device/ServerStatus.cs b/KitX Core Contracts/KitX.Core.Contract/Device/ServerStatus.cs new file mode 100644 index 0000000..c41bda0 --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Device/ServerStatus.cs @@ -0,0 +1,13 @@ +namespace KitX.Core.Contract.Device; + +/// +/// Server status enumeration +/// +public enum ServerStatus +{ + Pending, + Starting, + Running, + Stopping, + Errored +} diff --git a/KitX Core Contracts/KitX.Core.Contract/Event/EventArgs.cs b/KitX Core Contracts/KitX.Core.Contract/Event/EventArgs.cs new file mode 100644 index 0000000..50f6efd --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Event/EventArgs.cs @@ -0,0 +1,89 @@ +using System; +using KitX.Shared.CSharp.Device; +using KitX.Shared.CSharp.Plugin; + +namespace KitX.Core.Contract.Event; + +/// +/// Event args for port changed events +/// +public class PortChangedEventArgs : EventArgs +{ + /// + /// The new port number + /// + public int Port { get; set; } +} + +/// +/// Event args for device key events +/// +public class DeviceKeyEventArgs : EventArgs +{ + /// + /// The device key + /// + public string Key { get; set; } = string.Empty; +} + +/// +/// Event args for device info events +/// +public class DeviceInfoEventArgs : EventArgs +{ + /// + /// The device information + /// + public DeviceInfo? DeviceInfo { get; set; } +} + +/// +/// Event args for plugin events +/// +[Obsolete("Use KitX.Core.Contract.Plugin.Events.PluginRegisteredEventArgs or PluginUnregisteredEventArgs instead.")] +public class PluginEventArgs : EventArgs +{ + /// + /// The plugin information + /// + public PluginInfo? PluginInfo { get; set; } +} + +/// +/// Event args for plugin connection events +/// +public class PluginConnectionEventArgs : EventArgs +{ + /// + /// The connection ID + /// + public string ConnectionId { get; set; } = string.Empty; + + /// + /// The plugin information + /// + public PluginInfo? PluginInfo { get; set; } +} + +/// +/// Event args for exchange device key request events. +/// Published when a key exchange request is received, requiring user confirmation. +/// +public class ExchangeDeviceKeyEventArgs : EventArgs +{ + /// + /// The verification code displayed to the user for confirmation. + /// This code must match between the two devices for the exchange to proceed. + /// + public string VerificationCode { get; set; } = string.Empty; + + /// + /// The address of the requesting device + /// + public string RequestingDeviceAddress { get; set; } = string.Empty; + + /// + /// The encrypted device key from the request (for informational purposes only, not for UI display) + /// + public string? EncryptedDeviceKey { get; set; } +} diff --git a/KitX Core Contracts/KitX.Core.Contract/Event/EventNames.cs b/KitX Core Contracts/KitX.Core.Contract/Event/EventNames.cs new file mode 100644 index 0000000..d921005 --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Event/EventNames.cs @@ -0,0 +1,163 @@ +namespace KitX.Core.Contract.Event; + +/// +/// Event names for the event bus +/// +public static class EventNames +{ + /// + /// Language changed event + /// + public const string LanguageChanged = "LanguageChanged"; + + /// + /// Greeting text interval updated event + /// + public const string GreetingTextIntervalUpdated = "GreetingTextIntervalUpdated"; + + /// + /// App config changed event + /// + public const string AppConfigChanged = "AppConfigChanged"; + + /// + /// Plugins config changed event + /// + public const string PluginsConfigChanged = "PluginsConfigChanged"; + + /// + /// Mica opacity changed event + /// + public const string MicaOpacityChanged = "MicaOpacityChanged"; + + /// + /// Develop settings changed event + /// + public const string DevelopSettingsChanged = "DevelopSettingsChanged"; + + /// + /// Log config updated event + /// + public const string LogConfigUpdated = "LogConfigUpdated"; + + /// + /// Theme config changed event + /// + public const string ThemeConfigChanged = "ThemeConfigChanged"; + + /// + /// Use statistics changed event + /// + public const string UseStatisticsChanged = "UseStatisticsChanged"; + + /// + /// Devices server port changed event + /// + public const string DevicesServerPortChanged = "DevicesServerPortChanged"; + + /// + /// Plugins server port changed event + /// + public const string PluginsServerPortChanged = "PluginsServerPortChanged"; + + /// + /// Activities updated event + /// + public const string OnActivitiesUpdated = "OnActivitiesUpdated"; + + /// + /// Receive cancel exchanging device key event + /// + public const string OnReceiveCancelExchangingDeviceKey = "OnReceiveCancelExchangingDeviceKey"; + + /// + /// Exiting event + /// + public const string OnExiting = "OnExiting"; + + /// + /// Receiving device info event + /// + public const string OnReceivingDeviceInfo = "OnReceivingDeviceInfo"; + + /// + /// Config hot reloaded event + /// + public const string OnConfigHotReloaded = "OnConfigHotReloaded"; + + /// + /// Accepting device key event + /// + public const string OnAcceptingDeviceKey = "OnAcceptingDeviceKey"; + + /// + /// Receive exchange device key request event. + /// Published when a key exchange request is received, requiring user confirmation. + /// + public const string OnReceiveExchangeDeviceKey = "OnReceiveExchangeDeviceKey"; + + /// + /// Plugin connected event + /// + public const string PluginConnected = "PluginConnected"; + + /// + /// Plugin disconnected event + /// + public const string PluginDisconnected = "PluginDisconnected"; + + /// + /// Plugin registered event + /// + public const string PluginRegistered = "PluginRegistered"; + + /// + /// Plugin unregistered event + /// + public const string PluginUnregistered = "PluginUnregistered"; + + /// + /// Plugin message received event + /// + public const string PluginMessageReceived = "PluginMessageReceived"; + + /// + /// Plugin response event (has RequestId) + /// + public const string PluginResponse = "PluginResponse"; + + /// + /// Workflow created event + /// + public const string WorkflowCreated = "WorkflowCreated"; + + /// + /// Workflow deleted event + /// + public const string WorkflowDeleted = "WorkflowDeleted"; + + /// + /// Workflow renamed event + /// + public const string WorkflowRenamed = "WorkflowRenamed"; + + /// + /// Workflow data saved event + /// + public const string WorkflowDataSaved = "WorkflowDataSaved"; + + /// + /// Trigger fired event + /// + public const string TriggerFired = "TriggerFired"; + + /// + /// Workflow triggered event (a workflow was started by a trigger) + /// + public const string WorkflowTriggered = "WorkflowTriggered"; + + /// + /// Workflow execution result event (success or failure) + /// + public const string WorkflowExecutionResult = "WorkflowExecutionResult"; +} diff --git a/KitX Core Contracts/KitX.Core.Contract/Event/IEventService.cs b/KitX Core Contracts/KitX.Core.Contract/Event/IEventService.cs new file mode 100644 index 0000000..843b86a --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Event/IEventService.cs @@ -0,0 +1,58 @@ +using System; +using System.ComponentModel; + +namespace KitX.Core.Contract.Event; + +/// +/// Event service interface for global event bus +/// +public interface IEventService +{ + /// + /// Subscribes to an event + /// + /// The event name + /// The event handler + void Subscribe(string eventName, EventHandler handler); + + /// + /// Unsubscribes from an event + /// + /// The event name + /// The event handler + void Unsubscribe(string eventName, EventHandler handler); + + /// + /// Publishes an event + /// + /// The event name + /// The event arguments + void Publish(string eventName, EventArgs args); + + /// + /// Subscribes to a typed event + /// + /// The event args type + /// The event name + /// The event handler + void Subscribe(string eventName, EventHandler handler) + where TEventArgs : EventArgs; + + /// + /// Unsubscribes from a typed event + /// + /// The event args type + /// The event name + /// The event handler + void Unsubscribe(string eventName, EventHandler handler) + where TEventArgs : EventArgs; + + /// + /// Publishes a typed event + /// + /// The event args type + /// The event name + /// The event arguments + void Publish(string eventName, TEventArgs args) + where TEventArgs : EventArgs; +} diff --git a/KitX Core Contracts/KitX.Core.Contract/Event/WorkflowEventArgs.cs b/KitX Core Contracts/KitX.Core.Contract/Event/WorkflowEventArgs.cs new file mode 100644 index 0000000..cda2807 --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Event/WorkflowEventArgs.cs @@ -0,0 +1,98 @@ +using System; +using System.Collections.Generic; + +namespace KitX.Core.Contract.Event; + +/// +/// Event arguments for workflow rename events +/// +public class WorkflowRenamedEventArgs : EventArgs +{ + /// + /// The workflow ID that was renamed + /// + public string WorkflowId { get; } + + /// + /// The new name of the workflow + /// + public string NewName { get; } + + public WorkflowRenamedEventArgs(string workflowId, string newName) + { + WorkflowId = workflowId; + NewName = newName; + } +} + +/// +/// Event arguments for workflow data saved events +/// +public class WorkflowSavedEventArgs : EventArgs +{ + /// + /// The workflow ID that was saved + /// + public string WorkflowId { get; } + + /// + /// The workflow name at time of save + /// + public string WorkflowName { get; } + + /// + /// The workflow description at time of save + /// + public string Description { get; } + + /// + /// The workflow author at time of save + /// + public string Author { get; } + + public WorkflowSavedEventArgs(string workflowId, string workflowName, + string description = "", string author = "") + { + WorkflowId = workflowId; + WorkflowName = workflowName; + Description = description; + Author = author; + } +} + +/// +/// Event arguments for workflow execution result events +/// +public class WorkflowExecutionResultEventArgs : EventArgs +{ + /// + /// The workflow ID that was executed + /// + public string WorkflowId { get; } + + /// + /// Whether the execution succeeded + /// + public bool IsSuccess { get; } + + /// + /// Error message if execution failed + /// + public string? ErrorMessage { get; } + + /// + /// Lines of Print() output produced during execution (null if not captured). + /// Surfaced to the Debug activity log so users can see what the workflow printed + /// without opening the editor's output panel. + /// + public IReadOnlyList? Output { get; } + + public WorkflowExecutionResultEventArgs(string workflowId, bool isSuccess, + string? errorMessage = null, IReadOnlyList? output = null) + { + WorkflowId = workflowId; + IsSuccess = isSuccess; + ErrorMessage = errorMessage; + Output = output; + } +} diff --git a/KitX Core Contracts/KitX.Core.Contract/FileWatcher/IFileWatcherService.cs b/KitX Core Contracts/KitX.Core.Contract/FileWatcher/IFileWatcherService.cs new file mode 100644 index 0000000..037bc27 --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/FileWatcher/IFileWatcherService.cs @@ -0,0 +1,28 @@ +using System.IO; +using System.ComponentModel; + +namespace KitX.Core.Contract.FileWatcher; + +/// +/// File watcher service interface +/// +public interface IFileWatcherService +{ + /// + /// Registers a file watcher + /// + /// The file path to watch + /// The callback when file changes + void RegisterWatcher(string filePath, FileSystemEventHandler onChanged); + + /// + /// Unregisters a file watcher + /// + /// The file path to stop watching + void UnregisterWatcher(string filePath); + + /// + /// Clears all file watchers + /// + void Clear(); +} diff --git a/KitX Core Contracts/KitX.Core.Contract/Hotkey/IKeyHookService.cs b/KitX Core Contracts/KitX.Core.Contract/Hotkey/IKeyHookService.cs new file mode 100644 index 0000000..9e1dddb --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Hotkey/IKeyHookService.cs @@ -0,0 +1,32 @@ +using System; + +namespace KitX.Core.Contract.Hotkey; + +/// +/// Key hook service interface for global hotkeys +/// +public interface IKeyHookService +{ + /// + /// Starts the key hook + /// + void StartHook(); + + /// + /// Stops the key hook + /// + void StopHook(); + + /// + /// Registers a hotkey handler + /// + /// The keys sequence + /// The handler + void RegisterHotKeyHandler(string keysSequence, Action handler); + + /// + /// Unregisters a hotkey handler + /// + /// The keys sequence + void UnregisterHotKeyHandler(string keysSequence); +} diff --git a/KitX Core Contracts/KitX.Core.Contract/KitX-Background-ani.png b/KitX Core Contracts/KitX.Core.Contract/KitX-Background-ani.png new file mode 100644 index 0000000..7abdbc3 Binary files /dev/null and b/KitX Core Contracts/KitX.Core.Contract/KitX-Background-ani.png differ diff --git a/KitX Core Contracts/KitX.Core.Contract/KitX.Core.Contract.csproj b/KitX Core Contracts/KitX.Core.Contract/KitX.Core.Contract.csproj new file mode 100644 index 0000000..a621f46 --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/KitX.Core.Contract.csproj @@ -0,0 +1,48 @@ + + + + net10.0 + enable + True + + + + $(Version) + $(Version) + 24.10.$([System.DateTime]::UtcNow.Date.Subtract($([System.DateTime]::Parse("2024-02-07"))).TotalDays).$([System.Math]::Floor($([System.DateTime]::UtcNow.TimeOfDay.TotalMinutes))) + + + + KitX.Core.Contract.CSharp + Dynesshely + Crequency + Core service contracts for KitX Dashboard written in C# + AGPL-3.0-only + True + KitX-Background-ani.png + README.md + https://github.com/Crequency/KitX/ + https://github.com/Crequency/KitX-Standard/ + + + + + True + \ + + + True + \ + + + + + + + + + + + + + diff --git a/KitX Core Contracts/KitX.Core.Contract/Plugin/Events/PluginEventArgs.cs b/KitX Core Contracts/KitX.Core.Contract/Plugin/Events/PluginEventArgs.cs new file mode 100644 index 0000000..068086b --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Plugin/Events/PluginEventArgs.cs @@ -0,0 +1,122 @@ +using System; +using KitX.Shared.CSharp.Plugin; + +namespace KitX.Core.Contract.Plugin.Events; + +/// +/// Plugin status changed event arguments +/// +public class PluginStatusChangedEventArgs : EventArgs +{ + /// + /// Gets or sets the plugin ID + /// + public Guid PluginId { get; set; } + + /// + /// Gets or sets the plugin name + /// + public string PluginName { get; set; } = string.Empty; + + /// + /// Gets or sets the old status + /// + public PluginStatus OldStatus { get; set; } + + /// + /// Gets or sets the new status + /// + public PluginStatus NewStatus { get; set; } +} + +/// +/// Plugin response event arguments +/// +public class PluginResponseEventArgs : EventArgs +{ + /// + /// Gets or sets the request ID + /// + public string RequestId { get; set; } = string.Empty; + + /// + /// Gets or sets the response content + /// + public string Content { get; set; } = string.Empty; +} + +/// +/// Plugin status report event arguments +/// +public class PluginStatusReportEventArgs : EventArgs +{ + /// + /// Gets or sets the connection ID + /// + public string ConnectionId { get; set; } = string.Empty; + + /// + /// Gets or sets the status message + /// + public string Status { get; set; } = string.Empty; +} + +/// +/// Plugin registered event arguments +/// +public class PluginRegisteredEventArgs : EventArgs +{ + /// + /// Gets or sets the plugin info + /// + public PluginInfo? PluginInfo { get; set; } +} + +/// +/// Plugin unregistered event arguments +/// +public class PluginUnregisteredEventArgs : EventArgs +{ + /// + /// Gets or sets the plugin info + /// + public PluginInfo? PluginInfo { get; set; } +} + +/// +/// Plugin connected event arguments +/// +public class PluginConnectedEventArgs : EventArgs +{ + /// + /// Gets or sets the connection ID + /// + public string? ConnectionId { get; set; } +} + +/// +/// Plugin disconnected event arguments +/// +public class PluginDisconnectedEventArgs : EventArgs +{ + /// + /// Gets or sets the connection ID + /// + public string? ConnectionId { get; set; } +} + +/// +/// Plugin message received event arguments +/// +public class PluginMessageReceivedEventArgs : EventArgs +{ + /// + /// Gets or sets the connection ID + /// + public string? ConnectionId { get; set; } + + /// + /// Gets or sets the message + /// + public string? Message { get; set; } +} \ No newline at end of file diff --git a/KitX Core Contracts/KitX.Core.Contract/Plugin/IPluginConnection.cs b/KitX Core Contracts/KitX.Core.Contract/Plugin/IPluginConnection.cs new file mode 100644 index 0000000..d343993 --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Plugin/IPluginConnection.cs @@ -0,0 +1,48 @@ +using System; +using KitX.Shared.CSharp.Plugin; +using KitX.Core.Contract.Device; +using CTask = System.Threading.Tasks.Task; + +namespace KitX.Core.Contract.Plugin; + +/// +/// Plugin connection interface +/// +public interface IPluginConnection : IPluginConnector +{ + /// + /// Gets or sets the plugin info + /// + new PluginInfo? PluginInfo { get; set; } + + /// + /// Gets the connection status + /// + ServerStatus Status { get; } + + /// + /// Event raised when a message is received + /// + event EventHandler? MessageReceived; + + /// + /// Event raised when connection is closed + /// + event EventHandler? Closed; + + /// + /// Initializes the connection + /// + void Initialize(); + + /// + /// Sends a message + /// + /// The message to send + void Send(string message); + + /// + /// Closes the connection + /// + CTask CloseAsync(); +} diff --git a/KitX Core Contracts/KitX.Core.Contract/Plugin/IPluginConnector.cs b/KitX Core Contracts/KitX.Core.Contract/Plugin/IPluginConnector.cs new file mode 100644 index 0000000..5f63eff --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Plugin/IPluginConnector.cs @@ -0,0 +1,37 @@ +using System; +using KitX.Shared.CSharp.Plugin; +using KitX.Core.Contract.Plugin.Events; + +namespace KitX.Core.Contract.Plugin; + +/// +/// Plugin connector interface for managing individual plugin connections +/// +public interface IPluginConnector +{ + /// + /// Gets the connection ID + /// + string? ConnectionId { get; } + + /// + /// Gets the plugin info + /// + PluginInfo? PluginInfo { get; } + + /// + /// Sends a request to the plugin + /// + /// The request to send + void Request(object request); + + /// + /// Event raised when a plugin response is received + /// + event EventHandler? PluginResponse; + + /// + /// Event raised when plugin reports status + /// + event EventHandler? StatusReport; +} \ No newline at end of file diff --git a/KitX Core Contracts/KitX.Core.Contract/Plugin/IPluginServer.cs b/KitX Core Contracts/KitX.Core.Contract/Plugin/IPluginServer.cs new file mode 100644 index 0000000..871277a --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Plugin/IPluginServer.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using KitX.Shared.CSharp.Plugin; +using KitX.Core.Contract.Plugin.Events; + +namespace KitX.Core.Contract.Plugin; + +/// +/// Plugin server interface for managing plugin connections +/// +public interface IPluginServer +{ + /// + /// Gets the port the server is running on + /// + int? Port { get; } + + /// + /// Gets the list of currently connected plugins + /// + IReadOnlyList Connections { get; } + + /// + /// Starts the plugin server + /// + /// The server instance + IPluginServer Run(); + + /// + /// Stops the plugin server + /// + void Stop(); + + /// + /// Finds a connector for a specific plugin + /// + /// The plugin info + /// The plugin connector or null if not found + IPluginConnector? FindConnector(PluginInfo pluginInfo); + + /// + /// Finds a connection by connection ID + /// + /// The connection ID + /// The plugin connection or null if not found + IPluginConnection? FindConnection(string connectionId); + + /// + /// Event raised when server port changes + /// + event EventHandler? PortChanged; + + /// + /// Event raised when a plugin connects + /// + event EventHandler? PluginConnected; + + /// + /// Event raised when a plugin disconnects + /// + event EventHandler? PluginDisconnected; + + /// + /// Event raised when a plugin message is received + /// + event EventHandler? PluginMessageReceived; + + /// + /// Event raised when a plugin registers with the server + /// + event EventHandler? PluginRegistered; + + /// + /// Event raised when a plugin unregisters/disconnects from the server + /// + event EventHandler? PluginUnregistered; + + /// + /// Event raised when a plugin sends a response (has RequestId) + /// + event EventHandler? PluginResponse; +} \ No newline at end of file diff --git a/KitX Core Contracts/KitX.Core.Contract/Plugin/IPluginService.cs b/KitX Core Contracts/KitX.Core.Contract/Plugin/IPluginService.cs new file mode 100644 index 0000000..495e5a2 --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Plugin/IPluginService.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using KitX.Core.Contract.Configuration; +using KitX.Shared.CSharp.Plugin; +using KitX.Core.Contract.Plugin.Events; + +namespace KitX.Core.Contract.Plugin; + +/// +/// Plugin management service interface +/// +public interface IPluginService +{ + /// + /// Gets all installed plugins + /// + IReadOnlyList GetInstalledPlugins(); + + /// + /// Gets a plugin by its ID + /// + /// The plugin ID + /// The plugin installation or null if not found + IPluginInstallation? GetPlugin(Guid pluginId); + + /// + /// Imports a plugin package (.kxp file) + /// + /// Path to the .kxp file + /// True if import was successful + Task ImportPluginAsync(string kxpFilePath); + + /// + /// Removes a plugin + /// + /// The plugin ID + /// True if removal was successful + Task RemovePluginAsync(Guid pluginId); + + /// + /// Starts a plugin + /// + /// The plugin ID + /// True if start was successful + Task StartPluginAsync(Guid pluginId); + + /// + /// Stops a plugin + /// + /// The plugin ID + /// True if stop was successful + Task StopPluginAsync(Guid pluginId); + + /// + /// Calls a plugin function + /// + /// The plugin ID + /// The function name + /// Optional parameters + /// The function result + Task CallPluginFunctionAsync(Guid pluginId, string functionName, Dictionary? parameters = null); + + /// + /// Event raised when plugin status changes + /// + event EventHandler? PluginStatusChanged; +} \ No newline at end of file diff --git a/KitX Core Contracts/KitX.Core.Contract/Plugin/PluginStatus.cs b/KitX Core Contracts/KitX.Core.Contract/Plugin/PluginStatus.cs new file mode 100644 index 0000000..0586cfe --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Plugin/PluginStatus.cs @@ -0,0 +1,32 @@ +namespace KitX.Core.Contract.Plugin; + +/// +/// Plugin status enumeration +/// +public enum PluginStatus +{ + /// + /// Unknown status + /// + Unknown, + + /// + /// Plugin is installed but not running + /// + Installed, + + /// + /// Plugin is running + /// + Running, + + /// + /// Plugin was running but is now stopped + /// + Stopped, + + /// + /// Plugin encountered an error + /// + Error +} \ No newline at end of file diff --git a/KitX Core Contracts/KitX.Core.Contract/README.md b/KitX Core Contracts/KitX.Core.Contract/README.md new file mode 100644 index 0000000..0bbdea1 --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/README.md @@ -0,0 +1,129 @@ +# KitX.Core.Contract.CSharp + +Core service contracts for KitX Dashboard written in C#. + +## Overview + +This project defines the service interfaces that separate the UI layer (Dashboard) from the core business logic layer. These interfaces enable: + +- **Dependency Injection**: Core services can be injected into ViewModels +- **Testability**: Core logic can be tested independently of the UI +- **Flexibility**: Multiple frontends (Dashboard, CLI, etc.) can use the same core services +- **Maintainability**: Clear boundaries between UI and business logic + +## Architecture + +``` +UI Layer (Dashboard, CLI, etc.) + ↓ depends on +Core Service Contracts (interfaces) + ↓ implemented by +Core Service Implementations (Managers) +``` + +## Service Interfaces + +### Configuration +- `IConfigService` - Configuration management service +- `IAppConfig` - Application configuration (aggregate root) +- `IAppConf` - Application base configuration section +- `IPluginsConf` - Plugins configuration +- `ISecurityConf` - Security configuration +- `IAnnouncementConf` - Announcements configuration +- `ILogConf` - Log configuration section +- `IPagesConf` - Pages configuration section +- `IWindowsConf` - Windows configuration section +- `IWebConf` - Web configuration section +- `IIOConf` - IO configuration section +- `IActivityConf` - Activity configuration section +- `ILoadersConf` - Loaders configuration section + +### Plugin Management +- `IPluginService` - Plugin lifecycle management +- `IPluginServer` - WebSocket server for plugin connections +- `IPluginConnector` - Individual plugin connection handler + +### Device Management +- `IDeviceDiscoveryService` - UDP broadcast device discovery +- `IDeviceServer` - HTTP API server for device communication +- `IDevicesOrganizer` - Device organization and tracking + +### Security +- `ISecurityService` - Encryption, decryption, and device key management + +### Activity Logging +- `IActivityService` - Activity recording and statistics + +### Statistics +- `IStatisticsService` - Application usage statistics + +### Workflow +- `IWorkflowService` - Workflow script execution +- `IPluginServiceProvider` - Plugin integration for workflow scripts + +### Event System +- `IEventService` - Global event bus for component communication + +### Task Management +- `ITasksService` - Background task execution + +### File Watching +- `IFileWatcherService` - File system monitoring for hot reload + +### Hotkeys +- `IKeyHookService` - Global hotkey registration and handling + +### Announcements +- `IAnnouncementService` - Announcement fetching and display + +## Usage Example + +```csharp +using KitX.Core.Contract.Configuration; +using KitX.Core.Contract.Plugin; + +public class MyViewModel +{ + private readonly IConfigService _configService; + private readonly IPluginService _pluginService; + + public MyViewModel( + IConfigService configService, + IPluginService pluginService) + { + _configService = configService; + _pluginService = pluginService; + + // Subscribe to events + _pluginService.PluginStatusChanged += OnPluginStatusChanged; + } + + private void OnPluginStatusChanged(object? sender, PluginStatusChangedEventArgs e) + { + // Handle plugin status change + } + + public async Task ImportPlugin(string filePath) + { + var success = await _pluginService.ImportPluginAsync(filePath); + if (success) + { + _configService.SaveAll(); + } + } +} +``` + +## Dependencies + +- .NET Standard 2.0/2.1 +- KitX.Shared.CSharp - Shared data models + +## License + +AGPL-3.0-only + +## Links + +- [KitX Repository](https://github.com/Crequency/KitX/) +- [KitX Standard Repository](https://github.com/Crequency/KitX-Standard/) diff --git a/KitX Core Contracts/KitX.Core.Contract/Security/IDeviceKeyService.cs b/KitX Core Contracts/KitX.Core.Contract/Security/IDeviceKeyService.cs new file mode 100644 index 0000000..bf770dd --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Security/IDeviceKeyService.cs @@ -0,0 +1,60 @@ +using System.Collections.Generic; +using KitX.Shared.CSharp.Device; +using KitXIDeviceKey = KitX.Core.Contract.Configuration.IDeviceKey; + +namespace KitX.Core.Contract.Security; + +/// +/// Device key management service interface +/// +public interface IDeviceKeyService +{ + /// + /// Gets all device keys + /// + IReadOnlyList GetDeviceKeys(); + + /// + /// Adds a device key + /// + /// The MAC address + /// The device name + /// The public key + /// True if addition was successful + bool AddDeviceKey(string macAddress, string deviceName, string publicKey); + + /// + /// Removes a device key + /// + /// The MAC address + /// True if removal was successful + bool RemoveDeviceKey(string macAddress); + + /// + /// Searches for a device key by device locator + /// + /// The device locator + /// The device key if found, otherwise null + DeviceKey? SearchDeviceKey(DeviceLocator locator); + + /// + /// Checks if a device key is correct + /// + /// The device locator + /// The device key to verify + /// True if the key is correct + bool IsDeviceKeyCorrect(DeviceLocator locator, DeviceKey key); + + /// + /// Checks if a device is authorized + /// + /// The device locator + /// True if the device is authorized + bool IsDeviceAuthorized(DeviceLocator device); + + /// + /// Gets the private device key for local device + /// + /// The private device key, or null if not available + DeviceKey? GetPrivateDeviceKey(); +} diff --git a/KitX Core Contracts/KitX.Core.Contract/Security/IEncryptionService.cs b/KitX Core Contracts/KitX.Core.Contract/Security/IEncryptionService.cs new file mode 100644 index 0000000..3ae516d --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Security/IEncryptionService.cs @@ -0,0 +1,75 @@ +using System.Threading.Tasks; +using KitX.Shared.CSharp.Security; + +namespace KitX.Core.Contract.Security; + +/// +/// Encryption service interface +/// +public interface IEncryptionService +{ + /// + /// Encrypts a string + /// + /// The content to encrypt + /// The target device MAC address + /// The encrypted content + Task EncryptStringAsync(string content, string targetDeviceMacAddress); + + /// + /// Decrypts a string + /// + /// The encrypted content + /// The source device MAC address + /// The decrypted content + Task DecryptStringAsync(string encryptedContent, string sourceDeviceMacAddress); + + /// + /// Encrypts a string using RSA with a specific device's public key + /// + /// The device key containing the public key + /// The data to encrypt + /// The encrypted data as Base64 string + string? RsaEncryptString(Shared.CSharp.Device.DeviceKey key, string data); + + /// + /// Decrypts a string using RSA with a specific device's private key + /// + /// The device key containing the private key + /// The encrypted data as Base64 string + /// The decrypted data + string? RsaDecryptString(Shared.CSharp.Device.DeviceKey key, string encryptedData); + + /// + /// Encrypts content using RSA+AES hybrid encryption + /// + /// The device key + /// The content to encrypt + /// The encrypted content + EncryptedContent RsaEncryptContent(Shared.CSharp.Device.DeviceKey key, string content); + + /// + /// Decrypts content using RSA+AES hybrid decryption + /// + /// The device key + /// The encrypted content + /// The decrypted content + string RsaDecryptContent(Shared.CSharp.Device.DeviceKey key, EncryptedContent content); + + /// + /// Encrypts a string with AES + /// + /// The source string + /// The encryption key + /// The encrypted string + string AesEncrypt(string source, string key); + + /// + /// Decrypts a string with AES + /// + /// The source string + /// The decryption key + /// Whether the source is in Base64 + /// The decrypted string + string AesDecrypt(string source, string key, bool isSourceInBase64 = true); +} diff --git a/KitX Core Contracts/KitX.Core.Contract/Statistics/IStatisticsService.cs b/KitX Core Contracts/KitX.Core.Contract/Statistics/IStatisticsService.cs new file mode 100644 index 0000000..13d0310 --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Statistics/IStatisticsService.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Threading.Tasks; + +namespace KitX.Core.Contract.Statistics; + +/// +/// Statistics service interface +/// +public interface IStatisticsService +{ + /// + /// Starts statistics collection + /// + void Start(); + + /// + /// Stops statistics collection + /// + void Stop(); + + /// + /// Gets usage statistics + /// + /// Start date + /// End date + /// Usage statistics + IUsageStatistics GetUsageStatistics(DateTime startDate, DateTime endDate); +} + +/// +/// Usage statistics interface +/// +public interface IUsageStatistics +{ + /// + /// Gets the total usage in seconds + /// + double TotalUsageSeconds { get; } + + /// + /// Gets the daily usage dictionary + /// + Dictionary DailyUsage { get; } +} diff --git a/KitX Core Contracts/KitX.Core.Contract/Tasks/ITasksService.cs b/KitX Core Contracts/KitX.Core.Contract/Tasks/ITasksService.cs new file mode 100644 index 0000000..e2b3f3a --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Tasks/ITasksService.cs @@ -0,0 +1,35 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace KitX.Core.Contract.Tasks; + +/// +/// Tasks service interface for background task management +/// +public interface ITasksService +{ + /// + /// Runs a synchronous task + /// + /// The task to run + /// Optional task name + void RunTask(Action task, string? taskName = null); + + /// + /// Runs an asynchronous task + /// + /// The task to run + /// Optional task name + /// Task representing the async operation + Task RunTaskAsync(Func task, string? taskName = null); + + /// + /// Runs an asynchronous task with cancellation support + /// + /// The task to run + /// Cancellation token + /// Optional task name + /// Task representing the async operation + Task RunTaskAsync(Func task, CancellationToken cancellationToken, string? taskName = null); +} diff --git a/KitX Core Contracts/KitX.Core.Contract/Workflow/BlueprintModels.cs b/KitX Core Contracts/KitX.Core.Contract/Workflow/BlueprintModels.cs new file mode 100644 index 0000000..df2a7c4 --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Workflow/BlueprintModels.cs @@ -0,0 +1,231 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json.Serialization; +using System.Threading.Tasks; + +namespace KitX.Core.Contract.Workflow; + +/// +/// Simple 2D point structure for view positioning +/// +public struct ViewPoint +{ + public double X { get; set; } + public double Y { get; set; } + + public ViewPoint(double x, double y) + { + X = x; + Y = y; + } + + public static implicit operator (double X, double Y)(ViewPoint p) => (p.X, p.Y); + public static implicit operator ViewPoint((double X, double Y) p) => new(p.X, p.Y); +} + +/// +/// Connection between two pins +/// +public class BlueprintConnection +{ + /// +/// Unique identifier +/// + public string Id { get; set; } = Guid.NewGuid().ToString(); + + /// + /// Source node ID + /// + public string SourceNodeId { get; set; } = string.Empty; + + /// + /// Source pin ID + /// + public string SourcePinId { get; set; } = string.Empty; + + /// + /// Target node ID + /// + public string TargetNodeId { get; set; } = string.Empty; + + /// + /// Target pin ID + /// + public string TargetPinId { get; set; } = string.Empty; + + /// + /// Corresponding PubVar name for data flow connections (optional) + /// + public string? PubVarName { get; set; } +} + +/// +/// A statement-level (data-connection subgraph) comment. Backs the KS +/// LeadingComment through the BP round-trip: one KS statement maps to one +/// data-connection subgraph, and this comment annotates that whole subgraph. +/// is the statement's primary node (the node the exec +/// chain enters) so the reverse translator can reattach it. +/// lists the subgraph's nodes for frontend box-rendering (optional). +/// +public class BlueprintGroupComment +{ + /// The comment text (may contain multiple lines joined by \n). + public string Comment { get; set; } = string.Empty; + + /// + /// The statement's primary node id (the node the exec chain enters — Branch/Each/ + /// While/Switch/control node, or the last function node of a pipeline). The reverse + /// translator matches leading comments by this id. + /// + public string AnchorNodeId { get; set; } = string.Empty; + + /// + /// All node ids belonging to this statement's data-connection subgraph (for frontend + /// box/highlight rendering). Optional; may be empty. + /// + public List NodeIds { get; set; } = []; +} + +/// +/// Blueprint document container +/// +public class Blueprint +{ + /// + /// Unique identifier + /// + public string Id { get; set; } = Guid.NewGuid().ToString(); + + /// + /// Document name + /// + public string Name { get; set; } = "Untitled"; + + /// + /// Creation timestamp + /// + public DateTime CreatedAt { get; set; } = DateTime.Now; + + /// + /// Last modified timestamp + /// + public DateTime ModifiedAt { get; set; } = DateTime.Now; + + /// + /// All nodes in this blueprint + /// + public List Nodes { get; set; } = []; + + /// + /// All connections in this blueprint + /// + public List Connections { get; set; } = []; + + /// + /// View zoom level (0.1 to 5.0) + /// + public double ZoomLevel { get; set; } = 1.0; + + /// + /// View pan offset + /// + public ViewPoint PanOffset { get; set; } = new(0, 0); + + /// + /// Helper functions available in this blueprint + /// + public List HelperFunctions { get; set; } = []; + + /// + /// PubVar variable names (invisible in Blueprint, used for data flow) + /// + public List PubVarNames { get; set; } = []; + + /// + /// Constant values (from ConstBlock) + /// + public List ConstValues { get; set; } = []; + + /// + /// Statement-level (data-connection subgraph) comments. Each entry attaches a + /// leading comment to the set of nodes forming one KS statement's data subgraph + /// (one KS statement == one data-connection subgraph). + /// is the statement's primary node (the node the exec chain enters), used by the + /// reverse translator to reattach the comment as a leading comment. + /// Empty for blueprints without preserved leading comments. + /// + public List GroupComments { get; set; } = []; + + /// + /// Ids of every statement's *primary* node — the node the exec chain enters for that + /// statement (Branch/Each/While/Switch control-flow node, or the last function/tap + /// node of a pipeline). Populated by the renderer; the frontend uses it to offer + /// group-comment anchoring on valid statement leaders (an arbitrary non-leader node + /// cannot carry a leading comment, since the reverse translator reattaches comments + /// by anchor node id). + /// Empty for blueprints that predate this field. + /// + public List StatementPrimaryNodeIds { get; set; } = []; + + /// + /// Maps every node that belongs to a statement's *data subgraph* to that statement's + /// primary (leader) node id. A data subgraph is the connected component of data edges + /// reachable from the statement's primary node (KS one line ⇔ one data subgraph; + /// subgraphs never overlap). The frontend uses this to attach a group comment to any + /// data node — it lands on the containing statement's primary, matching the KS→BP + /// anchoring. Empty for blueprints that predate this field. + /// + public Dictionary StatementNodeToPrimary { get; set; } = new(); + + /// + /// Get node by ID + /// + public BlueprintNode? GetNodeById(string nodeId) + { + foreach (var node in Nodes) + if (node.Id == nodeId) return node; + return null; + } + + /// + /// Get connections from a node + /// + public IEnumerable GetConnectionsFrom(string nodeId) + { + foreach (var conn in Connections) + if (conn.SourceNodeId == nodeId) yield return conn; + } + + /// + /// Get connections to a node + /// + public IEnumerable GetConnectionsTo(string nodeId) + { + foreach (var conn in Connections) + if (conn.TargetNodeId == nodeId) yield return conn; + } + + /// + /// Add a node to this blueprint, automatically setting the back-reference + /// + /// Node to add + public void AddNode(BlueprintNode node) + { + node.Blueprint = this; + Nodes.Add(node); + } + + /// + /// Add a connection to this blueprint (deduplicates by source/target/pin) + /// + /// Connection to add + public void AddConnection(BlueprintConnection connection) + { + var alreadyExists = Connections.Any(c => + c.SourceNodeId == connection.SourceNodeId && c.SourcePinId == connection.SourcePinId && + c.TargetNodeId == connection.TargetNodeId && c.TargetPinId == connection.TargetPinId); + if (!alreadyExists) + Connections.Add(connection); + } +} diff --git a/KitX Core Contracts/KitX.Core.Contract/Workflow/BpEditAction.cs b/KitX Core Contracts/KitX.Core.Contract/Workflow/BpEditAction.cs new file mode 100644 index 0000000..29e8fae --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Workflow/BpEditAction.cs @@ -0,0 +1,44 @@ +// v5.2 BP editing action hierarchy (command pattern). +// Pure data records — no dependency on CFG or Workflow types. +// Dashboard creates these to describe a user edit; IBpEditApplier translates them to CFG mutations. + +namespace KitX.Core.Contract.Workflow; + +public abstract record BpEditAction; + +// ── Node-level ── + +/// Insertion index within the block (null = append). +public record AddNodeInBlock(string BlockName, string BpNodeKind, int? Position = null) : BpEditAction; + +/// The BP node id to delete. +public record DeleteNode(string NodeId) : BpEditAction; + +/// Destination block. +/// Insertion index within the target block (null = append). +public record MoveNodeToBlock(string NodeId, string TargetBlock, int? Position = null) : BpEditAction; + +/// Zero-based argument index. +public record SetNodeArgument(string NodeId, int ArgIndex, string Value) : BpEditAction; + +// ── Connection-level (data flow) ── + +public record ConnectData(string SourceNodeId, string SourcePin, string TargetNodeId, string TargetPin, string? PubVarName = null) : BpEditAction; + +public record Disconnect(string ConnectionId) : BpEditAction; + +// ── Control flow (Exec arms) ── + +public record SetControlFlowArm(string NodeId, string ArmPinName, string TargetBlockName) : BpEditAction; + +// ── Block-level ── + +public record AddBlock(string BlockName) : BpEditAction; + +public record RenameBlock(string OldName, string NewName) : BpEditAction; + +public record DeleteBlock(string BlockName) : BpEditAction; + +// ── Position (G5) ── + +public record MoveNodePosition(string NodeId, double X, double Y) : BpEditAction; diff --git a/KitX Core Contracts/KitX.Core.Contract/Workflow/IBlueprintDebugController.cs b/KitX Core Contracts/KitX.Core.Contract/Workflow/IBlueprintDebugController.cs new file mode 100644 index 0000000..54b172f --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Workflow/IBlueprintDebugController.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace KitX.Core.Contract.Workflow; + +public enum ExecutionSpeed +{ + RealTime, + Slow, + StepByStep +} + +public interface IBlueprintDebugController +{ + event Action? NodeExecuting; + event Action? NodeExecuted; + event Action? BlockEntered; + event Action? VariableChanged; + event Action? ExecutionPaused; + event Action? ExecutionResumed; + + void SetBreakpoint(string nodeId); + void RemoveBreakpoint(string nodeId); + void ClearBreakpoints(); + bool HasBreakpoint(string nodeId); + + void Pause(); + void StepNext(); + void Continue(); + void SetSpeed(ExecutionSpeed speed); + + ExecutionSpeed Speed { get; } + bool IsPaused { get; } + + IReadOnlyDictionary CurrentVariableSnapshot { get; } + + void UpdateVariableSnapshot(Dictionary variables); + + /// + /// Forwards a named runtime value change to subscribers via + /// . Used by the generated workflow code to + /// publish both PubVar writes (name = PubVar identifier) and wire-value + /// updates (name = "w:{nodeId}" or "w:{nodeId}:{pinName}"). + /// The naming convention lets the frontend distinguish the two categories + /// by checking the w: prefix. + /// + /// + /// Discussion notes §十二-M (data-tooltip MVP): wire values flow on the same + /// channel as PubVar changes so the existing VariableChanged event and the + /// existing frontend plumbing (BlueprintConnectorVM.RuntimeValue) can be + /// reused without a separate WireValueChanged event. + /// + void NotifyValueChanged(string name, object? value); + + Task CheckpointAsync( + string statementId, string? blockName, + System.Threading.CancellationToken cancellationToken); +} diff --git a/KitX Core Contracts/KitX.Core.Contract/Workflow/ITriggerManager.cs b/KitX Core Contracts/KitX.Core.Contract/Workflow/ITriggerManager.cs new file mode 100644 index 0000000..c65edc7 --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Workflow/ITriggerManager.cs @@ -0,0 +1,34 @@ +namespace KitX.Core.Contract.Workflow; + +/// +/// Routes plugin trigger signals to the workflows that subscribed to them. +/// +/// A trigger is a pure signal (equivalent to pressing the "Run" button); it carries +/// no business payload. Plugins fire triggers via the TriggerFired command; the +/// matches the firing plugin/trigger against registered +/// subscriptions and runs each matching workflow. +/// +/// Subscriptions are purely RUNTIME state: a workflow is armed only while the user +/// keeps it Running (Run = register, Stop = unregister). There is deliberately NO +/// startup re-subscription from persisted TriggerConfig — the Dashboard would +/// otherwise silently arm every saved workflow at launch, desyncing the card's +/// mounted indicator. A user-configurable "auto-start workflows at KitX launch" +/// mechanism is planned as part of the Toolkit system (see Toolkit功能需求文档.md). +/// +public interface ITriggerManager +{ + /// + /// Registers a workflow's trigger configuration so that matching + /// TriggerFired events from plugins will run the workflow. + /// Only PluginEvent triggers with a non-empty plugin name take effect. + /// + /// The workflow identifier. + /// The trigger configuration. + void RegisterWorkflowTrigger(string workflowId, TriggerConfig config); + + /// + /// Removes the trigger subscription for a workflow (e.g. on delete/rename). + /// + /// The workflow identifier. + void UnregisterWorkflowTrigger(string workflowId); +} diff --git a/KitX Core Contracts/KitX.Core.Contract/Workflow/IWorkflowService.cs b/KitX Core Contracts/KitX.Core.Contract/Workflow/IWorkflowService.cs new file mode 100644 index 0000000..dedfc82 --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Workflow/IWorkflowService.cs @@ -0,0 +1,104 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using KitX.Shared.CSharp.Plugin; + +namespace KitX.Core.Contract.Workflow; + +/// +/// Workflow management interface +/// +public interface IWorkflowManagementService +{ + /// + /// Runs a workflow + /// + Task RunWorkflowAsync(string workflowId); + + /// + /// Runs a workflow and returns the full execution result including Print() output. + /// Use this when the caller needs the execution output (e.g. the Debug activity log). + /// + Task RunWorkflowWithDetailsAsync(string workflowId); + + /// + /// Stops a workflow + /// + Task StopWorkflowAsync(string workflowId); + + /// + /// Compiles a workflow's BlockScript into a persisted assembly on disk. + /// + /// The workflow ID to compile and persist. + /// True if compilation and persistence succeeded. + Task CompileAndPersistWorkflowAsync(string workflowId); +} + +/// +/// Workflow case interface +/// +public interface IWorkflowCase +{ + /// + /// Gets the workflow ID + /// + string Id { get; } + + /// + /// Gets or sets the workflow name + /// + string Name { get; set; } + + /// + /// Gets or sets the workflow description + /// + string Description { get; set; } + + /// + /// Gets or sets the author name + /// + string Author { get; set; } + + /// + /// Gets or sets a value indicating whether the workflow is running + /// + bool IsRunning { get; set; } + + /// + /// Gets or sets a value indicating whether the workflow is in an error state + /// + bool IsError { get; set; } + + /// + /// Gets or sets the error message if the workflow is in an error state + /// + string? ErrorMessage { get; set; } + + /// + /// Gets or sets the script file path + /// + string? ScriptPath { get; set; } + + /// + /// Gets the creation time + /// + DateTime CreatedTime { get; } + + /// + /// Gets or sets the last modified time + /// + DateTime LastModifiedTime { get; set; } + + /// + /// Gets or sets the trigger configuration + /// + TriggerConfig? TriggerConfig { get; set; } +} + +/// +/// Result of a workflow run, including the Print() output lines captured during execution. +/// +public record WorkflowRunResult( + bool IsSuccess, + string? ErrorMessage, + IReadOnlyList? Output); diff --git a/KitX Core Contracts/KitX.Core.Contract/Workflow/IWorkflowStorageService.cs b/KitX Core Contracts/KitX.Core.Contract/Workflow/IWorkflowStorageService.cs new file mode 100644 index 0000000..0ec2a37 --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Workflow/IWorkflowStorageService.cs @@ -0,0 +1,60 @@ +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace KitX.Core.Contract.Workflow; + +/// +/// Workflow storage service interface - manages workflow file persistence +/// +public interface IWorkflowStorageService +{ + /// + /// Gets the storage directory path (e.g., ./Data/Workflows/) + /// + string StorageDirectory { get; } + + /// + /// Creates a new workflow with an empty IR (P5-A4). The + /// selects the stored IR format: "v6" (WorkflowV6, default) or "v5" (WorkflowIR, + /// legacy/archived — v5 has been archived, new workflows are produced as v6), + /// mirroring so the editor window dispatches correctly. + /// + /// Workflow name + /// Optional description + /// The stored IR format: "v6" or "v5". + /// The created workflow case + Task CreateWorkflowAsync(string name, string? description = null, string irVersion = "v6"); + + /// + /// Loads workflow data from a .kcs file + /// + /// Workflow ID + /// The loaded KCS file data, or null if not found + Task LoadWorkflowDataAsync(string workflowId); + + /// + /// Saves workflow data to a .kcs file + /// + /// Workflow ID + /// The KCS file data to save + Task SaveWorkflowDataAsync(string workflowId, KcsFileFormat data); + + /// + /// Deletes a workflow and its .kcs file + /// + /// Workflow ID + Task DeleteWorkflowAsync(string workflowId); + + /// + /// Discovers all stored workflows by scanning the storage directory + /// + /// List of discovered workflow cases + Task> DiscoverWorkflowsAsync(); + + /// + /// Gets the file path for a workflow + /// + /// Workflow ID + /// Full file path + string GetWorkflowFilePath(string workflowId); +} diff --git a/KitX Core Contracts/KitX.Core.Contract/Workflow/KcsFileFormat.cs b/KitX Core Contracts/KitX.Core.Contract/Workflow/KcsFileFormat.cs new file mode 100644 index 0000000..7ce9ff4 --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Workflow/KcsFileFormat.cs @@ -0,0 +1,188 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace KitX.Core.Contract.Workflow; + +// ───────────────────────────────────────────────────────────────────────────── +// KcsFileFormat v2 — IR as the single source of truth. +// +// v1 stored three redundant representations side-by-side: BS text +// (MainProgram/BlockScriptSource), the mutable Blueprint graph (BlueprintData), +// and the v5.1 CFG DTO (CfgData). Under the v6.0 architecture (IR-Architecture- +// v6.0.md §2) IR is the sole truth and BS/BP are projections produced on demand +// by BsTextLens.Project / BpGraphLens.Project. v2 collapses the three into one +// stored IR blob (IrData) plus the pure-metadata envelope fields that are NOT +// part of IR semantics (identity, authoring, trigger config, user constant +// overrides). +// +// Dropped v1 fields → how they are recovered +// MainProgram BsTextLens.Project(ir) (on-demand BS view) +// BlockScriptSource ditto +// BlueprintData BpGraphLens.Project(ir) (on-demand BP view) +// CfgData replaced wholesale by IrData (IrDto v6.0) +// UseBlockMode meaningless under IR-as-truth (no "mode") +// HelperFunctions already carried by IrWorkflow.HelperFunctions +// +// Kept envelope fields (NOT derivable from IR): +// Id / Name / Description / Author / timestamps — workflow identity/metadata +// TriggerConfig — deployment/runtime concern +// VariableConstants — user overrides on IrConstant +// +// Migration: KitX.WorkflowMigrator converts v1 → v2 (BS → parse → IR → serialize). +// ───────────────────────────────────────────────────────────────────────────── + +/// +/// KCS (KitX Code Script) 文件格式定义 — v2 (IR as storage). +/// +public class KcsFileFormat +{ + /// + /// 工作流唯一标识符 + /// + public string Id { get; set; } = string.Empty; + + /// + /// 工作流名称 + /// + public string Name { get; set; } = "Untitled Workflow"; + + /// + /// 工作流描述 + /// + public string Description { get; set; } = string.Empty; + + /// + /// 作者名称 + /// + public string Author { get; set; } = string.Empty; + + /// + /// 创建时间 + /// + public DateTime CreatedTime { get; set; } = DateTime.UtcNow; + + /// + /// 最后修改时间 + /// + public DateTime LastModifiedTime { get; set; } = DateTime.UtcNow; + + /// + /// 触发器配置(包含触发类型、插件名、触发器名等结构化字段) + /// + public TriggerConfig? TriggerConfig { get; set; } + + /// + /// 可变常量及其用户修改后的值。IR 的 Constants 存默认值,这里只存用户的覆盖值。 + /// 加载时合并:IR 提供默认值,信封覆盖值优先。 + /// + public Dictionary VariableConstants { get; set; } = []; + + /// + /// v2: 工作流的 IR 序列化形式(IrSerializer.Serialize(ir) 产出的 JSON 字符串)。 + /// 这是工作流的唯一真相源——BS 文本与 BP 图都是它的投影,按需生成,不再持久化。 + /// + public string IrData { get; set; } = "{}"; + + /// + /// IR 格式版本:"v6"(KitX.WorkflowV6.Serialization.WorkflowSerializer)或 + /// "v5"(KitX.Workflow.Serialization.IrSerializer,已归档)。 + /// 默认 "v6"——v5 已归档,只产 v6。 + /// 旧 .kcs 文件无此字段,反序列化时取默认值;老文件显式写了 "v5" 的仍按 v5 处理。 + /// V6 工具(KcsBuilder)写入 "v6"。Dashboard 打开时据此选择编辑器。 + /// + public string IrVersion { get; set; } = "v6"; + + /// + /// BP 画布布局(v6):节点规范 ID → 画布坐标。规范 ID 是节点在 + /// KitX.WorkflowV6.Ir.NodeId.Of(path)(FNV-1a of BpRenderer path)—— + /// 与 BpGraphLens.Project 重投影后的节点 ID 一致,故加载端可直接查表覆写坐标。 + /// 随机画布 ID 永不进入本字典(保存端经 Reverse 的 nodeId→canonical 映射归一化)。 + /// 空/缺省 = 使用布局网格位(LayoutService)。旧 .kcs 文件无此字段(反序列化为 null)。 + /// + public Dictionary? BlueprintLayout { get; set; } +} + +/// +/// BP 画布布局条目:单个节点的画布坐标( 的值)。 +/// +public class BlueprintLayoutEntry +{ + /// + /// 画布 X 坐标 + /// + public double X { get; set; } + + /// + /// 画布 Y 坐标 + /// + public double Y { get; set; } +} + +/// +/// 辅助函数参数定义 +/// +public class HelperFunctionParameter +{ + /// + /// 参数名称 + /// + public string Name { get; set; } = string.Empty; + + /// + /// 参数类型 + /// + public string Type { get; set; } = "object"; +} + +/// +/// 辅助函数定义 +/// +public class HelperFunction +{ + /// + /// 函数名称 + /// + public string Name { get; set; } = string.Empty; + + /// + /// 函数参数列表 + /// + public List Parameters { get; set; } = []; + + /// + /// 返回值类型 + /// + public string ReturnType { get; set; } = "object"; + + /// + /// 函数体代码(不包括函数签名) + /// + public string Code { get; set; } = string.Empty; +} + +/// +/// 可变常量(用于UI显示) +/// +public class VariableConstant +{ + /// + /// 常量名称 + /// + public string Name { get; set; } = string.Empty; + + /// + /// 默认值 + /// + public object? DefaultValue { get; set; } + + /// + /// 用户修改后的值 + /// + public object? UserValue { get; set; } + + /// + /// 数据类型 + /// + public string Type { get; set; } = "string"; +} diff --git a/KitX Core Contracts/KitX.Core.Contract/Workflow/Nodes/BlueprintNode.cs b/KitX Core Contracts/KitX.Core.Contract/Workflow/Nodes/BlueprintNode.cs new file mode 100644 index 0000000..2694dcc --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Workflow/Nodes/BlueprintNode.cs @@ -0,0 +1,113 @@ +using System; +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace KitX.Core.Contract.Workflow; + +// Forward declaration - Node types are in their own files +/// +/// Base class for all blueprint nodes. +/// Uses polymorphic JSON serialization so that concrete node types +/// round-trip correctly through System.Text.Json. +/// +[JsonPolymorphic(UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(EntryNode), "Entry")] +[JsonDerivedType(typeof(ConstNode), "Const")] +[JsonDerivedType(typeof(VariableNode), "Variable")] +[JsonDerivedType(typeof(BuiltinFunctionNode), "BuiltinFunction")] +[JsonDerivedType(typeof(PluginTriggerNode), "PluginTrigger")] +public abstract partial class BlueprintNode +{ + /// + /// Unique identifier + /// + public string Id { get; set; } = Guid.NewGuid().ToString(); + + /// + /// Node type + /// + public BlueprintNodeType NodeType { get; set; } + + /// + /// Display name + /// + public string Name { get; set; } = string.Empty; + + /// + /// X position on canvas + /// + public double X { get; set; } + + /// + /// Y position on canvas + /// + public double Y { get; set; } + + /// + /// Node width + /// + public double Width { get; set; } = 200; + + /// + /// Node height + /// + public double Height { get; set; } = 100; + + /// + /// Whether node is selected + /// + public bool IsSelected { get; set; } + + /// + /// User-facing comment attached to this node (v5.0 bidirectional comment retention). + /// Sourced from BS `//` comments via the anchoring rules in BlockScriptGrammarRule §9. + /// Nullable: null = no comment. Serialized for all subclasses via the base-class property. + /// + public string? Comment { get; set; } + + /// + /// Input pins + /// + public List InputPins { get; set; } = []; + + /// + /// Output pins + /// + public List OutputPins { get; set; } = []; + + /// + /// Parent blueprint reference (set when node is added to blueprint). + /// Ignored during JSON serialization to prevent circular reference. + /// + [JsonIgnore] + public Blueprint? Blueprint { get; set; } + + /// + /// Get pin by ID + /// + public BlueprintPin? GetPinById(string pinId) + { + foreach (var pin in InputPins) + if (pin.Id == pinId) return pin; + foreach (var pin in OutputPins) + if (pin.Id == pinId) return pin; + return null; + } + + /// + /// Get all pins + /// + public IEnumerable GetAllPins() + { + foreach (var pin in InputPins) + yield return pin; + foreach (var pin in OutputPins) + yield return pin; + } + + /// + /// Returns the display title for UI rendering (e.g., "Call: Plugin.Func"). + /// Default implementation returns Name; subclasses override for richer display. + /// + public virtual string GetDisplayTitle() => Name; +} \ No newline at end of file diff --git a/KitX Core Contracts/KitX.Core.Contract/Workflow/Nodes/BlueprintNodeType.cs b/KitX Core Contracts/KitX.Core.Contract/Workflow/Nodes/BlueprintNodeType.cs new file mode 100644 index 0000000..f80ec4d --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Workflow/Nodes/BlueprintNodeType.cs @@ -0,0 +1,63 @@ +namespace KitX.Core.Contract.Workflow; + +/// +/// Blueprint node types +/// +public enum BlueprintNodeType +{ + /// + /// Entry point node - triggered by Run button + /// + Entry, + + /// + /// Plugin event trigger node - alternative entry point activated by plugin triggers. + /// Has same pin structure as Entry (0 input, 1 Exec output) but carries PluginName/TriggerName metadata. + /// + PluginTrigger, + + /// + /// Constant value node + /// + Const, + + /// + /// Plugin function call node + /// + Call, + + /// + /// Helper function call node + /// + CallHelper, + + /// + /// Variable declaration node (ConstBlock variables without initial values). + /// A floating node with no ports — users can only change the data type. + /// + Variable, + + /// + /// 通用内置函数节点。通过 BuiltinFunctionNode.FunctionName 区分具体函数。 + /// 所有内置函数统一使用此类型,无需为每个函数创建专用 enum 值。 + /// + BuiltinFunction, + + /// + /// Block function node (v5.0) — a #Block promoted to a first-class collapsible node. + /// Its body lives in a sub-graph (ChildNodeIds), bounded by EntryPoint/ExitPoint nodes. + /// + Block, + + /// + /// Marks a data-input boundary inside a BlockNode's sub-graph. + /// Each EntryPoint corresponds to one input port on the collapsed BlockNode. + /// + EntryPoint, + + /// + /// Marks a data-output boundary inside a BlockNode's sub-graph. + /// Each ExitPoint corresponds to one output port on the collapsed BlockNode. + /// + ExitPoint +} diff --git a/KitX Core Contracts/KitX.Core.Contract/Workflow/Nodes/BlueprintPin.cs b/KitX Core Contracts/KitX.Core.Contract/Workflow/Nodes/BlueprintPin.cs new file mode 100644 index 0000000..f1ba9ba --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Workflow/Nodes/BlueprintPin.cs @@ -0,0 +1,34 @@ +using System; + +namespace KitX.Core.Contract.Workflow; + +/// +/// Pin on a blueprint node +/// +public class BlueprintPin +{ + /// + /// Unique identifier + /// + public string Id { get; set; } = Guid.NewGuid().ToString(); + + /// + /// Pin name (e.g., "Exec", "Condition", "True", "Value") + /// + public string Name { get; set; } = string.Empty; + + /// + /// Pin direction + /// + public PinDirection Direction { get; set; } + + /// + /// Pin data type + /// + public PinType Type { get; set; } = PinType.Any; + + /// + /// Default value for input pins + /// + public string? DefaultValue { get; set; } +} \ No newline at end of file diff --git a/KitX Core Contracts/KitX.Core.Contract/Workflow/Nodes/BuiltinFunctionNode.cs b/KitX Core Contracts/KitX.Core.Contract/Workflow/Nodes/BuiltinFunctionNode.cs new file mode 100644 index 0000000..9c53c92 --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Workflow/Nodes/BuiltinFunctionNode.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; + +namespace KitX.Core.Contract.Workflow; + +/// +/// 通用内置函数节点。通过 区分具体函数。 +/// 引脚布局由 驱动, +/// 消除了为每个内置函数创建专用节点子类的需要。 +/// +public class BuiltinFunctionNode : BlueprintNode +{ + /// + /// BlockScript 函数名(如 "Flip"),作为具体函数的唯一标识 + /// + public string FunctionName { get; set; } = string.Empty; + + /// + /// 额外属性字典,用于特殊节点(如 Set 的 VarName、Get 的 VarName) + /// + public Dictionary Properties { get; set; } = []; + + public BuiltinFunctionNode() + { + NodeType = BlueprintNodeType.BuiltinFunction; + Name = "BuiltinFunction"; + } + + public override string GetDisplayTitle() => FunctionName; +} \ No newline at end of file diff --git a/KitX Core Contracts/KitX.Core.Contract/Workflow/Nodes/ConstNode.cs b/KitX Core Contracts/KitX.Core.Contract/Workflow/Nodes/ConstNode.cs new file mode 100644 index 0000000..175f970 --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Workflow/Nodes/ConstNode.cs @@ -0,0 +1,47 @@ +namespace KitX.Core.Contract.Workflow; + +/// +/// Const node - constant value +/// +public class ConstNode : BlueprintNode +{ + /// + /// Constant name + /// + public string ConstName { get; set; } = string.Empty; + + /// + /// Constant type + /// + public string ConstType { get; set; } = "string"; + + /// + /// User value — the override entered on the BP node (initialised empty; maps to the + /// KS editor's Variable Constants panel UserValue). Empty means "use the default". + /// + public string? ConstValue { get; set; } + + /// + /// Default value — the initial value from the KS script declaration + /// (const { int x = 5 } → "5"). Read-only on the BP side; the node displays + /// when set, otherwise falls back to this default. + /// + public string? DefaultValue { get; set; } + + /// + /// True when this node is a definition (a const { ... } block declaration) + /// rather than a usage node (a pipeline-source literal). Set by the renderer for + /// /def/const/{name} nodes; the frontend reads this instead of inferring + /// definition-ness from pin presence or connectivity (those change over time; + /// definition-ness is fixed at creation). + /// + public bool IsDefinition { get; set; } + + public ConstNode() + { + NodeType = BlueprintNodeType.Const; + Name = "Const"; + } + + public override string GetDisplayTitle() => $"Const: {ConstName}"; +} \ No newline at end of file diff --git a/KitX Core Contracts/KitX.Core.Contract/Workflow/Nodes/EntryNode.cs b/KitX Core Contracts/KitX.Core.Contract/Workflow/Nodes/EntryNode.cs new file mode 100644 index 0000000..7e2c1d5 --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Workflow/Nodes/EntryNode.cs @@ -0,0 +1,13 @@ +namespace KitX.Core.Contract.Workflow; + +/// +/// Entry node - execution entry point +/// +public class EntryNode : BlueprintNode +{ + public EntryNode() + { + NodeType = BlueprintNodeType.Entry; + Name = "Entry"; + } +} \ No newline at end of file diff --git a/KitX Core Contracts/KitX.Core.Contract/Workflow/Nodes/PinDirection.cs b/KitX Core Contracts/KitX.Core.Contract/Workflow/Nodes/PinDirection.cs new file mode 100644 index 0000000..15eb9a2 --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Workflow/Nodes/PinDirection.cs @@ -0,0 +1,10 @@ +namespace KitX.Core.Contract.Workflow; + +/// +/// Pin direction +/// +public enum PinDirection +{ + Input, + Output +} \ No newline at end of file diff --git a/KitX Core Contracts/KitX.Core.Contract/Workflow/Nodes/PinType.cs b/KitX Core Contracts/KitX.Core.Contract/Workflow/Nodes/PinType.cs new file mode 100644 index 0000000..b061389 --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Workflow/Nodes/PinType.cs @@ -0,0 +1,54 @@ +namespace KitX.Core.Contract.Workflow; + +/// +/// Pin type for data flow +/// +public enum PinType +{ + /// + /// Execution flow pin - green + /// + Execution, + + /// + /// Boolean pin - cyan + /// + Boolean, + + /// + /// Integer pin - orange + /// + Integer, + + /// + /// Double pin - purple + /// + Double, + + /// + /// String pin - yellow + /// + String, + + /// + /// Any type pin - white + /// + Any, + + /// + /// Structured-data pin (System.Text.Json.JsonElement: Array/Object/scalar) - blue. + /// The first-class type for collection/object values flowing from plugin returns and + /// JSON functions (Package/List-Port-And-Json-Functions-Design.md §2.1). Distinct from Any + /// (which is an untyped catch-all); Json declares "this is structured data". + /// + Json, + + /// + /// Dictionary pin (System.Collections.Generic.Dictionary<string, object?>) - grey. + /// The first-class mutable key-value container type for Dict values constructed in KS + /// (Package/Dict-Type-Design.md). Distinct from Json (which is a read-only JsonElement + /// view of plugin-returned data); Dict declares "this is a mutable flat map". Bridged to + /// Json via DictToJson/JsonToDict. + /// + Dict +} \ No newline at end of file diff --git a/KitX Core Contracts/KitX.Core.Contract/Workflow/Nodes/PluginTriggerNode.cs b/KitX Core Contracts/KitX.Core.Contract/Workflow/Nodes/PluginTriggerNode.cs new file mode 100644 index 0000000..3bb0590 --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Workflow/Nodes/PluginTriggerNode.cs @@ -0,0 +1,25 @@ +namespace KitX.Core.Contract.Workflow; + +/// +/// Plugin trigger entry node - activated when a specific plugin fires a specific trigger signal. +/// Has same pin structure as Entry (0 input, 1 Exec output) but carries PluginName/TriggerName metadata. +/// +public class PluginTriggerNode : BlueprintNode +{ + /// The plugin name to listen for + public string PluginName { get; set; } = string.Empty; + + /// The trigger name to listen for + public string TriggerName { get; set; } = string.Empty; + + public PluginTriggerNode() + { + NodeType = BlueprintNodeType.PluginTrigger; + Name = "PluginTrigger"; + } + + public override string GetDisplayTitle() + => string.IsNullOrEmpty(PluginName) + ? $"Trigger: {TriggerName}" + : $"Trigger: {PluginName}.{TriggerName}"; +} \ No newline at end of file diff --git a/KitX Core Contracts/KitX.Core.Contract/Workflow/Nodes/VariableKind.cs b/KitX Core Contracts/KitX.Core.Contract/Workflow/Nodes/VariableKind.cs new file mode 100644 index 0000000..4846289 --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Workflow/Nodes/VariableKind.cs @@ -0,0 +1,29 @@ +namespace KitX.Core.Contract.Workflow; + +/// +/// The storage tier a belongs to (v5.0 three-tier variable model). +/// See BlockScriptGrammarRule §3.4. +/// +public enum VariableKind +{ + /// + /// ConstBlock variable — read-only, must have initial value (§3.1). + /// + Const, + + /// + /// PubVarBlock variable — global mutable, cross-block read/write (§3.2). + /// + PubVar, + + /// + /// ##BlockVars variable — block-local mutable, lifetime = one block activation (§3.3). + /// + BlockVar, + + /// + /// ForLoop-injected index — read-only, loop-injected scope variable (§7.1, §3.4). + /// Not declared in any block; auto-injected by the ForLoop node. + /// + LoopIndex +} diff --git a/KitX Core Contracts/KitX.Core.Contract/Workflow/Nodes/VariableNode.cs b/KitX Core Contracts/KitX.Core.Contract/Workflow/Nodes/VariableNode.cs new file mode 100644 index 0000000..36b6be0 --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Workflow/Nodes/VariableNode.cs @@ -0,0 +1,65 @@ +namespace KitX.Core.Contract.Workflow; + +/// +/// Variable node (v5.0) — unified read/write node for all variable tiers. +/// +/// Replaces the v4.0 Get/Set builtin function nodes: a read is a data edge leaving the +/// Value output pin; a write is a data edge entering the Value input pin. +/// The tap semantics (0 > x > Print) is expressed by both edges existing on +/// the same node. See BlockScriptGrammarRule §4.4, §6.3. +/// +/// +/// distinguishes the storage tier (Const / PubVar / BlockVar / LoopIndex), +/// which governs mutability, scope and reset behaviour (§3.4). +/// +/// +public class VariableNode : BlueprintNode +{ + /// + /// Variable name. + /// + public string VarName { get; set; } = string.Empty; + + /// + /// Variable type (e.g., "int", "double", "string", "bool"). + /// + public string VarType { get; set; } = "int"; + + /// + /// Storage tier this variable belongs to (v5.0). Governs mutability/scope/reset. + /// + public VariableKind VarKind { get; set; } = VariableKind.PubVar; + + /// + /// Optional initial-value payload. For dict-typed vars this carries the JSON-serialised + /// KsDictLiteral so the BP→IR reverse path can rebuild the structured initialiser + /// (Package/Dict-Type-Design.md §3.3). For scalar-typed vars it may carry the verbatim + /// initialiser expression text. Null when the var has no initial value. + /// + public string? VarInitialValue { get; set; } + + /// + /// Default value — the initial value from the KS script declaration + /// (var { int counter = 0 } → "0"). Read-only on the BP side; the node displays + /// (user value) when set, otherwise falls back to this + /// default. Mirrors the KS editor's Variable Constants panel DefaultValue. + /// + public string? DefaultValue { get; set; } + + /// + /// True when this node is a definition (a var { ... } block declaration) + /// rather than a usage node (a pipeline read/write reference). Set by the renderer + /// for /def/var/{name} nodes; the frontend reads this instead of inferring + /// definition-ness from pin presence or connectivity (those change over time; + /// definition-ness is fixed at creation). + /// + public bool IsDefinition { get; set; } + + public VariableNode() + { + NodeType = BlueprintNodeType.Variable; + Name = "Variable"; + } + + public override string GetDisplayTitle() => VarName; +} diff --git a/KitX Core Contracts/KitX.Core.Contract/Workflow/Nodes/VariadicPinSpec.cs b/KitX Core Contracts/KitX.Core.Contract/Workflow/Nodes/VariadicPinSpec.cs new file mode 100644 index 0000000..9681cc1 --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Workflow/Nodes/VariadicPinSpec.cs @@ -0,0 +1,42 @@ +namespace KitX.Core.Contract.Workflow; + +/// +/// Declares a variadic (auto-growing) pin group on a node descriptor. +/// +/// When a node declares InputVariadic or OutputVariadic, the Blueprint editor +/// appends a fresh pin of whenever the last pin of that group +/// gets connected — so the user can chain more inputs/outputs without manually adding pins. +/// +/// +/// is the title prefix for new pins; is the +/// starting number appended to it. The editor derives each new pin's name from the node's +/// current pin count (not by mutating this record), so every node instance counts independently +/// and survives save/load round-trips. +/// +/// Examples: +/// +/// StringConcat input: new("Input ", 3, PinType.String) → "Input 3", "Input 4", ... +/// Switch output: new("", 1, PinType.Execution) → "1", "2", ... +/// +/// +public record VariadicPinSpec(string BasePinName, int StartIndex, PinType PinType) +{ + /// + /// Optional pin-name prefixes for paired/multi-type variadic pin groups. + /// + /// When set together with , the variadic group grows by appending + /// one pin per prefix (cycling in order) per growth iteration, instead of a single pin. + /// This supports nodes needing alternating pin types, e.g. DictNew's + /// Key(String)/Value(Any) pairs: PinNamePrefixes=["Key","Value"], PinTypes=[String,Any] + /// grows "Key0","Value0","Key1","Value1",... (index from appended + /// to every prefix). Null = legacy single- behaviour (backward compatible). + /// + /// + public string[]? PinNamePrefixes { get; init; } + + /// + /// Optional pin types paired 1:1 with (same length). + /// Null = legacy single- behaviour. + /// + public PinType[]? PinTypes { get; init; } +} diff --git a/KitX Core Contracts/KitX.Core.Contract/Workflow/Results/BlockScriptExecutionResult.cs b/KitX Core Contracts/KitX.Core.Contract/Workflow/Results/BlockScriptExecutionResult.cs new file mode 100644 index 0000000..cfe0251 --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Workflow/Results/BlockScriptExecutionResult.cs @@ -0,0 +1,45 @@ +using System.Collections.Generic; + +namespace KitX.Core.Contract.Workflow; + +/// +/// Execution result for block scripts +/// +public class BlockScriptExecutionResult +{ + /// + /// Whether execution was successful + /// + public bool IsSuccess { get; set; } + + /// + /// Return value from script (if any) + /// + public object? ReturnValue { get; set; } + + /// + /// Error message if execution failed + /// + public string? ErrorMessage { get; set; } + + /// + /// Output from Print() calls + /// + public List Output { get; set; } = []; + + /// + /// Number of blocks executed + /// + public int ExecutedBlockCount { get; set; } + + /// + /// Execution time in milliseconds + /// + public long ExecutionTimeMs { get; set; } + + /// + /// Debug mapping: CFG statementId → Blueprint nodeId, for highlighting during debug. + /// Populated when executing with a debugger attached. + /// + public Dictionary? DebugNodeMapping { get; set; } +} \ No newline at end of file diff --git a/KitX Core Contracts/KitX.Core.Contract/Workflow/TriggerConfig.cs b/KitX Core Contracts/KitX.Core.Contract/Workflow/TriggerConfig.cs new file mode 100644 index 0000000..9401097 --- /dev/null +++ b/KitX Core Contracts/KitX.Core.Contract/Workflow/TriggerConfig.cs @@ -0,0 +1,37 @@ +namespace KitX.Core.Contract.Workflow; + +/// +/// 工作流触发器配置 +/// +public class TriggerConfig +{ + /// + /// 触发器类型: + /// - "Manual": 手动触发(默认) + /// - "PluginEvent": 插件事件触发 + /// Cron/FileWatcher/Webhook 等场景由专门插件通过 PluginEvent 实现 + /// + public string TriggerType { get; set; } = "Manual"; + + /// + /// PluginEvent 类型:监听的插件名称 + /// + public string? PluginName { get; set; } + + /// + /// PluginEvent 类型:监听的触发器名称(null = 该插件的所有触发器) + /// + public string? TriggerName { get; set; } + + /// + /// EntryNode / PluginTriggerNode 在蓝图画布上的 X 坐标。 + /// EntryNode 是纯合成节点(不对应任何 IrBlock),其坐标不进入 IR 注解体系, + /// 而是随触发配置一同持久化(入口标记与触发方式天然关联)。 + /// + public double EntryNodeX { get; set; } + + /// + /// EntryNode / PluginTriggerNode 在蓝图画布上的 Y 坐标。 + /// + public double EntryNodeY { get; set; } +} diff --git a/KitX File Formats/KitX.FileFormats.CSharp/ExtensionsPackage/Decoder.cs b/KitX File Formats/KitX.FileFormats.CSharp/ExtensionsPackage/Decoder.cs index 53ad236..e90249c 100644 --- a/KitX File Formats/KitX.FileFormats.CSharp/ExtensionsPackage/Decoder.cs +++ b/KitX File Formats/KitX.FileFormats.CSharp/ExtensionsPackage/Decoder.cs @@ -163,7 +163,15 @@ public Tuple Decode(string releaseFolder) #endregion - #region 获取源文件文件名与文件体并立即写回释放文件夹 + #region 获取源文件文件名与文件体, 校验路径后写回释放文件夹 + + // 释放文件夹的完整路径前缀, 用于校验解包路径不逃逸到外部 + var releaseRoot = Path.GetFullPath(releaseFolder) + .TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar) + + Path.DirectorySeparatorChar; + + // 先读取全部文件并逐个校验路径, 存在非法路径时整体拒绝, 不做半解包 + var extracted = new List>(); foreach (var item in FileMap) { @@ -177,14 +185,26 @@ public Tuple Decode(string releaseFolder) fb[i] = src[cursor]; var fileName = Encoding.UTF8.GetString(fn); - var dirPath = Path.GetDirectoryName( - Path.GetFullPath($"{releaseFolder}/{fileName}") - ); + + // 校验文件路径必须位于释放文件夹内, 防御 ".." 路径穿越与绝对路径 + var fullPath = Path.GetFullPath(Path.Combine(releaseFolder, fileName)); + + if (!fullPath.StartsWith(releaseRoot, StringComparison.Ordinal)) + throw new InvalidDataException($"Invalid file path in KXP package: {fileName}"); + + extracted.Add(Tuple.Create(fileName, fb)); + } + + // 校验全部通过后再写回释放文件夹 + foreach (var (fileName, fileBody) in extracted) + { + var fullPath = Path.GetFullPath(Path.Combine(releaseFolder, fileName)); + var dirPath = Path.GetDirectoryName(fullPath); if (!Directory.Exists(dirPath)) Directory.CreateDirectory(dirPath); - File.WriteAllBytes($"{releaseFolder}/{fileName}", fb); + File.WriteAllBytes(fullPath, fileBody); } #endregion diff --git a/KitX Script/Kscript.Compiler/Kscript.Compiler.csproj b/KitX Script/Kscript.Compiler/Kscript.Compiler.csproj deleted file mode 100644 index 74abf5c..0000000 --- a/KitX Script/Kscript.Compiler/Kscript.Compiler.csproj +++ /dev/null @@ -1,10 +0,0 @@ - - - - Exe - net6.0 - enable - enable - - - diff --git a/KitX Script/Kscript.Compiler/Program.cs b/KitX Script/Kscript.Compiler/Program.cs deleted file mode 100644 index 3751555..0000000 --- a/KitX Script/Kscript.Compiler/Program.cs +++ /dev/null @@ -1,2 +0,0 @@ -// See https://aka.ms/new-console-template for more information -Console.WriteLine("Hello, World!"); diff --git a/KitX Script/Kscript.Editor/.gitignore b/KitX Script/Kscript.Editor/.gitignore deleted file mode 100644 index 8afdcb6..0000000 --- a/KitX Script/Kscript.Editor/.gitignore +++ /dev/null @@ -1,454 +0,0 @@ -## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. -## -## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore - -# User-specific files -*.rsuser -*.suo -*.user -*.userosscache -*.sln.docstates - -# User-specific files (MonoDevelop/Xamarin Studio) -*.userprefs - -# Mono auto generated files -mono_crash.* - -# Build results -[Dd]ebug/ -[Dd]ebugPublic/ -[Rr]elease/ -[Rr]eleases/ -x64/ -x86/ -[Ww][Ii][Nn]32/ -[Aa][Rr][Mm]/ -[Aa][Rr][Mm]64/ -bld/ -[Bb]in/ -[Oo]bj/ -[Ll]og/ -[Ll]ogs/ - -# Visual Studio 2015/2017 cache/options directory -.vs/ -# Uncomment if you have tasks that create the project's static files in wwwroot -#wwwroot/ - -# Visual Studio 2017 auto generated files -Generated\ Files/ - -# MSTest test Results -[Tt]est[Rr]esult*/ -[Bb]uild[Ll]og.* - -# NUnit -*.VisualState.xml -TestResult.xml -nunit-*.xml - -# Build Results of an ATL Project -[Dd]ebugPS/ -[Rr]eleasePS/ -dlldata.c - -# Benchmark Results -BenchmarkDotNet.Artifacts/ - -# .NET Core -project.lock.json -project.fragment.lock.json -artifacts/ - -# Tye -.tye/ - -# ASP.NET Scaffolding -ScaffoldingReadMe.txt - -# StyleCop -StyleCopReport.xml - -# Files built by Visual Studio -*_i.c -*_p.c -*_h.h -*.ilk -*.meta -*.obj -*.iobj -*.pch -*.pdb -*.ipdb -*.pgc -*.pgd -*.rsp -*.sbr -*.tlb -*.tli -*.tlh -*.tmp -*.tmp_proj -*_wpftmp.csproj -*.log -*.vspscc -*.vssscc -.builds -*.pidb -*.svclog -*.scc - -# Chutzpah Test files -_Chutzpah* - -# Visual C++ cache files -ipch/ -*.aps -*.ncb -*.opendb -*.opensdf -*.sdf -*.cachefile -*.VC.db -*.VC.VC.opendb - -# Visual Studio profiler -*.psess -*.vsp -*.vspx -*.sap - -# Visual Studio Trace Files -*.e2e - -# TFS 2012 Local Workspace -$tf/ - -# Guidance Automation Toolkit -*.gpState - -# ReSharper is a .NET coding add-in -_ReSharper*/ -*.[Rr]e[Ss]harper -*.DotSettings.user - -# TeamCity is a build add-in -_TeamCity* - -# DotCover is a Code Coverage Tool -*.dotCover - -# AxoCover is a Code Coverage Tool -.axoCover/* -!.axoCover/settings.json - -# Coverlet is a free, cross platform Code Coverage Tool -coverage*.json -coverage*.xml -coverage*.info - -# Visual Studio code coverage results -*.coverage -*.coveragexml - -# NCrunch -_NCrunch_* -.*crunch*.local.xml -nCrunchTemp_* - -# MightyMoose -*.mm.* -AutoTest.Net/ - -# Web workbench (sass) -.sass-cache/ - -# Installshield output folder -[Ee]xpress/ - -# DocProject is a documentation generator add-in -DocProject/buildhelp/ -DocProject/Help/*.HxT -DocProject/Help/*.HxC -DocProject/Help/*.hhc -DocProject/Help/*.hhk -DocProject/Help/*.hhp -DocProject/Help/Html2 -DocProject/Help/html - -# Click-Once directory -publish/ - -# Publish Web Output -*.[Pp]ublish.xml -*.azurePubxml -# Note: Comment the next line if you want to checkin your web deploy settings, -# but database connection strings (with potential passwords) will be unencrypted -*.pubxml -*.publishproj - -# Microsoft Azure Web App publish settings. Comment the next line if you want to -# checkin your Azure Web App publish settings, but sensitive information contained -# in these scripts will be unencrypted -PublishScripts/ - -# NuGet Packages -*.nupkg -# NuGet Symbol Packages -*.snupkg -# The packages folder can be ignored because of Package Restore -**/[Pp]ackages/* -# except build/, which is used as an MSBuild target. -!**/[Pp]ackages/build/ -# Uncomment if necessary however generally it will be regenerated when needed -#!**/[Pp]ackages/repositories.config -# NuGet v3's project.json files produces more ignorable files -*.nuget.props -*.nuget.targets - -# Microsoft Azure Build Output -csx/ -*.build.csdef - -# Microsoft Azure Emulator -ecf/ -rcf/ - -# Windows Store app package directories and files -AppPackages/ -BundleArtifacts/ -Package.StoreAssociation.xml -_pkginfo.txt -*.appx -*.appxbundle -*.appxupload - -# Visual Studio cache files -# files ending in .cache can be ignored -*.[Cc]ache -# but keep track of directories ending in .cache -!?*.[Cc]ache/ - -# Others -ClientBin/ -~$* -*~ -*.dbmdl -*.dbproj.schemaview -*.jfm -*.pfx -*.publishsettings -orleans.codegen.cs - -# Including strong name files can present a security risk -# (https://github.com/github/gitignore/pull/2483#issue-259490424) -#*.snk - -# Since there are multiple workflows, uncomment next line to ignore bower_components -# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) -#bower_components/ - -# RIA/Silverlight projects -Generated_Code/ - -# Backup & report files from converting an old project file -# to a newer Visual Studio version. Backup files are not needed, -# because we have git ;-) -_UpgradeReport_Files/ -Backup*/ -UpgradeLog*.XML -UpgradeLog*.htm -ServiceFabricBackup/ -*.rptproj.bak - -# SQL Server files -*.mdf -*.ldf -*.ndf - -# Business Intelligence projects -*.rdl.data -*.bim.layout -*.bim_*.settings -*.rptproj.rsuser -*- [Bb]ackup.rdl -*- [Bb]ackup ([0-9]).rdl -*- [Bb]ackup ([0-9][0-9]).rdl - -# Microsoft Fakes -FakesAssemblies/ - -# GhostDoc plugin setting file -*.GhostDoc.xml - -# Node.js Tools for Visual Studio -.ntvs_analysis.dat -node_modules/ - -# Visual Studio 6 build log -*.plg - -# Visual Studio 6 workspace options file -*.opt - -# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) -*.vbw - -# Visual Studio LightSwitch build output -**/*.HTMLClient/GeneratedArtifacts -**/*.DesktopClient/GeneratedArtifacts -**/*.DesktopClient/ModelManifest.xml -**/*.Server/GeneratedArtifacts -**/*.Server/ModelManifest.xml -_Pvt_Extensions - -# Paket dependency manager -.paket/paket.exe -paket-files/ - -# FAKE - F# Make -.fake/ - -# CodeRush personal settings -.cr/personal - -# Python Tools for Visual Studio (PTVS) -__pycache__/ -*.pyc - -# Cake - Uncomment if you are using it -# tools/** -# !tools/packages.config - -# Tabs Studio -*.tss - -# Telerik's JustMock configuration file -*.jmconfig - -# BizTalk build output -*.btp.cs -*.btm.cs -*.odx.cs -*.xsd.cs - -# OpenCover UI analysis results -OpenCover/ - -# Azure Stream Analytics local run output -ASALocalRun/ - -# MSBuild Binary and Structured Log -*.binlog - -# NVidia Nsight GPU debugger configuration file -*.nvuser - -# MFractors (Xamarin productivity tool) working folder -.mfractor/ - -# Local History for Visual Studio -.localhistory/ - -# BeatPulse healthcheck temp database -healthchecksdb - -# Backup folder for Package Reference Convert tool in Visual Studio 2017 -MigrationBackup/ - -# Ionide (cross platform F# VS Code tools) working folder -.ionide/ - -# Fody - auto-generated XML schema -FodyWeavers.xsd - -## -## Visual studio for Mac -## - - -# globs -Makefile.in -*.userprefs -*.usertasks -config.make -config.status -aclocal.m4 -install-sh -autom4te.cache/ -*.tar.gz -tarballs/ -test-results/ - -# Mac bundle stuff -*.dmg -*.app - -# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore -# General -.DS_Store -.AppleDouble -.LSOverride - -# Icon must end with two \r -Icon - - -# Thumbnails -._* - -# Files that might appear in the root of a volume -.DocumentRevisions-V100 -.fseventsd -.Spotlight-V100 -.TemporaryItems -.Trashes -.VolumeIcon.icns -.com.apple.timemachine.donotpresent - -# Directories potentially created on remote AFP share -.AppleDB -.AppleDesktop -Network Trash Folder -Temporary Items -.apdisk - -# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore -# Windows thumbnail cache files -Thumbs.db -ehthumbs.db -ehthumbs_vista.db - -# Dump file -*.stackdump - -# Folder config file -[Dd]esktop.ini - -# Recycle Bin used on file shares -$RECYCLE.BIN/ - -# Windows Installer files -*.cab -*.msi -*.msix -*.msm -*.msp - -# Windows shortcuts -*.lnk - -# JetBrains Rider -.idea/ -*.sln.iml - -## -## Visual Studio Code -## -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json diff --git a/KitX Script/Kscript.Editor/Directory.Build.props b/KitX Script/Kscript.Editor/Directory.Build.props deleted file mode 100644 index 6da774b..0000000 --- a/KitX Script/Kscript.Editor/Directory.Build.props +++ /dev/null @@ -1,6 +0,0 @@ - - - enable - 11.0.0-preview4 - - diff --git a/KitX Script/Kscript.Editor/Kscript.Editor.Android/Icon.png b/KitX Script/Kscript.Editor/Kscript.Editor.Android/Icon.png deleted file mode 100644 index 41a2a61..0000000 Binary files a/KitX Script/Kscript.Editor/Kscript.Editor.Android/Icon.png and /dev/null differ diff --git a/KitX Script/Kscript.Editor/Kscript.Editor.Android/Kscript.Editor.Android.csproj b/KitX Script/Kscript.Editor/Kscript.Editor.Android/Kscript.Editor.Android.csproj deleted file mode 100644 index 81b7873..0000000 --- a/KitX Script/Kscript.Editor/Kscript.Editor.Android/Kscript.Editor.Android.csproj +++ /dev/null @@ -1,27 +0,0 @@ - - - Exe - net7.0-android - 21 - enable - com.CompanyName.Kscript.Editor - 1 - 1.0 - apk - False - - - - - Resources\drawable\Icon.png - - - - - - - - - - - diff --git a/KitX Script/Kscript.Editor/Kscript.Editor.Android/MainActivity.cs b/KitX Script/Kscript.Editor/Kscript.Editor.Android/MainActivity.cs deleted file mode 100644 index f43f258..0000000 --- a/KitX Script/Kscript.Editor/Kscript.Editor.Android/MainActivity.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Android.App; -using Android.Content.PM; -using Avalonia.Android; - -namespace Kscript.Editor.Android -{ - [Activity(Label = "Kscript.Editor.Android", Theme = "@style/MyTheme.NoActionBar", Icon = "@drawable/icon", LaunchMode = LaunchMode.SingleTop, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize)] - public class MainActivity : AvaloniaMainActivity - { - } -} \ No newline at end of file diff --git a/KitX Script/Kscript.Editor/Kscript.Editor.Android/Properties/AndroidManifest.xml b/KitX Script/Kscript.Editor/Kscript.Editor.Android/Properties/AndroidManifest.xml deleted file mode 100644 index a55ac0e..0000000 --- a/KitX Script/Kscript.Editor/Kscript.Editor.Android/Properties/AndroidManifest.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/KitX Script/Kscript.Editor/Kscript.Editor.Android/Resources/drawable/splash_screen.xml b/KitX Script/Kscript.Editor/Kscript.Editor.Android/Resources/drawable/splash_screen.xml deleted file mode 100644 index 2e920b4..0000000 --- a/KitX Script/Kscript.Editor/Kscript.Editor.Android/Resources/drawable/splash_screen.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - diff --git a/KitX Script/Kscript.Editor/Kscript.Editor.Android/Resources/values/colors.xml b/KitX Script/Kscript.Editor/Kscript.Editor.Android/Resources/values/colors.xml deleted file mode 100644 index 59279d5..0000000 --- a/KitX Script/Kscript.Editor/Kscript.Editor.Android/Resources/values/colors.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - #FFFFFF - diff --git a/KitX Script/Kscript.Editor/Kscript.Editor.Android/Resources/values/styles.xml b/KitX Script/Kscript.Editor/Kscript.Editor.Android/Resources/values/styles.xml deleted file mode 100644 index 2759d29..0000000 --- a/KitX Script/Kscript.Editor/Kscript.Editor.Android/Resources/values/styles.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - diff --git a/KitX Script/Kscript.Editor/Kscript.Editor.Android/SplashActivity.cs b/KitX Script/Kscript.Editor/Kscript.Editor.Android/SplashActivity.cs deleted file mode 100644 index 5c0344d..0000000 --- a/KitX Script/Kscript.Editor/Kscript.Editor.Android/SplashActivity.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Android.App; -using Android.Content; -using Android.OS; -using Avalonia; -using Avalonia.Android; -using Avalonia.ReactiveUI; -using Application = Android.App.Application; - -namespace Kscript.Editor.Android -{ - [Activity(Theme = "@style/MyTheme.Splash", MainLauncher = true, NoHistory = true)] - public class SplashActivity : AvaloniaSplashActivity - { - protected override AppBuilder CustomizeAppBuilder(AppBuilder builder) - { - return base.CustomizeAppBuilder(builder) - .UseReactiveUI(); - } - - protected override void OnCreate(Bundle? savedInstanceState) - { - base.OnCreate(savedInstanceState); - } - - protected override void OnResume() - { - base.OnResume(); - - StartActivity(new Intent(Application.Context, typeof(MainActivity))); - } - } -} \ No newline at end of file diff --git a/KitX Script/Kscript.Editor/Kscript.Editor.Desktop/Kscript.Editor.Desktop.csproj b/KitX Script/Kscript.Editor/Kscript.Editor.Desktop/Kscript.Editor.Desktop.csproj deleted file mode 100644 index 6c193e5..0000000 --- a/KitX Script/Kscript.Editor/Kscript.Editor.Desktop/Kscript.Editor.Desktop.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - WinExe - net7.0 - enable - true - - - - app.manifest - true - - - - - - - - - - - - diff --git a/KitX Script/Kscript.Editor/Kscript.Editor.Desktop/PInvoke/WindowStyle.cs b/KitX Script/Kscript.Editor/Kscript.Editor.Desktop/PInvoke/WindowStyle.cs deleted file mode 100644 index f78aecf..0000000 --- a/KitX Script/Kscript.Editor/Kscript.Editor.Desktop/PInvoke/WindowStyle.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.InteropServices; -using System.Text; -using System.Threading.Tasks; - -namespace Kscript.Editor.Desktop.PInvoke; - -public class WindowStyle -{ - [DllImport("dwmapi.dll")] - public static extern int DwmSetWindowAttribute(IntPtr hwnd, DwmWindowAttribute dwAttribute, - ref int pvAttribute, int cbAttribute); - - [Flags] - public enum DwmWindowAttribute : uint - { - DWMWA_USE_HOSTBACKDROPBRUSH = 17, - DWMWA_USE_IMMERSIVE_DARK_MODE = 20, - DWMWA_SYSTEMBACKDROP_TYPE = 38, - DWMWA_MICA_EFFECT = 1029, - } - - [Flags] - public enum DwmSystemBackDropType : int - { - DWMSBT_AUTO, - DWMSBT_NONE, - DWMSBT_MAINWINDOW = 2, // Mica - DWMSBT_TRANSIENTWINDOW = 3, // Acrylic - DWMSBT_TABBEDWINDOW = 4, // Tabbed - - Mica = 2, - Acrylic = 3, - Tabbed = 4 - } - - public static void UpdateStyleAttributes(nint hwnd) - { - int trueValue = 0x01; - //int backDrop = (int)DwmSystemBackDropType.Tabbed; - - _ = DwmSetWindowAttribute(hwnd, DwmWindowAttribute.DWMWA_MICA_EFFECT, - ref trueValue, Marshal.SizeOf(typeof(int))); - _ = DwmSetWindowAttribute(hwnd, DwmWindowAttribute.DWMWA_USE_IMMERSIVE_DARK_MODE, - ref trueValue, Marshal.SizeOf(typeof(int))); - //_ = DwmSetWindowAttribute(hwnd, DwmWindowAttribute.DWMWA_SYSTEMBACKDROP_TYPE, - // ref backDrop, Marshal.SizeOf(typeof(int))); - } -} diff --git a/KitX Script/Kscript.Editor/Kscript.Editor.Desktop/Program.cs b/KitX Script/Kscript.Editor/Kscript.Editor.Desktop/Program.cs deleted file mode 100644 index 03decbe..0000000 --- a/KitX Script/Kscript.Editor/Kscript.Editor.Desktop/Program.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Avalonia; -using Avalonia.ReactiveUI; -using Kscript.Editor.Data; -using Kscript.Editor.Desktop.PInvoke; -using Kscript.Editor.Views; -using System; - -namespace Kscript.Editor.Desktop -{ - internal class Program - { - // Initialization code. Don't use any Avalonia, third-party APIs or any - // SynchronizationContext-reliant code before AppMain is called: things aren't initialized - // yet and stuff might break. - [STAThread] - public static void Main(string[] args) - { - if (OperatingSystem.IsWindows()) - { - nameof(GlobalInfo.WindowHandles).RegisterInfoReactor(() => - WindowStyle.UpdateStyleAttributes(nameof(MainWindow).RequestWindowHandle())); - } - - BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); - } - - // Avalonia configuration, don't remove; also used by visual designer. - public static AppBuilder BuildAvaloniaApp() - => AppBuilder.Configure() - .UsePlatformDetect() - .LogToTrace() - .UseReactiveUI(); - } -} diff --git a/KitX Script/Kscript.Editor/Kscript.Editor.Desktop/Properties/launchSettings.json b/KitX Script/Kscript.Editor/Kscript.Editor.Desktop/Properties/launchSettings.json deleted file mode 100644 index 33504c9..0000000 --- a/KitX Script/Kscript.Editor/Kscript.Editor.Desktop/Properties/launchSettings.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "profiles": { - "WSL": { - "commandName": "WSL2", - "distributionName": "" - } - } -} \ No newline at end of file diff --git a/KitX Script/Kscript.Editor/Kscript.Editor.Desktop/app.manifest b/KitX Script/Kscript.Editor/Kscript.Editor.Desktop/app.manifest deleted file mode 100644 index 34e50e5..0000000 --- a/KitX Script/Kscript.Editor/Kscript.Editor.Desktop/app.manifest +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - diff --git a/KitX Script/Kscript.Editor/Kscript.Editor.Web/AppBundle/Logo.svg b/KitX Script/Kscript.Editor/Kscript.Editor.Web/AppBundle/Logo.svg deleted file mode 100644 index 9685a23..0000000 --- a/KitX Script/Kscript.Editor/Kscript.Editor.Web/AppBundle/Logo.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/KitX Script/Kscript.Editor/Kscript.Editor.Web/AppBundle/app.css b/KitX Script/Kscript.Editor/Kscript.Editor.Web/AppBundle/app.css deleted file mode 100644 index 027ea8f..0000000 --- a/KitX Script/Kscript.Editor/Kscript.Editor.Web/AppBundle/app.css +++ /dev/null @@ -1,67 +0,0 @@ -/* HTML styles for the splash screen */ - -.highlight { - color: white; - font-size: 2.5rem; - display: block; -} - -.purple { - color: #8b44ac; -} - -.icon { - opacity: 0.05; - height: 35%; - width: 35%; - position: absolute; - background-repeat: no-repeat; - right: 0px; - bottom: 0px; - margin-right: 3%; - margin-bottom: 5%; - z-index: 5000; - background-position: right bottom; - pointer-events: none; -} - -#avalonia-splash a { - color: whitesmoke; - text-decoration: none; -} - -.center { - display: flex; - justify-content: center; - align-items: center; - height: 100vh; -} - -#avalonia-splash { - position: relative; - height: 100%; - width: 100%; - color: whitesmoke; - background: #1b2a4e; - font-family: 'Nunito', sans-serif; - background-position: center; - background-size: cover; - background-repeat: no-repeat; - justify-content: center; - align-items: center; -} - -.splash-close { - animation: fadeout 0.25s linear forwards; -} - -@keyframes fadeout { - 0% { - opacity: 100%; - } - - 100% { - opacity: 0; - visibility: collapse; - } -} diff --git a/KitX Script/Kscript.Editor/Kscript.Editor.Web/AppBundle/favicon.ico b/KitX Script/Kscript.Editor/Kscript.Editor.Web/AppBundle/favicon.ico deleted file mode 100644 index da8d49f..0000000 Binary files a/KitX Script/Kscript.Editor/Kscript.Editor.Web/AppBundle/favicon.ico and /dev/null differ diff --git a/KitX Script/Kscript.Editor/Kscript.Editor.Web/AppBundle/index.html b/KitX Script/Kscript.Editor/Kscript.Editor.Web/AppBundle/index.html deleted file mode 100644 index 4807fa0..0000000 --- a/KitX Script/Kscript.Editor/Kscript.Editor.Web/AppBundle/index.html +++ /dev/null @@ -1,31 +0,0 @@ - - - - - Kscript.Editor.Web - - - - - - - - - - - -
-
-
-

- Powered by - Avalonia UI -

-
- Avalonia Logo -
-
- - - - \ No newline at end of file diff --git a/KitX Script/Kscript.Editor/Kscript.Editor.Web/AppBundle/main.js b/KitX Script/Kscript.Editor/Kscript.Editor.Web/AppBundle/main.js deleted file mode 100644 index 2426ede..0000000 --- a/KitX Script/Kscript.Editor/Kscript.Editor.Web/AppBundle/main.js +++ /dev/null @@ -1,16 +0,0 @@ -import { dotnet } from './dotnet.js' -import { registerAvaloniaModule } from './avalonia.js'; - -const is_browser = typeof window != "undefined"; -if (!is_browser) throw new Error(`Expected to be running in a browser`); - -const dotnetRuntime = await dotnet - .withDiagnosticTracing(false) - .withApplicationArgumentsFromQuery() - .create(); - -await registerAvaloniaModule(dotnetRuntime); - -const config = dotnetRuntime.getConfig(); - -await dotnetRuntime.runMainAndExit(config.mainAssemblyName, [window.location.search]); \ No newline at end of file diff --git a/KitX Script/Kscript.Editor/Kscript.Editor.Web/Kscript.Editor.Web.csproj b/KitX Script/Kscript.Editor/Kscript.Editor.Web/Kscript.Editor.Web.csproj deleted file mode 100644 index 17856eb..0000000 --- a/KitX Script/Kscript.Editor/Kscript.Editor.Web/Kscript.Editor.Web.csproj +++ /dev/null @@ -1,20 +0,0 @@ - - - net7.0 - browser-wasm - AppBundle\main.js - Exe - - - - - - - - - - - - - - diff --git a/KitX Script/Kscript.Editor/Kscript.Editor.Web/Program.cs b/KitX Script/Kscript.Editor/Kscript.Editor.Web/Program.cs deleted file mode 100644 index 7f499f8..0000000 --- a/KitX Script/Kscript.Editor/Kscript.Editor.Web/Program.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Avalonia; -using Avalonia.ReactiveUI; -using Avalonia.Web; -using Kscript.Editor; -using System.Runtime.Versioning; - -[assembly: SupportedOSPlatform("browser")] - -internal partial class Program -{ - private static void Main(string[] args) => BuildAvaloniaApp() - .UseReactiveUI() - .SetupBrowserApp("out"); - - public static AppBuilder BuildAvaloniaApp() - => AppBuilder.Configure(); -} \ No newline at end of file diff --git a/KitX Script/Kscript.Editor/Kscript.Editor.Web/Properties/launchSettings.json b/KitX Script/Kscript.Editor/Kscript.Editor.Web/Properties/launchSettings.json deleted file mode 100644 index 3aa6fd1..0000000 --- a/KitX Script/Kscript.Editor/Kscript.Editor.Web/Properties/launchSettings.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "profiles": { - "Kscript.Editor.Web": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "https://localhost:5001;http://localhost:5000", - "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/debug?browser={browserInspectUri}" - } - } -} \ No newline at end of file diff --git a/KitX Script/Kscript.Editor/Kscript.Editor.Web/runtimeconfig.template.json b/KitX Script/Kscript.Editor/Kscript.Editor.Web/runtimeconfig.template.json deleted file mode 100644 index c6990ba..0000000 --- a/KitX Script/Kscript.Editor/Kscript.Editor.Web/runtimeconfig.template.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "wasmHostProperties": { - "perHostConfig": [ - { - "name": "browser", - "html-path": "index.html", - "Host": "browser" - } - ] - } -} \ No newline at end of file diff --git a/KitX Script/Kscript.Editor/Kscript.Editor.iOS/AppDelegate.cs b/KitX Script/Kscript.Editor/Kscript.Editor.iOS/AppDelegate.cs deleted file mode 100644 index 8bbc98c..0000000 --- a/KitX Script/Kscript.Editor/Kscript.Editor.iOS/AppDelegate.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Avalonia; -using Avalonia.Controls; -using Avalonia.iOS; -using Avalonia.Media; -using Avalonia.ReactiveUI; -using Foundation; -using UIKit; - -namespace Kscript.Editor.iOS -{ - // The UIApplicationDelegate for the application. This class is responsible for launching the - // User Interface of the application, as well as listening (and optionally responding) to - // application events from iOS. - [Register("AppDelegate")] - public partial class AppDelegate : AvaloniaAppDelegate - { - protected override AppBuilder CustomizeAppBuilder(AppBuilder builder) - { - return builder.UseReactiveUI(); - } - } -} \ No newline at end of file diff --git a/KitX Script/Kscript.Editor/Kscript.Editor.iOS/Entitlements.plist b/KitX Script/Kscript.Editor/Kscript.Editor.iOS/Entitlements.plist deleted file mode 100644 index 0c67376..0000000 --- a/KitX Script/Kscript.Editor/Kscript.Editor.iOS/Entitlements.plist +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/KitX Script/Kscript.Editor/Kscript.Editor.iOS/Info.plist b/KitX Script/Kscript.Editor/Kscript.Editor.iOS/Info.plist deleted file mode 100644 index ac5f603..0000000 --- a/KitX Script/Kscript.Editor/Kscript.Editor.iOS/Info.plist +++ /dev/null @@ -1,47 +0,0 @@ - - - - - CFBundleDisplayName - Kscript.Editor - CFBundleIdentifier - companyName.Kscript.Editor - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1.0 - LSRequiresIPhoneOS - - MinimumOSVersion - 10.0 - UIDeviceFamily - - 1 - 2 - - UILaunchStoryboardName - LaunchScreen - UIRequiredDeviceCapabilities - - armv7 - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UIStatusBarHidden - - UIViewControllerBasedStatusBarAppearance - - - diff --git a/KitX Script/Kscript.Editor/Kscript.Editor.iOS/Kscript.Editor.iOS.csproj b/KitX Script/Kscript.Editor/Kscript.Editor.iOS/Kscript.Editor.iOS.csproj deleted file mode 100644 index 04201bc..0000000 --- a/KitX Script/Kscript.Editor/Kscript.Editor.iOS/Kscript.Editor.iOS.csproj +++ /dev/null @@ -1,22 +0,0 @@ - - - Exe - net7.0-ios - 10.0 - manual - enable - iossimulator-x64 - - - - - - - - - - - - - - diff --git a/KitX Script/Kscript.Editor/Kscript.Editor.iOS/Main.cs b/KitX Script/Kscript.Editor/Kscript.Editor.iOS/Main.cs deleted file mode 100644 index 4ca7864..0000000 --- a/KitX Script/Kscript.Editor/Kscript.Editor.iOS/Main.cs +++ /dev/null @@ -1,15 +0,0 @@ -using UIKit; - -namespace Kscript.Editor.iOS -{ - public class Application - { - // This is the main entry point of the application. - static void Main(string[] args) - { - // if you want to use a different Application Delegate class from "AppDelegate" - // you can specify it here. - UIApplication.Main(args, null, typeof(AppDelegate)); - } - } -} \ No newline at end of file diff --git a/KitX Script/Kscript.Editor/Kscript.Editor.iOS/Resources/LaunchScreen.xib b/KitX Script/Kscript.Editor/Kscript.Editor.iOS/Resources/LaunchScreen.xib deleted file mode 100644 index 55b5c6b..0000000 --- a/KitX Script/Kscript.Editor/Kscript.Editor.iOS/Resources/LaunchScreen.xib +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/KitX Script/Kscript.Editor/Kscript.Editor.sln b/KitX Script/Kscript.Editor/Kscript.Editor.sln deleted file mode 100644 index 411c557..0000000 --- a/KitX Script/Kscript.Editor/Kscript.Editor.sln +++ /dev/null @@ -1,55 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.3.32811.315 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kscript.Editor", "Kscript.Editor\Kscript.Editor.csproj", "{EBFA8512-1EA5-4D8C-B4AC-AB5B48A6D568}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kscript.Editor.Desktop", "Kscript.Editor.Desktop\Kscript.Editor.Desktop.csproj", "{ABC31E74-02FF-46EB-B3B2-4E6AE43B456C}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kscript.Editor.Web", "Kscript.Editor.Web\Kscript.Editor.Web.csproj", "{1C1A049E-235C-4CD0-B6FA-D53AC418F4DA}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kscript.Editor.iOS", "Kscript.Editor.iOS\Kscript.Editor.iOS.csproj", "{EBD9022F-BC83-4846-9A11-6F7F3772DC64}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kscript.Editor.Android", "Kscript.Editor.Android\Kscript.Editor.Android.csproj", "{7AD1DAC8-7FBE-49D5-8614-7321233DB82E}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{3DA99C4E-89E3-4049-9C22-0A7EC60D83D8}" - ProjectSection(SolutionItems) = preProject - Directory.Build.props = Directory.Build.props - global.json = global.json - EndProjectSection -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {EBFA8512-1EA5-4D8C-B4AC-AB5B48A6D568}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EBFA8512-1EA5-4D8C-B4AC-AB5B48A6D568}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EBFA8512-1EA5-4D8C-B4AC-AB5B48A6D568}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EBFA8512-1EA5-4D8C-B4AC-AB5B48A6D568}.Release|Any CPU.Build.0 = Release|Any CPU - {ABC31E74-02FF-46EB-B3B2-4E6AE43B456C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {ABC31E74-02FF-46EB-B3B2-4E6AE43B456C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {ABC31E74-02FF-46EB-B3B2-4E6AE43B456C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {ABC31E74-02FF-46EB-B3B2-4E6AE43B456C}.Release|Any CPU.Build.0 = Release|Any CPU - {1C1A049E-235C-4CD0-B6FA-D53AC418F4DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1C1A049E-235C-4CD0-B6FA-D53AC418F4DA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1C1A049E-235C-4CD0-B6FA-D53AC418F4DA}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1C1A049E-235C-4CD0-B6FA-D53AC418F4DA}.Release|Any CPU.Build.0 = Release|Any CPU - {EBD9022F-BC83-4846-9A11-6F7F3772DC64}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EBD9022F-BC83-4846-9A11-6F7F3772DC64}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EBD9022F-BC83-4846-9A11-6F7F3772DC64}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EBD9022F-BC83-4846-9A11-6F7F3772DC64}.Release|Any CPU.Build.0 = Release|Any CPU - {7AD1DAC8-7FBE-49D5-8614-7321233DB82E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7AD1DAC8-7FBE-49D5-8614-7321233DB82E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7AD1DAC8-7FBE-49D5-8614-7321233DB82E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7AD1DAC8-7FBE-49D5-8614-7321233DB82E}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {83CB65B8-011F-4ED7-BCD3-A6CFA935EF7E} - EndGlobalSection -EndGlobal diff --git a/KitX Script/Kscript.Editor/Kscript.Editor/App.axaml b/KitX Script/Kscript.Editor/Kscript.Editor/App.axaml deleted file mode 100644 index c706c1c..0000000 --- a/KitX Script/Kscript.Editor/Kscript.Editor/App.axaml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - diff --git a/KitX Script/Kscript.Editor/Kscript.Editor/App.axaml.cs b/KitX Script/Kscript.Editor/Kscript.Editor/App.axaml.cs deleted file mode 100644 index 7f0c4e8..0000000 --- a/KitX Script/Kscript.Editor/Kscript.Editor/App.axaml.cs +++ /dev/null @@ -1,42 +0,0 @@ -using Avalonia; -using Avalonia.Controls.ApplicationLifetimes; -using Avalonia.Markup.Xaml; -using Avalonia.VisualTree; -using Kscript.Editor.Data; -using Kscript.Editor.ViewModels; -using Kscript.Editor.Views; - -namespace Kscript.Editor; - -public partial class App : Application -{ - public override void Initialize() - { - AvaloniaXamlLoader.Load(this); - } - - public override void OnFrameworkInitializationCompleted() - { - if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) - { - var mainWin = new MainWindow - { - DataContext = new MainViewModel() - }; - desktop.MainWindow = mainWin; - - nameof(MainWindow).RegisterWindowHandle(mainWin.PlatformImpl?.Handle.Handle); - - nameof(GlobalInfo.WindowHandles).InvokeReactor(); - } - else if (ApplicationLifetime is ISingleViewApplicationLifetime singleViewPlatform) - { - singleViewPlatform.MainView = new MainView - { - DataContext = new MainViewModel() - }; - } - - base.OnFrameworkInitializationCompleted(); - } -} diff --git a/KitX Script/Kscript.Editor/Kscript.Editor/Assets/avalonia-logo.ico b/KitX Script/Kscript.Editor/Kscript.Editor/Assets/avalonia-logo.ico deleted file mode 100644 index da8d49f..0000000 Binary files a/KitX Script/Kscript.Editor/Kscript.Editor/Assets/avalonia-logo.ico and /dev/null differ diff --git a/KitX Script/Kscript.Editor/Kscript.Editor/Data/GlobalInfo.cs b/KitX Script/Kscript.Editor/Kscript.Editor/Data/GlobalInfo.cs deleted file mode 100644 index 6b70214..0000000 --- a/KitX Script/Kscript.Editor/Kscript.Editor/Data/GlobalInfo.cs +++ /dev/null @@ -1,72 +0,0 @@ -using Common.BasicHelper.Utils.Extensions; -using System; -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace Kscript.Editor.Data; - -public static class GlobalInfo -{ - public static readonly Dictionary> InfoReactors = new(); - - public static void RegisterInfoReactor(this string name, Action action) - { - if (InfoReactors.TryGetValue(name, out var value)) - value.Push(action); - else InfoReactors.Add(name, new Queue().Push(action)); - } - - public static void InvokeReactor(this string name) - { - if (InfoReactors.TryGetValue(name, out var value)) - value.ForEach(x => x.Invoke(), true); - } - - private static void OnInfoChanged(string info) => InvokeReactor(info); - - public static readonly Dictionary WindowHandles = new() - { - { "MainWindow", null } - }; - - public static void RegisterWindowHandle(this string name, nint? value) - { - if (WindowHandles.ContainsKey(name)) - WindowHandles[name] = value; - else WindowHandles.Add(name, value); - - OnInfoChanged(nameof(WindowHandles)); - } - - public static nint RequestWindowHandle(this string name) - { - if (WindowHandles.TryGetValue(name, out var value)) - if (value is not null) - return (nint)value; - throw new IndexOutOfRangeException(); - } - - private static EditorState editorState = EditorState.Normal; - - public static EditorState EditorState - { - get => editorState; - set - { - editorState = value; - OnInfoChanged(nameof(EditorState)); - } - } -} - -public enum EditorState -{ - Normal = 0, - Running = 1, - Debugging = 2, - RunInError = 3, - EventInvoking = 4, - TestsRunning = 5, - TestsPassed = 6, - TestsFailed = 7, -} diff --git a/KitX Script/Kscript.Editor/Kscript.Editor/Kscript.Editor.csproj b/KitX Script/Kscript.Editor/Kscript.Editor/Kscript.Editor.csproj deleted file mode 100644 index 9806892..0000000 --- a/KitX Script/Kscript.Editor/Kscript.Editor/Kscript.Editor.csproj +++ /dev/null @@ -1,30 +0,0 @@ - - - net7.0 - enable - latest - - - - - - - - - - - - - - - - - - - - - - diff --git a/KitX Script/Kscript.Editor/Kscript.Editor/Roots.xml b/KitX Script/Kscript.Editor/Kscript.Editor/Roots.xml deleted file mode 100644 index 17d3222..0000000 --- a/KitX Script/Kscript.Editor/Kscript.Editor/Roots.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/KitX Script/Kscript.Editor/Kscript.Editor/ViewLocator.cs b/KitX Script/Kscript.Editor/Kscript.Editor/ViewLocator.cs deleted file mode 100644 index 4e66180..0000000 --- a/KitX Script/Kscript.Editor/Kscript.Editor/ViewLocator.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Avalonia.Controls; -using Avalonia.Controls.Templates; -using Kscript.Editor.ViewModels; -using System; - -namespace Kscript.Editor; - -public class ViewLocator : IDataTemplate -{ - public IControl? Build(object? data) - { - if (data is null) - return null; - - var name = data.GetType().FullName!.Replace("ViewModel", "View"); - var type = Type.GetType(name); - - if (type != null) - { - return (Control)Activator.CreateInstance(type)!; - } - - return new TextBlock { Text = name }; - } - - public bool Match(object? data) - { - return data is ViewModelBase; - } -} diff --git a/KitX Script/Kscript.Editor/Kscript.Editor/ViewModels/MainViewModel.cs b/KitX Script/Kscript.Editor/Kscript.Editor/ViewModels/MainViewModel.cs deleted file mode 100644 index cf9598b..0000000 --- a/KitX Script/Kscript.Editor/Kscript.Editor/ViewModels/MainViewModel.cs +++ /dev/null @@ -1,90 +0,0 @@ -using Avalonia.Media; -using Kscript.Editor.Data; -using ReactiveUI; -using System.Reactive; - -namespace Kscript.Editor.ViewModels; - -public class MainViewModel : ViewModelBase -{ - - public MainViewModel() - { - UpdateVMCommand = ReactiveCommand.Create(UpdateVM); - - nameof(GlobalInfo.EditorState).RegisterInfoReactor( - () => BackgroundTintColor = EditorState2BackgroundColor); - } - - public static Color EditorState2BackgroundColor - { - get - { - var state = GlobalInfo.EditorState; - return state switch - { - EditorState.Normal => Colors.Transparent, - EditorState.Running => Colors.Blue, - EditorState.Debugging => Colors.Yellow, - EditorState.RunInError => Colors.Red, - EditorState.EventInvoking => Colors.RosyBrown, - EditorState.TestsRunning => Colors.GreenYellow, - EditorState.TestsPassed => Colors.Green, - EditorState.TestsFailed => Colors.OrangeRed, - _ => Colors.Transparent, - }; - } - } - - private Color backgroundTintColor = Colors.Transparent; - - public Color BackgroundTintColor - { - get => EditorState2BackgroundColor; - set => this.RaiseAndSetIfChanged(ref backgroundTintColor, value); - } - - private double backgroundTintOpacity = 0.2; - - public double BackgroundTintOpacity - { - get => backgroundTintOpacity; - set => this.RaiseAndSetIfChanged(ref backgroundTintOpacity, value); - } - - private double materialOpacity = 0; - - public double MaterialOpacity - { - get => materialOpacity; - set => this.RaiseAndSetIfChanged(ref materialOpacity, value); - } - - private string content = "Update"; - - public string Content - { - get => content; - set => this.RaiseAndSetIfChanged(ref content, value); - } - - public ReactiveCommand UpdateVMCommand { get; } - - public void UpdateVM() - { - GlobalInfo.EditorState = GlobalInfo.EditorState switch - { - EditorState.Normal => EditorState.Running, - EditorState.Running => EditorState.Debugging, - EditorState.Debugging => EditorState.RunInError, - EditorState.RunInError => EditorState.EventInvoking, - EditorState.EventInvoking => EditorState.TestsRunning, - EditorState.TestsRunning => EditorState.TestsPassed, - EditorState.TestsPassed => EditorState.TestsFailed, - EditorState.TestsFailed => GlobalInfo.EditorState = EditorState.Normal, - _ => EditorState.Normal, - }; - Content = GlobalInfo.EditorState.ToString(); - } - -} diff --git a/KitX Script/Kscript.Editor/Kscript.Editor/ViewModels/ViewModelBase.cs b/KitX Script/Kscript.Editor/Kscript.Editor/ViewModels/ViewModelBase.cs deleted file mode 100644 index 74d1d44..0000000 --- a/KitX Script/Kscript.Editor/Kscript.Editor/ViewModels/ViewModelBase.cs +++ /dev/null @@ -1,7 +0,0 @@ -using ReactiveUI; - -namespace Kscript.Editor.ViewModels; - -public class ViewModelBase : ReactiveObject -{ -} diff --git a/KitX Script/Kscript.Editor/Kscript.Editor/Views/MainView.axaml b/KitX Script/Kscript.Editor/Kscript.Editor/Views/MainView.axaml deleted file mode 100644 index 81617b9..0000000 --- a/KitX Script/Kscript.Editor/Kscript.Editor/Views/MainView.axaml +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - - - - - - -