From e03e1b976676101e560a212955a3653a55f946ed Mon Sep 17 00:00:00 2001 From: ShrlAlgo Date: Sun, 1 Mar 2026 10:42:42 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=89=98=E7=9B=98=E5=9B=BE?= =?UTF-8?q?=E6=A0=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Melskin/Appearance/ThemeManager.cs | 3 +- Melskin/Controls/AutoComplete.xaml.cs | 2 +- .../Decorations/GlassChromeDecorator.cs | 2 +- Melskin/Controls/MelWindow.xaml.cs | 9 +- Melskin/Controls/Toast/ToastControl.xaml.cs | 2 +- Melskin/Controls/TrayIcon.cs | 216 ++++++++++++++++++ Melskin/Markup/SymbolIconExtension.cs | 2 +- MelskinTest/MainWindow.xaml | 21 ++ MelskinTest/MainWindow.xaml.cs | 32 ++- MelskinTest/MelskinTest.csproj | 5 + .../Resources/Images/authentication.ico | Bin 0 -> 4286 bytes .../Checker/ModelCheckCmd.cs | 2 +- .../Checker/ModelCheckViewModel.cs | 7 +- .../Common/Extensions/StringExtensions.cs | 43 +--- .../ValidationRules/RequiredValidationRule.cs | 4 +- .../Entry/DrawingViewApp.cs | 2 +- .../FamMaster/FamilyLibraryViewModel.cs | 2 +- .../General/LLMScript/CodeExecutor.cs | 52 +++++ .../General/LLMScript/ILLMProvider.cs | 11 + .../General/LLMScript/LLMCmd.cs | 40 ++++ .../General/LLMScript/OllamaProvider.cs | 74 ++++++ .../General/LLMScript/OpenAiProvider.cs | 84 +++++++ .../General/LLMScript/PromptManager.cs | 24 ++ ShrlAlgoToolkit.RevitAddins/GlobalUsings.cs | 2 +- .../ShrlAlgoToolkit.RevitAddins.csproj | 7 +- ShrlAlgoToolkit.RevitCore/Base/BaseCommand.cs | 6 +- 26 files changed, 582 insertions(+), 72 deletions(-) create mode 100644 Melskin/Controls/TrayIcon.cs create mode 100644 MelskinTest/Resources/Images/authentication.ico create mode 100644 ShrlAlgoToolkit.RevitAddins/General/LLMScript/CodeExecutor.cs create mode 100644 ShrlAlgoToolkit.RevitAddins/General/LLMScript/ILLMProvider.cs create mode 100644 ShrlAlgoToolkit.RevitAddins/General/LLMScript/LLMCmd.cs create mode 100644 ShrlAlgoToolkit.RevitAddins/General/LLMScript/OllamaProvider.cs create mode 100644 ShrlAlgoToolkit.RevitAddins/General/LLMScript/OpenAiProvider.cs create mode 100644 ShrlAlgoToolkit.RevitAddins/General/LLMScript/PromptManager.cs diff --git a/Melskin/Appearance/ThemeManager.cs b/Melskin/Appearance/ThemeManager.cs index f4ea90c..0e2197d 100644 --- a/Melskin/Appearance/ThemeManager.cs +++ b/Melskin/Appearance/ThemeManager.cs @@ -1,5 +1,4 @@ -using System.Collections; -using System.Diagnostics; +using System.Diagnostics; using System.IO; using System.Reflection; using System.Windows.Media.Animation; diff --git a/Melskin/Controls/AutoComplete.xaml.cs b/Melskin/Controls/AutoComplete.xaml.cs index 73cce5e..e4c6072 100644 --- a/Melskin/Controls/AutoComplete.xaml.cs +++ b/Melskin/Controls/AutoComplete.xaml.cs @@ -235,7 +235,7 @@ namespace Melskin.Controls } view.Refresh(); - // !string.IsNullOrEmpty(Text) 的判断 + // !string.IsNullOrEmpty(Tip) 的判断 if (!string.IsNullOrEmpty(Text) && !view.IsEmpty && textBox is { IsKeyboardFocused: true }) { IsDropDownOpen = true; diff --git a/Melskin/Controls/Decorations/GlassChromeDecorator.cs b/Melskin/Controls/Decorations/GlassChromeDecorator.cs index 88da6dd..be8071d 100644 --- a/Melskin/Controls/Decorations/GlassChromeDecorator.cs +++ b/Melskin/Controls/Decorations/GlassChromeDecorator.cs @@ -17,7 +17,7 @@ namespace Melskin.Controls.Decorations; /// CornerRadius="10" /// Background="#40FF0000"> /// -/// +/// /// /// /// diff --git a/Melskin/Controls/MelWindow.xaml.cs b/Melskin/Controls/MelWindow.xaml.cs index 305ad06..b68a0dc 100644 --- a/Melskin/Controls/MelWindow.xaml.cs +++ b/Melskin/Controls/MelWindow.xaml.cs @@ -1,5 +1,4 @@ -using System.Diagnostics; -using System.Runtime.InteropServices; +using System.Runtime.InteropServices; using System.Windows.Input; namespace Melskin.Controls; @@ -193,7 +192,7 @@ public class MelWindow : Window if (maximizeRestoreButton is Visual maximizeButtonVisual) { var bounds = VisualTreeHelper.GetDescendantBounds(maximizeButtonVisual); - Debug.WriteLine(bounds.ToString()); + //Debug.WriteLine(bounds.ToString()); var buttonTransform = maximizeButtonVisual.TransformToAncestor(this); var buttonRect = buttonTransform.TransformBounds(bounds); @@ -228,7 +227,7 @@ public class MelWindow : Window //VisualStateManager.GoToState(maximizeRestoreButton, "MouseOver", true); new SolidColorBrush((Color)ColorConverter.ConvertFromString("#1A000000")), "Normal" => Brushes.Transparent, - //VisualStateManager.GoToState(maximizeRestoreButton, "Normal", true); + //VisualStateManager.GoToState(maximizeRestoreButton, "Normal", true); _ => maximizeRestoreButton?.Background }; } @@ -322,7 +321,7 @@ public class MelWindow : Window /// public override void OnApplyTemplate() { - base.OnApplyTemplate(); + base.OnApplyTemplate(); minimizeButton?.Click -= MinimizeButtonClickHandler; minimizeButton = GetTemplateChild(VbMinimizeButtonName) as Button; diff --git a/Melskin/Controls/Toast/ToastControl.xaml.cs b/Melskin/Controls/Toast/ToastControl.xaml.cs index 8736ffe..ca86165 100644 --- a/Melskin/Controls/Toast/ToastControl.xaml.cs +++ b/Melskin/Controls/Toast/ToastControl.xaml.cs @@ -83,7 +83,7 @@ public partial class ToastControl // foreground = new SolidColorBrush(Color.FromRgb(245, 108, 108)); // break; // } - // control.IconTextBlock.Text = icon; + // control.IconTextBlock.Tip = icon; // control.IconTextBlock.Foreground = foreground; // control.RootBorder.Background = background; // control.RootBorder.BorderBrush = background; diff --git a/Melskin/Controls/TrayIcon.cs b/Melskin/Controls/TrayIcon.cs new file mode 100644 index 0000000..5f1c493 --- /dev/null +++ b/Melskin/Controls/TrayIcon.cs @@ -0,0 +1,216 @@ +using System.Runtime.InteropServices; +using System.Windows.Controls.Primitives; +using System.Windows.Input; + +namespace Melskin.Controls +{ + public class TrayIcon : FrameworkElement, IDisposable + { + // 1. 悬浮提示文本 + public static readonly DependencyProperty TipProperty = + DependencyProperty.Register(nameof(Tip), typeof(string), typeof(TrayIcon), + new PropertyMetadata(string.Empty, OnTextChanged)); + + public string Tip + { + get => (string)GetValue(TipProperty); + set => SetValue(TipProperty, value); + } + + // 2. 双击命令 + public static readonly DependencyProperty DoubleClickCommandProperty = + DependencyProperty.Register(nameof(DoubleClickCommand), typeof(ICommand), typeof(TrayIcon)); + + public ICommand DoubleClickCommand + { + get => (ICommand)GetValue(DoubleClickCommandProperty); + set => SetValue(DoubleClickCommandProperty, value); + } + + public static readonly DependencyProperty ClickCommandProperty = + DependencyProperty.Register(nameof(ClickCommand), typeof(ICommand), typeof(TrayIcon)); + + public ICommand ClickCommand + { + get => (ICommand)GetValue(ClickCommandProperty); + set => SetValue(ClickCommandProperty, value); + } + + // 3. 【新增】图标本地路径 (例如: "app.ico") + public static readonly DependencyProperty IconPathProperty = + DependencyProperty.Register(nameof(IconPath), typeof(string), typeof(TrayIcon), + new PropertyMetadata(string.Empty, OnIconPathChanged)); + + public string IconPath + { + get => (string)GetValue(IconPathProperty); + set => SetValue(IconPathProperty, value); + } + + private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var ctrl = (TrayIcon)d; + if (ctrl._isAdded) + { + ctrl._nid.szTip = e.NewValue?.ToString() ?? ""; + Shell_NotifyIcon(NIM_MODIFY, ref ctrl._nid); + } + } + + private static void OnIconPathChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var ctrl = (TrayIcon)d; + if (ctrl._isAdded) + { + ctrl._nid.hIcon = ctrl.LoadMyIcon(e.NewValue?.ToString()); + Shell_NotifyIcon(NIM_MODIFY, ref ctrl._nid); + } + } + + // --- Win32 API 声明 --- + private const int NIM_ADD = 0x0000; + private const int NIM_MODIFY = 0x0001; + private const int NIM_DELETE = 0x0002; + private const int NIF_MESSAGE = 0x0001; + private const int NIF_ICON = 0x0002; + private const int NIF_TIP = 0x0004; + private const int WM_LBUTTONUP = 0x0202; // 左键弹起消息 + private const int WM_TRAYMOUSEMESSAGE = 0x0400 + 1024; + private const int WM_LBUTTONDBLCLK = 0x0203; + private const int WM_RBUTTONUP = 0x0205; [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + private struct NOTIFYICONDATA + { + public int cbSize; + public IntPtr hwnd; + public int uID; + public int uFlags; + public int uCallbackMessage; + public IntPtr hIcon; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] + public string szTip; + public int dwState; + public int dwStateMask; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] + public string szInfo; + public int uTimeoutOrVersion; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)] + public string szInfoTitle; + public int dwInfoFlags; + public Guid guidItem; + public IntPtr hBalloonIcon; + } + [DllImport("shell32.dll", CharSet = CharSet.Unicode)] + private static extern bool Shell_NotifyIcon(int dwMessage, ref NOTIFYICONDATA pnid); + + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool SetForegroundWindow(IntPtr hWnd); + + // 【新增】纯 Win32 加载本地图标 API + [DllImport("user32.dll", CharSet = CharSet.Unicode)] + private static extern IntPtr LoadImage(IntPtr hInst, string lpszName, uint uType, int cxDesired, int cyDesired, uint fuLoad); + + private IntPtr LoadMyIcon(string iconPath) + { + if (string.IsNullOrEmpty(iconPath)) return IntPtr.Zero; + // 1 = IMAGE_ICON, 0x00000010 = LR_LOADFROMFILE + return LoadImage(IntPtr.Zero, iconPath, 1, 0, 0, 0x00000010); + } + + // --- 内部状态 --- + private HwndSource _messageSink; + private int _uID = 100; + private bool _isAdded = false; + private NOTIFYICONDATA _nid; + + public TrayIcon() + { + Visibility = Visibility.Collapsed; + this.Loaded += OnLoaded; + this.Unloaded += OnUnloaded; + if (Application.Current != null) + Application.Current.Exit += (s, e) => Dispose(); + } + + private void OnLoaded(object sender, RoutedEventArgs e) + { + if (_isAdded) return; + + HwndSourceParameters p = new HwndSourceParameters("TrayIconMessageSink") + { + Width = 0, Height = 0, WindowStyle = 0, ExtendedWindowStyle = 0x00000080 + }; + _messageSink = new HwndSource(p); + _messageSink.AddHook(WndProc); + + _nid = new NOTIFYICONDATA + { + cbSize = Marshal.SizeOf(typeof(NOTIFYICONDATA)), + hwnd = _messageSink.Handle, + uID = _uID, + uFlags = NIF_MESSAGE | NIF_TIP | NIF_ICON, + uCallbackMessage = WM_TRAYMOUSEMESSAGE, + szTip = this.Tip ?? "", + // 加载属性中指定的图标 + hIcon = LoadMyIcon(this.IconPath) + }; + + Shell_NotifyIcon(NIM_ADD, ref _nid); + _isAdded = true; + } + + private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) + { + if (msg == WM_TRAYMOUSEMESSAGE) + { + int mouseMsg = lParam.ToInt32(); + if (mouseMsg == WM_LBUTTONUP) + { + if (ClickCommand?.CanExecute(null) == true) + { + ClickCommand.Execute(null); + } + handled = true; + } + else if (mouseMsg == WM_LBUTTONDBLCLK) + { + if (DoubleClickCommand?.CanExecute(null) == true) + DoubleClickCommand.Execute(null); + handled = true; + } + else if (mouseMsg == WM_RBUTTONUP) + { + ShowContextMenu(); + handled = true; + } + } + return IntPtr.Zero; + } + + private void ShowContextMenu() + { + if (this.ContextMenu != null) + { + SetForegroundWindow(_messageSink.Handle); + this.ContextMenu.PlacementTarget = null; + this.ContextMenu.Placement = PlacementMode.MousePoint; + this.ContextMenu.IsOpen = true; + } + } + + private void OnUnloaded(object sender, RoutedEventArgs e) => Dispose(); + + public void Dispose() + { + if (_isAdded) + { + Shell_NotifyIcon(NIM_DELETE, ref _nid); + _isAdded = false; + } + if (_messageSink != null) + { + _messageSink.RemoveHook(WndProc); + _messageSink.Dispose(); + _messageSink = null; + } + } + } +} diff --git a/Melskin/Markup/SymbolIconExtension.cs b/Melskin/Markup/SymbolIconExtension.cs index b10a495..50afb30 100644 --- a/Melskin/Markup/SymbolIconExtension.cs +++ b/Melskin/Markup/SymbolIconExtension.cs @@ -14,7 +14,7 @@ namespace Melskin.Markup; /// 使用此扩展时,需要确保已正确引用了Material Design图标字体资源。可以通过设置`FontFamily`属性来应用图标字体。 /// /// -/// <TextBlock Text="{enu:SymbolIcon Symbol=AddBox}" FontFamily="{StaticResource MaterialIconFont}"/> +/// <TextBlock Tip="{enu:SymbolIcon Symbol=AddBox}" FontFamily="{StaticResource MaterialIconFont}"/> /// /// [ContentProperty(nameof(Symbol))] diff --git a/MelskinTest/MainWindow.xaml b/MelskinTest/MainWindow.xaml index 43c361e..423af88 100644 --- a/MelskinTest/MainWindow.xaml +++ b/MelskinTest/MainWindow.xaml @@ -120,6 +120,27 @@ + + + + + + + + + + + + + diff --git a/MelskinTest/MainWindow.xaml.cs b/MelskinTest/MainWindow.xaml.cs index 4f0f1cc..2437e4c 100644 --- a/MelskinTest/MainWindow.xaml.cs +++ b/MelskinTest/MainWindow.xaml.cs @@ -1,11 +1,9 @@ using System; -using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; using System.Linq; -using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; @@ -82,6 +80,7 @@ public partial class MainWindow #region 命令 public ICommand ShowSelectedItemsCommand { get; set; } public ICommand ToggleDetailsCommand { get; set; } + public ICommand ShowWindowCommand { get; set; } [RelayCommand] private void AddArea() @@ -227,6 +226,7 @@ public partial class MainWindow { ShowSelectedItemsCommand = new RelayCommand(ShowSelectedItems); ToggleDetailsCommand = new RelayCommand(ToggleDetails); + ShowWindowCommand = new RelayCommand(ShowWindow); } #endregion @@ -456,12 +456,38 @@ public partial class MainWindow case 2: MyTutorial.ShowStep(StartBtn, "删除功能", "谨慎使用,这会清除数据。", "2 / 2"); break; - + default: MyTutorial.Close(); break; } } + private void ShowWindow() + { + this.Show(); + this.WindowState = WindowState.Normal; + this.Activate(); // 激活窗口使其置顶 + } + // --- 2. 右键菜单的 Click 事件 (Code-behind 风格) --- + private void MenuShow_Click(object sender, RoutedEventArgs e) + { + ShowWindow(); + } + + private void MenuExit_Click(object sender, RoutedEventArgs e) + { + // 真正退出程序 + Application.Current.Shutdown(); + } + + // --- 3. 重写关闭事件,实现“点X最小化到托盘” --- + protected override void OnClosing(CancelEventArgs e) + { + // 阻止窗口真正销毁 + e.Cancel = true; + // 隐藏窗口(此时托盘图标依然在运行) + this.Hide(); + } } #region 数据模型 diff --git a/MelskinTest/MelskinTest.csproj b/MelskinTest/MelskinTest.csproj index 5050c3b..432f8f8 100644 --- a/MelskinTest/MelskinTest.csproj +++ b/MelskinTest/MelskinTest.csproj @@ -34,5 +34,10 @@ + + + PreserveNewest + + diff --git a/MelskinTest/Resources/Images/authentication.ico b/MelskinTest/Resources/Images/authentication.ico new file mode 100644 index 0000000000000000000000000000000000000000..caa9e48c1409a2c0b5fd6899271ebc2f4ebefe49 GIT binary patch literal 4286 zcmdUyy^l;$7{=c*WD(45$Vz5*Mq`&mf@UTZX!sX&rrjQeEmo_bK(ZC}YE?wD5m6{4 z3K5^XG)hB+6>G2GGjs3e%yG}`+?gHXZhmt<-{*PHxpU9lH7oE}tC>D+J~G>5W>Y|y zodor_FUJ1&t6qsLlX9njRO`#|+(zHtgsuMgM&;q^Hacscr0)XUhnMgwsb8d=Y)o|T z&oFtQ=kE15?O9lYIKW~uM|0O=2g$*Wr+x|^L&(?f*IdO6l*3wFIod?M2_M1tSg);^ zCikpB+0LV%eMsllC)#n-w7sh|j!}}Lw&tb90oUDAM**+)aXYUf$lL?dO-(JE8E1vmE<+BcxH?ulqK_4A&qnQ^=5QC^2k`SB_`B=%>wjbY zzNg#n$Nz)3XKwtkb!k^2bpGgUd3%p0dVg%fHFyMy z?>CoE%zX$~;T%Zo2uQnqUi%wBzx7}jW5?hMyoOL78q=Hx?1Z4E@9($k)nM#CbZ5YQ zKT}=50~-6ollMF;mbe-xg+X7Ybv_j(81 s<5*X#6vqq2I5vw{&1P53PAr>MTV|ytvoWz?R@?}r9bYbVJeRBSZ>xL`1^@s6 literal 0 HcmV?d00001 diff --git a/ShrlAlgoToolkit.RevitAddins/Checker/ModelCheckCmd.cs b/ShrlAlgoToolkit.RevitAddins/Checker/ModelCheckCmd.cs index 3a194a1..366ad67 100644 --- a/ShrlAlgoToolkit.RevitAddins/Checker/ModelCheckCmd.cs +++ b/ShrlAlgoToolkit.RevitAddins/Checker/ModelCheckCmd.cs @@ -1,12 +1,12 @@ using Autodesk.Revit.Attributes; using Nice3point.Revit.Toolkit.External; + using ShrlAlgoToolkit.RevitAddins.Common.Assists; namespace ShrlAlgoToolkit.RevitAddins.ModelManager; [Transaction(TransactionMode.Manual)] -[UsedImplicitly] public class ModelCheckCmd : ExternalCommand { public override void Execute() => WinDialogAssist.ShowOrActivate(UiApplication);//ModelCheckView view = SingletonViewAssist.GetInstance(out var isNewCreate);//if (isNewCreate)//{// view.DataContext = new ModelCheckViewModel(uiApplication);// view.ShowAhead();//}//view.Activate(); diff --git a/ShrlAlgoToolkit.RevitAddins/Checker/ModelCheckViewModel.cs b/ShrlAlgoToolkit.RevitAddins/Checker/ModelCheckViewModel.cs index 7555469..c38acd6 100644 --- a/ShrlAlgoToolkit.RevitAddins/Checker/ModelCheckViewModel.cs +++ b/ShrlAlgoToolkit.RevitAddins/Checker/ModelCheckViewModel.cs @@ -23,7 +23,6 @@ using ShrlAlgoToolkit.RevitAddins.Common.Controls; namespace ShrlAlgoToolkit.RevitAddins.ModelManager; -[UsedImplicitly] public partial class ModelCheckViewModel : ObservableObject { private readonly Standardizer.CorrectReferLevelExecutes correctReferLevelExecutes; @@ -116,13 +115,13 @@ public partial class ModelCheckViewModel : ObservableObject var errorItem = new MessageModel(type, "类型名称不符合三段式"); Items.Add(errorItem); } - + } - + } if (IsCheckProps) { - + } if (IsCheckSlope) { diff --git a/ShrlAlgoToolkit.RevitAddins/Common/Extensions/StringExtensions.cs b/ShrlAlgoToolkit.RevitAddins/Common/Extensions/StringExtensions.cs index c8d5d8d..8342e3d 100644 --- a/ShrlAlgoToolkit.RevitAddins/Common/Extensions/StringExtensions.cs +++ b/ShrlAlgoToolkit.RevitAddins/Common/Extensions/StringExtensions.cs @@ -3,48 +3,7 @@ using System.Text.RegularExpressions; namespace ShrlAlgoToolkit.RevitAddins.Common.Extensions; -#region 格式化 -//StringFormat={}{0:C} $123.46 -//StringFormat={}{0:C1} $123.5 -//StringFormat=单价:{0:C} 单价:$123.46 -//StringFormat={}{0}元 123.45678元 -//StringFormat={}{0:D6} 086723 -//StringFormat={}{0:F4} 28768234.9329 -//StringFormat={}{0:N3} 28,768,234.933 -//StringFormat={}{0:P1} 78.9 % -//StringFormat={}{0:0000.00} 0123.46 -//StringFormat={}{0:####.##} 123.46 -//StringFormat={}{0:d} 5/4/2015 -//StringFormat={}{0:D} Monday, May 04, 2015 -//StringFormat={}{0:f} Monday, May 04, 2015 5:46 PM -//StringFormat={}{0:F} Monday, May 04, 2015 5:46:56 PM -//StringFormat={}{0:g} 5/4/2015 5:46 PM -//StringFormat={}{0:G} 5/4/2015 5:46:56 PM -//StringFormat={}{0:m} May 04 -//StringFormat={}{0:Distinct} May 04 -//StringFormat={}{0:t} 5:46 PM -//StringFormat={}{0:Command} 5:46:56 PM -//StringFormat={}{0:yyyy年MM月dd日} 2015年05月04日 -//StringFormat={}{0:yyyy-MM-dd} 2015-05-04 -//StringFormat={}{0:yyyy-MM-dd HH:mm} 2015-05-04 17:46 -//StringFormat={}{0:yyyy-MM-dd HH:mm:ss},,ConverterCulture=zh-CN||StringFormat='yyyy:MM:dd HH:mm:ss',,ConverterCulture=zh-CN 2015-05-04 17:46:56 -//< TextBox.Text > -// < MultiBinding StringFormat = "姓名:{0} {1}" > -// < Binding Path = "FristName" /> -// < Binding Path = "LastName" /> -// -// -// < !-- -// \a  BEL -// \b  BS - Backspace -// \f FF - Formfeed -// \n LF, NL - Linefeed, New Line -// \r CR - Carriage return -// \t HT - Tab, Horizontal Tabelator -// \v VT - Vertical Tabelator - -#endregion public static class StringExtensions { /// @@ -297,5 +256,5 @@ public static class StringExtensions ? rv : Convert.ToInt32(StrToFloat(expression, defValue)); } - + } diff --git a/ShrlAlgoToolkit.RevitAddins/Common/ValidationRules/RequiredValidationRule.cs b/ShrlAlgoToolkit.RevitAddins/Common/ValidationRules/RequiredValidationRule.cs index d6a3b24..8c24bce 100644 --- a/ShrlAlgoToolkit.RevitAddins/Common/ValidationRules/RequiredValidationRule.cs +++ b/ShrlAlgoToolkit.RevitAddins/Common/ValidationRules/RequiredValidationRule.cs @@ -13,7 +13,7 @@ public class RequiredValidationRule : ValidationRule // InputMethod.IsInputMethodEnabled= "False" // md:HintAssist.Hint= "楼板偏移" // md:TextFieldAssist.SuffixText= "mm" > - // < TextBox.Text > + // < TextBox.Tip > // < Binding // Path= "FloorOffset" // StringFormat= "{}{0:N2}" @@ -23,7 +23,7 @@ public class RequiredValidationRule : ValidationRule // // // - // + // // public override ValidationResult Validate(object value, CultureInfo cultureInfo) { diff --git a/ShrlAlgoToolkit.RevitAddins/Entry/DrawingViewApp.cs b/ShrlAlgoToolkit.RevitAddins/Entry/DrawingViewApp.cs index 5fc617a..1542a7e 100644 --- a/ShrlAlgoToolkit.RevitAddins/Entry/DrawingViewApp.cs +++ b/ShrlAlgoToolkit.RevitAddins/Entry/DrawingViewApp.cs @@ -129,7 +129,7 @@ public class DrawingViewApp // LargeImage = Resources.zoom_32px.ToBitmapSource(), // Size = RibbonItemSize.Large, // Name = "ZoomElement", - // Text = "最大化", + // Tip = "最大化", // ShowText = true, // ToolTip = "根据视图的图元是否可见,在切换视图时,对当前选中的图元进行快速缩放定位", // IsCheckable = true, diff --git a/ShrlAlgoToolkit.RevitAddins/FamMaster/FamilyLibraryViewModel.cs b/ShrlAlgoToolkit.RevitAddins/FamMaster/FamilyLibraryViewModel.cs index bc75511..7d4ff5e 100644 --- a/ShrlAlgoToolkit.RevitAddins/FamMaster/FamilyLibraryViewModel.cs +++ b/ShrlAlgoToolkit.RevitAddins/FamMaster/FamilyLibraryViewModel.cs @@ -359,7 +359,7 @@ public partial class FamilyLibraryViewModel : ObservableObject private void SearchFamily(object obj) { //ListViewItem foundItem = - // textListView.FindItemWithText(searchBox.Text, false, 0, true); + // textListView.FindItemWithText(searchBox.Tip, false, 0, true); //if (foundItem != null) //{ // textListView.TopItem = foundItem; diff --git a/ShrlAlgoToolkit.RevitAddins/General/LLMScript/CodeExecutor.cs b/ShrlAlgoToolkit.RevitAddins/General/LLMScript/CodeExecutor.cs new file mode 100644 index 0000000..1715513 --- /dev/null +++ b/ShrlAlgoToolkit.RevitAddins/General/LLMScript/CodeExecutor.cs @@ -0,0 +1,52 @@ +using System.IO; +using System.Reflection; + +using Autodesk.Revit.DB; + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; + +namespace Szmedi.RvKits.LLMScript +{ + public class CodeExecutor + { + public void Execute(string methodBody, Autodesk.Revit.DB.Document doc, UIDocument uidoc) + { + string code = $@" +using Autodesk.Revit.DB; +using Autodesk.Revit.UI; +using System; + +public class ScriptRunner +{{ + public void Run(Document doc, UIDocument uidoc) + {{ + {methodBody} + }} +}}"; + + var syntaxTree = CSharpSyntaxTree.ParseText(code); + + var compilation = CSharpCompilation.Create( + "DynamicScript") + .AddReferences( + MetadataReference.CreateFromFile(typeof(object).Assembly.Location), + MetadataReference.CreateFromFile(typeof(Document).Assembly.Location)) + .AddSyntaxTrees(syntaxTree) + .WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); + + using var ms = new MemoryStream(); + var result = compilation.Emit(ms); + + if (!result.Success) + throw new Exception("Compilation failed"); + + ms.Seek(0, SeekOrigin.Begin); + var assembly = Assembly.Load(ms.ToArray()); + + var type = assembly.GetType("ScriptRunner"); + var instance = Activator.CreateInstance(type); + type.GetMethod("Run").Invoke(instance, new object[] { doc, uidoc }); + } + } +} diff --git a/ShrlAlgoToolkit.RevitAddins/General/LLMScript/ILLMProvider.cs b/ShrlAlgoToolkit.RevitAddins/General/LLMScript/ILLMProvider.cs new file mode 100644 index 0000000..da9b94c --- /dev/null +++ b/ShrlAlgoToolkit.RevitAddins/General/LLMScript/ILLMProvider.cs @@ -0,0 +1,11 @@ +using System.Threading.Tasks; + +namespace Szmedi.RvKits.LLMScript +{ + internal interface ILLMProvider + { + Task GenerateStreamAsync(string systemPrompt, + string userPrompt, + Action onTokenReceived); + } +} diff --git a/ShrlAlgoToolkit.RevitAddins/General/LLMScript/LLMCmd.cs b/ShrlAlgoToolkit.RevitAddins/General/LLMScript/LLMCmd.cs new file mode 100644 index 0000000..ef99c2f --- /dev/null +++ b/ShrlAlgoToolkit.RevitAddins/General/LLMScript/LLMCmd.cs @@ -0,0 +1,40 @@ +using System.Text; + +using Autodesk.Revit.Attributes; +using Autodesk.Revit.DB; + +using Nice3point.Revit.Toolkit.External; + +namespace Szmedi.RvKits.LLMScript +{ + /// + /// Revit执行命令 + /// + [Transaction(TransactionMode.Manual)] + [Regeneration(RegenerationOption.Manual)] + public class LLMCmd : ExternalCommand + { + public async override void Execute() + { + string userTask = "将当前选中墙的Comments参数追加'检查'"; + + PromptManager promptManager = new PromptManager("Prompts/System_UltraStrict.txt"); + string systemPrompt = promptManager.GetSystemPrompt(); + string userPrompt = promptManager.BuildUserPrompt(userTask); + + var llm = new OllamaProvider("http://localhost:11434/api/chat", "revit-coder"); + StringBuilder fullCode = new StringBuilder(); + + await llm.GenerateStreamAsync( + systemPrompt, + userPrompt, + chunk => + { + fullCode.Append(chunk); + }); + CodeExecutor executor = new CodeExecutor(); + + executor.Execute(fullCode.ToString(), Document, UiDocument); + } + } +} \ No newline at end of file diff --git a/ShrlAlgoToolkit.RevitAddins/General/LLMScript/OllamaProvider.cs b/ShrlAlgoToolkit.RevitAddins/General/LLMScript/OllamaProvider.cs new file mode 100644 index 0000000..47f0e59 --- /dev/null +++ b/ShrlAlgoToolkit.RevitAddins/General/LLMScript/OllamaProvider.cs @@ -0,0 +1,74 @@ +using System.IO; +using System.Net.Http; +using System.Text; + +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace Szmedi.RvKits.LLMScript +{ + public class OllamaProvider : ILLMProvider + { + private static readonly HttpClient _client = new HttpClient(); + private readonly string _endpoint; + private readonly string _model; + + public OllamaProvider(string endpoint, string model) + { + _endpoint = endpoint; + _model = model; + } + + public async Task GenerateStreamAsync( + string systemPrompt, + string userPrompt, + Action onTokenReceived) + { + var payload = new + { + model = _model, + messages = new[] + { + new { role = "system", content = systemPrompt }, + new { role = "user", content = userPrompt } + }, + stream = true + }; + + string json = JsonConvert.SerializeObject(payload); + + var request = new HttpRequestMessage(HttpMethod.Post, _endpoint); + request.Content = new StringContent(json, Encoding.UTF8, "application/json"); + + var response = await _client.SendAsync( + request, + HttpCompletionOption.ResponseHeadersRead); + + response.EnsureSuccessStatusCode(); + + using (var stream = await response.Content.ReadAsStreamAsync()) + using (var reader = new StreamReader(stream)) + { + while (!reader.EndOfStream) + { + string line = await reader.ReadLineAsync(); + if (string.IsNullOrWhiteSpace(line)) + continue; + + JObject obj = JObject.Parse(line); + + bool done = obj["done"]?.Value() ?? false; + + var content = obj["message"]?["content"]; + if (content != null) + { + onTokenReceived(content.ToString()); + } + + if (done) + break; + } + } + } + } +} diff --git a/ShrlAlgoToolkit.RevitAddins/General/LLMScript/OpenAiProvider.cs b/ShrlAlgoToolkit.RevitAddins/General/LLMScript/OpenAiProvider.cs new file mode 100644 index 0000000..39c14e0 --- /dev/null +++ b/ShrlAlgoToolkit.RevitAddins/General/LLMScript/OpenAiProvider.cs @@ -0,0 +1,84 @@ +using System.IO; +using System.Net.Http; +using System.Text; + +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace Szmedi.RvKits.LLMScript +{ + public class OpenAiProvider : ILLMProvider + { + private static readonly HttpClient _client = new HttpClient(); + private readonly string _endpoint; + private readonly string _apiKey; + private readonly string _model; + + public OpenAiProvider(string endpoint, string apiKey, string model) + { + _endpoint = endpoint; + _apiKey = apiKey; + _model = model; + + _client.DefaultRequestHeaders.Clear(); + _client.DefaultRequestHeaders.Add("Authorization", $"Bearer {_apiKey}"); + } + + public async Task GenerateStreamAsync( + string systemPrompt, + string userPrompt, + Action onTokenReceived) + { + var payload = new + { + model = _model, + messages = new[] + { + new { role = "system", content = systemPrompt }, + new { role = "user", content = userPrompt } + }, + temperature = 0, + stream = true + }; + + string json = JsonConvert.SerializeObject(payload); + + var request = new HttpRequestMessage(HttpMethod.Post, _endpoint); + request.Content = new StringContent(json, Encoding.UTF8, "application/json"); + + var response = await _client.SendAsync( + request, + HttpCompletionOption.ResponseHeadersRead); + + response.EnsureSuccessStatusCode(); + + using (var stream = await response.Content.ReadAsStreamAsync()) + using (var reader = new StreamReader(stream)) + { + while (!reader.EndOfStream) + { + string line = await reader.ReadLineAsync(); + + if (string.IsNullOrWhiteSpace(line)) + continue; + + if (!line.StartsWith("data:")) + continue; + + string data = line.Substring(5).Trim(); + + if (data == "[DONE]") + break; + + JObject obj = JObject.Parse(data); + var delta = obj["choices"]?[0]?["delta"]?["content"]; + + if (delta != null) + { + onTokenReceived(delta.ToString()); + } + } + } + } + } +} diff --git a/ShrlAlgoToolkit.RevitAddins/General/LLMScript/PromptManager.cs b/ShrlAlgoToolkit.RevitAddins/General/LLMScript/PromptManager.cs new file mode 100644 index 0000000..5cb13c4 --- /dev/null +++ b/ShrlAlgoToolkit.RevitAddins/General/LLMScript/PromptManager.cs @@ -0,0 +1,24 @@ +using System.IO; + +namespace Szmedi.RvKits.LLMScript +{ + public class PromptManager + { + private readonly string _systemPrompt; + + public PromptManager(string promptPath) + { + _systemPrompt = File.ReadAllText(promptPath); + } + + public string GetSystemPrompt() + { + return _systemPrompt; + } + + public string BuildUserPrompt(string task) + { + return task.Trim(); + } + } +} diff --git a/ShrlAlgoToolkit.RevitAddins/GlobalUsings.cs b/ShrlAlgoToolkit.RevitAddins/GlobalUsings.cs index 24151de..03eacda 100644 --- a/ShrlAlgoToolkit.RevitAddins/GlobalUsings.cs +++ b/ShrlAlgoToolkit.RevitAddins/GlobalUsings.cs @@ -1,4 +1,4 @@ global using Autodesk.Revit.UI; -global using JetBrains.Annotations; + global using ShrlAlgoToolkit.RevitCore.Assists; global using ShrlAlgoToolkit.RevitCore.Extensions; diff --git a/ShrlAlgoToolkit.RevitAddins/ShrlAlgoToolkit.RevitAddins.csproj b/ShrlAlgoToolkit.RevitAddins/ShrlAlgoToolkit.RevitAddins.csproj index 1d9970b..66da4d9 100644 --- a/ShrlAlgoToolkit.RevitAddins/ShrlAlgoToolkit.RevitAddins.csproj +++ b/ShrlAlgoToolkit.RevitAddins/ShrlAlgoToolkit.RevitAddins.csproj @@ -86,6 +86,9 @@ + + C:\Program Files\Microsoft Visual Studio\18\Community\MSBuild\Microsoft\Microsoft.NET.Build.Extensions\net461\lib\System.Net.Http.dll + @@ -101,9 +104,11 @@ + - + + diff --git a/ShrlAlgoToolkit.RevitCore/Base/BaseCommand.cs b/ShrlAlgoToolkit.RevitCore/Base/BaseCommand.cs index a3f57d9..f0ad0df 100644 --- a/ShrlAlgoToolkit.RevitCore/Base/BaseCommand.cs +++ b/ShrlAlgoToolkit.RevitCore/Base/BaseCommand.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; - -using Autodesk.Revit.DB; +using Autodesk.Revit.DB; namespace ShrlAlgoToolkit.RevitCore.Base {