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 0000000..caa9e48
Binary files /dev/null and b/MelskinTest/Resources/Images/authentication.ico differ
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" />
-// MultiBinding >
-// TextBox.Text >
-// < !--
-// \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
//
// Binding.ValidationRules >
// Binding >
- // TextBox.Text >
+ // TextBox.Tip >
// TextBox >
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
{