Skip to content

fix(web): normalize edit links, API GitHub actions, and img dimensions#67

Merged
PrzemyslawKlys merged 1 commit intov2-speedgonzalesfrom
fix/web-editlinks-apidocs-image-sizing-20260206
Feb 6, 2026
Merged

fix(web): normalize edit links, API GitHub actions, and img dimensions#67
PrzemyslawKlys merged 1 commit intov2-speedgonzalesfrom
fix/web-editlinks-apidocs-image-sizing-20260206

Conversation

@PrzemyslawKlys
Copy link
Member

Summary

  • normalize edit-link paths to collapse ./.. segments for clean GitHub URLs across mixed content roots
  • add API type-level source action button (GitHub-aware Edit on GitHub when source URL is GitHub)
  • extend image tag enhancement to include intrinsic width/height attributes when known
  • update fallback API docs CSS for new type action button and consistent custom checkbox styling
  • update optimize tests and docs for width/height injection behavior

Validation

  • dotnet test PowerForge.Tests/PowerForge.Tests.csproj -c Release --nologo
  • dotnet build PSPublishModule.sln -c Release --nologo

Copilot AI review requested due to automatic review settings February 6, 2026 12:49
@claude
Copy link

claude bot commented Feb 6, 2026

Pull Request Review: PR #67

Summary

This PR introduces three main enhancements:

  1. Path normalization for edit links to collapse . and .. segments
  2. GitHub-aware action buttons for API type documentation
  3. Intrinsic image dimensions (width/height) in optimized HTML

Code Quality Assessment

✅ Strengths

  1. Well-structured changes: Each feature is logically separated and addresses a specific improvement
  2. Proper HTML encoding: Consistent use of System.Web.HttpUtility.HtmlAttributeEncode for attributes and HtmlEncode for content
  3. Good documentation: Clear PR summary and updated docs reflect the new behavior
  4. Test coverage: Added assertions for width/height attributes in existing tests
  5. Defensive programming: Proper null checks and validation throughout

🔍 Code Review by Component

1. EditLinkResolver.cs - Path Normalization

Location: PowerForge.Web/Services/EditLinkResolver.cs:112-134

Good:

  • Handles both . and .. path segments correctly
  • Uses List<string> as a stack for building the normalized path
  • Early returns for empty/whitespace paths

Concerns:

  • Edge case: When encountering .. at the root level (when stack.Count == 0), the segment is silently ignored. This is reasonable, but could potentially hide path errors. Consider if this should log a warning for debugging.
  • Performance: Using List.RemoveAt(stack.Count - 1) is O(n) for list shrinking. For most paths this is fine, but using a Stack<string> would be more semantically correct and O(1) for pop operations.
// Current approach (lines 119-131)
var stack = new List<string>(parts.Length);
foreach (var part in parts)
{
    if (part == ".")
        continue;
    if (part == "..")
    {
        if (stack.Count > 0)
            stack.RemoveAt(stack.Count - 1); // O(n) operation
        continue;
    }
    stack.Add(part);
}

Suggestion: Consider using Stack<string> for better performance and semantic clarity:

var stack = new Stack<string>(parts.Length);
foreach (var part in parts)
{
    if (part == ".") continue;
    if (part == "..")
    {
        if (stack.Count > 0)
            stack.Pop(); // O(1) operation
        continue;
    }
    stack.Push(part);
}
return string.Join("/", stack.Reverse());

2. WebApiDocsGenerator.cs - GitHub Edit Action

Location: PowerForge.Web/Services/WebApiDocsGenerator.cs:3348-3384

Good:

  • Clean separation between GitHub and generic source URLs
  • Proper URI validation with Uri.TryCreate
  • Case-insensitive host comparison
  • Correct transformation from /blob/ to /edit/

