Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
231 changes: 231 additions & 0 deletions src/DynamoCoreWpf/ViewModels/Core/DynamoViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4513,6 +4513,237 @@
return true;
}

internal bool CanRecordNodeHelpData(object obj)

Check warning on line 4516 in src/DynamoCoreWpf/ViewModels/Core/DynamoViewModel.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Make 'CanRecordNodeHelpData' a static method.

See more on https://sonarcloud.io/project/issues?id=DynamoDS_Dynamo&issues=AZ1J_zD2a9eWpxzqhp1s&open=AZ1J_zD2a9eWpxzqhp1s&pullRequest=16870
{
return true;
}

/// <summary>
/// Records help documentation for the currently selected node.
/// Generates: .md file, _img.jpg screenshot, and .dyn sample graph.
/// Debug-only feature for creating node help documentation.
/// </summary>
Comment thread
zeusongit marked this conversation as resolved.
internal void RecordNodeHelpData(object parameter)
{
// 1. Validate selection - need exactly one node selected
var selectedNodes = DynamoSelection.Instance.Selection.OfType<NodeModel>().ToList();

if (selectedNodes.Count == 0)
{
ToastManager?.CreateRealTimeInfoWindow("Please select a node to record help data.", true);
return;
}

if (selectedNodes.Count > 1)
{
ToastManager?.CreateRealTimeInfoWindow("Please select only one node to record help data.", true);
return;
}

var selectedNode = selectedNodes.First();

// 2. Get minimum qualified name for file naming (no hash)
var mqn = GetMinimumQualifiedName(selectedNode);
Comment thread
zeusongit marked this conversation as resolved.

// 3. Use node name for display in toast
var nodeName = selectedNode.Name;

// 4. Get output directory - backtrack from bin\AnyCPU\Debug to doc\distrib\NodeHelpFiles\en-US
DirectoryInfo nodeHelpDocPath;
try
{
// Get the executing assembly location (bin\AnyCPU\Debug)
var assemblyLocation = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);

// Backtrack to Dynamo root: go up 3 levels from bin\AnyCPU\Debug
var dynamoRoot = Path.GetDirectoryName(Path.GetDirectoryName(Path.GetDirectoryName(assemblyLocation)));

// Build path to doc\distrib\NodeHelpFiles\en-US
var docPath = Path.Combine(dynamoRoot, "doc", "distrib", "NodeHelpFiles", "en-US");
nodeHelpDocPath = new DirectoryInfo(docPath);

// Create directory if it doesn't exist
if (!nodeHelpDocPath.Exists)
{
nodeHelpDocPath.Create();
}
}
catch (Exception ex)
{
ToastManager?.CreateRealTimeInfoWindow($"Failed to create output directory: {ex.Message}", true);
return;
}

// 5. Generate file paths using minimum qualified name directly (no hash)
var mdFileName = $"{mqn}.md";
var imgFileName = $"{mqn}_img.jpg";
var dynFileName = $"{mqn}.dyn";

var mdFilePath = Path.Combine(nodeHelpDocPath.FullName, mdFileName);
var imgFilePath = Path.Combine(nodeHelpDocPath.FullName, imgFileName);
var dynFilePath = Path.Combine(nodeHelpDocPath.FullName, dynFileName);

try
{
// 6. Export workspace image with 3D background overlay
ExportWorkspaceImageWithBackground(imgFilePath);

// 7. Save sample graph (.dyn)
SaveAs(dynFilePath, SaveContext.Save, false);

// 8. Generate markdown file
GenerateNodeHelpMarkdown(selectedNode, mqn, mdFilePath, imgFileName);

// 9. Show success toast with node name
var message = $"Node help files created for '{nodeName}':\n" +
$" {mdFileName}\n" +
$" {imgFileName}\n" +
$" {dynFileName}\n" +
$"Location: {nodeHelpDocPath.FullName}";
ToastManager?.CreateRealTimeInfoWindow(message, true);
}
catch (Exception ex)
{
ToastManager?.CreateRealTimeInfoWindow($"Error recording node help data: {ex.Message}", true);
}
}

/// <summary>
/// Exports workspace image with 3D background overlay.
/// Combines the 3D preview background with the workspace graph view.
/// </summary>
Comment thread
zeusongit marked this conversation as resolved.
private void ExportWorkspaceImageWithBackground(string outputPath)
{
var tempDir = Path.GetTempPath();
var backgroundPath = Path.Combine(tempDir, $"bg_{Guid.NewGuid()}.png");
var foregroundPath = Path.Combine(tempDir, $"fg_{Guid.NewGuid()}.png");

try
{
// Export 3D background
BackgroundPreviewViewModel?.ZoomToFitCommand?.Execute(null);
var backgroundImageArgs = new ImageSaveEventArgs(backgroundPath);
OnRequestSave3DImage(this, backgroundImageArgs);

// Export workspace (graph view)
var foregroundImageArgs = new ImageSaveEventArgs(foregroundPath);
OnRequestSaveImage(this, foregroundImageArgs);

// Combine images if both exist
if (File.Exists(backgroundPath) && File.Exists(foregroundPath))
{
using (var combined = OverlayWorkspaceImages(backgroundPath, foregroundPath))
{
if (combined != null)
{
combined.Save(outputPath, System.Drawing.Imaging.ImageFormat.Jpeg);
}
else
{
// Fallback: just copy the workspace image
File.Copy(foregroundPath, outputPath, true);
}
}
}
else if (File.Exists(foregroundPath))
{
// Fallback: just use workspace image if 3D background not available
File.Copy(foregroundPath, outputPath, true);
}
}
finally
{
// Clean up temp files
try { if (File.Exists(backgroundPath)) File.Delete(backgroundPath); } catch { }

Check warning on line 4657 in src/DynamoCoreWpf/ViewModels/Core/DynamoViewModel.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Either remove or fill this block of code.

See more on https://sonarcloud.io/project/issues?id=DynamoDS_Dynamo&issues=AZ1J_zD2a9eWpxzqhp1v&open=AZ1J_zD2a9eWpxzqhp1v&pullRequest=16870

Check warning on line 4657 in src/DynamoCoreWpf/ViewModels/Core/DynamoViewModel.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Handle the exception or explain in a comment why it can be ignored.

See more on https://sonarcloud.io/project/issues?id=DynamoDS_Dynamo&issues=AZ1J_zD2a9eWpxzqhp1t&open=AZ1J_zD2a9eWpxzqhp1t&pullRequest=16870
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, a comment as to why the exception is eaten could not hurt

try { if (File.Exists(foregroundPath)) File.Delete(foregroundPath); } catch { }

Check warning on line 4658 in src/DynamoCoreWpf/ViewModels/Core/DynamoViewModel.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Handle the exception or explain in a comment why it can be ignored.

See more on https://sonarcloud.io/project/issues?id=DynamoDS_Dynamo&issues=AZ1J_zD2a9eWpxzqhp1u&open=AZ1J_zD2a9eWpxzqhp1u&pullRequest=16870

Check warning on line 4658 in src/DynamoCoreWpf/ViewModels/Core/DynamoViewModel.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Either remove or fill this block of code.

See more on https://sonarcloud.io/project/issues?id=DynamoDS_Dynamo&issues=AZ1J_zD2a9eWpxzqhp1w&open=AZ1J_zD2a9eWpxzqhp1w&pullRequest=16870
Comment thread
zeusongit marked this conversation as resolved.
}
}

/// <summary>
/// Overlay workspace graph over 3D background.
/// </summary>
Comment thread
zeusongit marked this conversation as resolved.
private static System.Drawing.Bitmap OverlayWorkspaceImages(string backgroundPath, string foregroundPath)
{
try
{
using (var baseImage = (System.Drawing.Bitmap)System.Drawing.Image.FromFile(backgroundPath))
using (var overlayImage = (System.Drawing.Bitmap)System.Drawing.Image.FromFile(foregroundPath))
{
// Calculate scale to fit overlay properly
var scale = 1.5;
Comment thread
zeusongit marked this conversation as resolved.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, a comment here too, explain why 1.5

var scaleFactor = Math.Max(
overlayImage.Width / (float)baseImage.Width,
overlayImage.Height / (float)baseImage.Height);
var newWidth = (int)(baseImage.Width * scaleFactor * scale);
var newHeight = (int)(baseImage.Height * scaleFactor * scale);

var finalImage = new System.Drawing.Bitmap(newWidth, newHeight,
System.Drawing.Imaging.PixelFormat.Format32bppArgb);

using (var graphics = System.Drawing.Graphics.FromImage(finalImage))
{
graphics.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceOver;
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;

// Dynamo-white background
graphics.Clear(System.Drawing.Color.FromArgb(249, 249, 249));

const float BACKGROUND_VERTICAL_OFFSET_RATIO = 0.15f;
const float OVERLAY_VERTICAL_POSITION_RATIO = 0.25f;

// Draw resized background slightly down (15% from top)
using (var resizedBg = new System.Drawing.Bitmap(baseImage, newWidth, newHeight))
{
graphics.DrawImage(resizedBg, 0, (int)(newHeight * BACKGROUND_VERTICAL_OFFSET_RATIO));
}

// Draw workspace overlay in upper center
var offsetX = (newWidth - overlayImage.Width) / 2;
var offsetY = (int)((newHeight - overlayImage.Height) * OVERLAY_VERTICAL_POSITION_RATIO);
graphics.DrawImage(overlayImage, offsetX, offsetY);
}

return finalImage;
}
}
catch
{
return null;
}
}

/// <summary>
/// Generates the markdown help file for a node.
/// </summary>
Comment thread
zeusongit marked this conversation as resolved.
/// <param name="node">The node model instance for which the help markdown is generated.</param>
/// <param name="mqn">The fully qualified node name used in the generated markdown header.</param>
/// <param name="outputPath">The full file path where the markdown help file will be written.</param>
/// <param name="imgFileName">The image file name referenced from the markdown example section.</param>
private void GenerateNodeHelpMarkdown(NodeModel node, string mqn,

Check warning on line 4723 in src/DynamoCoreWpf/ViewModels/Core/DynamoViewModel.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Make 'GenerateNodeHelpMarkdown' a static method.

See more on https://sonarcloud.io/project/issues?id=DynamoDS_Dynamo&issues=AZ1J_zD2a9eWpxzqhp1y&open=AZ1J_zD2a9eWpxzqhp1y&pullRequest=16870
string outputPath, string imgFileName)
{
var sb = new System.Text.StringBuilder();

// Add HTML comment header with node name (pattern from existing help files)
sb.AppendLine($"<!--- {mqn} --->");

// In Depth section with node description
sb.AppendLine("## In Depth");
var description = !string.IsNullOrWhiteSpace(node.Description)
? node.Description
: $"{node.Name} - [Add description here]";
sb.AppendLine(description);
sb.AppendLine();

// Example file section with image
sb.AppendLine("___");
sb.AppendLine("## Example File");
sb.AppendLine();
sb.AppendLine($"![{node.Name}](./{imgFileName})");

File.WriteAllText(outputPath, sb.ToString());
}
#region "Home And End key press events in Canvas"
/// <summary>
/// Enable the shortcut key events when there is no selection.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ private void InitializeDelegateCommands()
SetNumberFormatCommand = new DelegateCommand(SetNumberFormat, CanSetNumberFormat);
DumpNodeHelpDataCommand = new DelegateCommand(DumpNodeHelpData, CanDumpNodeHelpData);
DumpNodeIconsCommand = new DelegateCommand(DumpNodeIconData, CanDumpNodeIconData);
RecordNodeHelpDataCommand = new DelegateCommand(RecordNodeHelpData, CanRecordNodeHelpData);
DumpLibraryToXmlCommand = new DelegateCommand(model.DumpLibraryToXml, model.CanDumpLibraryToXml);
ShowNewPresetsDialogCommand = new DelegateCommand(ShowNewPresetStateDialogAndMakePreset, CanShowNewPresetStateDialog);
NodeFromSelectionCommand = new DelegateCommand(CreateNodeFromSelection, CanCreateNodeFromSelection);
Expand Down Expand Up @@ -184,6 +185,7 @@ private void InitializeDelegateCommands()
public DelegateCommand SetNumberFormatCommand { get; set; }
public DelegateCommand OpenRecentCommand { get; set; }
public DelegateCommand CheckForLatestRenderCommand { get; set; }
public DelegateCommand RecordNodeHelpDataCommand { get; set; }
public DelegateCommand DumpLibraryToXmlCommand { get; set; }
public DelegateCommand DumpNodeHelpDataCommand { get; set; }
public DelegateCommand DumpNodeIconsCommand { get; set; }
Expand Down
3 changes: 3 additions & 0 deletions src/DynamoCoreWpf/Views/Core/DynamoView.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -696,6 +696,9 @@
<MenuItem Command="{Binding DumpNodeIconsCommand}"
Header="{x:Static p:Resources.DynamoViewDebugMenuDumpNodeIcons}"
IsEnabled="True" />
<MenuItem Command="{Binding RecordNodeHelpDataCommand}"
Header="Record Node Help Data"
IsEnabled="True" />
</MenuItem>
<MenuItem Name="DebugModes"
Click="OnDebugModesClick"
Expand Down
Loading