Concerns:

  • Fragment/query handling: The code doesn't preserve or handle URL fragments (e.g., #L123) or query parameters. GitHub blob URLs often include line numbers as fragments like https://github.com/owner/repo/blob/main/file.cs#L45-L50. When converting to edit URLs, these fragments should be stripped (which happens implicitly here), but this behavior might be worth documenting.
  • Branch/tag parsing: The filePath extraction includes the branch/tag in the path. For example:
    /owner/repo/blob/main/src/File.cs
    → repoPath: /owner/repo
    → filePath: main/src/File.cs
    → editUrl: https://github.com/owner/repo/edit/main/src/File.cs
    
    This is correct, but relies on the structure being preserved. Works well for standard GitHub URLs.

Minor improvement: Consider adding a comment explaining the URL transformation:

// Transform GitHub blob URL to edit URL
// Example: /owner/repo/blob/branch/path/file.cs → /owner/repo/edit/branch/path/file.cs

3. WebAssetOptimizer.cs - Image Dimensions

Location: PowerForge.Web/Services/WebAssetOptimizer.cs:787-800

Good:

  • Captures both width and height during image processing
  • Only adds attributes if they don't already exist (respects manual overrides)
  • Consistent with existing attribute injection patterns
  • Proper counter tracking (hintedInFile++)

Concerns:

  • Regex patterns: New regexes ImgWidthAttrRegex and ImgHeightAttrRegex follow the same pattern as existing ones - good consistency
  • Attribute ordering: The new attributes are added before loading and decoding. While HTML attribute order doesn't matter functionally, consider if there's a preferred ordering convention in the codebase
  • Missing dimensions: When OriginalWidth or OriginalHeight is 0 (e.g., if image loading failed earlier), the attributes aren't added. This is correct behavior - no validation needed.

🐛 Potential Issues

None identified - The code appears solid with no obvious bugs.

🚀 Performance Considerations

  1. Path normalization: As mentioned, List.RemoveAt() is O(n), but unlikely to be a bottleneck unless dealing with extremely deep paths
  2. Regex matching: Two additional regex checks per image tag when EnhanceImageTags is enabled. This is minimal overhead and consistent with existing patterns
  3. Image dimension capture: Already reading images via MagickImage, so no additional I/O cost

🔒 Security Considerations

All good:

  • ✅ Proper HTML attribute encoding for all user-controlled data
  • ✅ URI validation before processing GitHub URLs
  • ✅ No path traversal vulnerabilities (normalization actually helps prevent these)
  • ✅ No XSS vulnerabilities in generated HTML
  • ✅ Safe handling of external URLs with target="_blank" rel="noopener"

🧪 Test Coverage

Adequate:

  • ✅ Test updated to verify width/height attributes are injected
  • ⚠️ Gap: No explicit tests for the new path normalization logic
  • ⚠️ Gap: No tests for the GitHub edit URL transformation

Recommendations:

  1. Add unit tests for EditLinkResolver.NormalizePath() covering:

    • Simple paths: "a/b/c""a/b/c"
    • Current directory: "a/./b""a/b"
    • Parent directory: "a/b/../c""a/c"
    • Multiple traversals: "a/b/../../c""c"
    • Root escaping: "../a""a" (or document expected behavior)
    • Windows paths: "a\\b\\c""a/b/c"
  2. Add unit tests for TryBuildGitHubEditUrl() covering:

    • Valid GitHub URLs
    • Non-GitHub URLs
    • GitHub URLs without /blob/
    • URLs with fragments and query parameters

📝 Documentation

Good: The documentation in PowerForge.Web.Pipeline.md has been updated to reflect the new width/height behavior.

💡 Suggestions for Future Enhancements

  1. Consider logging or telemetry when path normalization removes .. segments at root level
  2. Consider supporting other Git hosting platforms (GitLab, Bitbucket) in the edit URL transformation
  3. Consider caching normalized paths if the same paths are processed multiple times

Verdict

APPROVE

This is a well-crafted PR that adds useful functionality with proper attention to security and encoding. The code quality is high, and the changes are focused and non-breaking. The only minor concern is the performance characteristic of the path normalization algorithm, but it's acceptable for the expected use case.

Before Merging

Consider adding the unit tests mentioned above to increase confidence in the path normalization and URL transformation logic. These are complex string operations that benefit from explicit test coverage.


Review generated by Claude Code

@PrzemyslawKlys PrzemyslawKlys merged commit 8391737 into v2-speedgonzales Feb 6, 2026
9 checks passed
@PrzemyslawKlys PrzemyslawKlys deleted the fix/web-editlinks-apidocs-image-sizing-20260206 branch February 6, 2026 12:52
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 024df1bf89

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +796 to +800
if (plan.OriginalHeight > 0 && !ImgHeightAttrRegex.IsMatch(attrsUpdated))
{
attrsUpdated += $" height=\"{plan.OriginalHeight}\"";
hintedInFile++;
changed = true;

Choose a reason for hiding this comment

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

P1 Badge Inject intrinsic dimensions only when both are missing

This height injection runs independently from the width check, so an <img> that already has a custom width (for example width="300" or width="100%") will still get height forced to the source file's original pixel height, which can change the rendered aspect ratio and visibly distort images when EnhanceImageTags is enabled. The same issue happens in reverse when only height is authored, so width/height should be added as a pair (or computed proportionally) rather than independently.

Useful? React with 👍 / 👎.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR enhances the PowerForge web build system with improved edit links, API documentation features, and image optimization. It normalizes edit link paths to collapse . and .. segments for cleaner GitHub URLs, adds GitHub-aware "Edit on GitHub" action buttons to API type documentation pages, and extends image tag enhancement to inject intrinsic width and height attributes alongside existing loading and decoding hints.

Changes:

  • Normalize edit link paths to collapse relative path segments (. and ..) for clean GitHub URLs
  • Add API type-level source action button with GitHub-aware labeling ("Edit on GitHub" vs "View source")
  • Inject intrinsic width/height attributes on image tags when dimensions are known

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
PowerForge.Web/Services/WebAssetOptimizer.cs Add regex patterns and tracking for width/height attributes; inject dimensions into image tags when EnhanceImageTags is enabled
PowerForge.Web/Services/WebApiDocsGenerator.cs Add RenderTypeSourceAction and TryBuildGitHubEditUrl methods to generate GitHub edit links for API type source files
PowerForge.Web/Services/EditLinkResolver.cs Enhance NormalizePath to collapse . and .. segments using stack-based algorithm
PowerForge.Web/Assets/ApiDocs/fallback.css Add CSS for type-source-action buttons and custom checkbox styling for member toggles
PowerForge.Tests/WebSiteAuditOptimizeBuildTests.cs Add assertions to verify width and height attributes are injected in generated HTML
Docs/PowerForge.Web.Pipeline.md Update documentation to reflect that width/height are now injected by imageEnhanceTags

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +124 to +128
if (part == "..")
{
if (stack.Count > 0)
stack.RemoveAt(stack.Count - 1);
continue;
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

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

The path normalization logic handles ".." segments by removing the previous path component. However, if there are more ".." segments than path components (e.g., "../../file.txt" becomes empty), this could lead to unexpected behavior. Consider whether this is the intended behavior or if a warning/error should be raised when ".." segments would escape the root. For example, if the path is "../../../evil.txt", all ".." are silently ignored once the stack is empty, potentially masking configuration issues.

Copilot uses AI. Check for mistakes.
Comment on lines +120 to +131
foreach (var part in parts)
{
if (part == ".")
continue;
if (part == "..")
{
if (stack.Count > 0)
stack.RemoveAt(stack.Count - 1);
continue;
}
stack.Add(part);
}
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

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

This foreach loop implicitly filters its target sequence - consider filtering the sequence explicitly using '.Where(...)'.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants