Add OpenLiveWriterPost project and writer DLLs#470
Conversation
Add all.csv index and integrate an OpenLiveWriterPost helper into the v2 generator. Introduces a new OpenLiveWriterPost project (csproj + generator + WritePost helper) that saves generated HTML as OpenLiveWriter .wpost drafts, and invokes it from MultiGeneratorV2. Update Generator.sln and Generator.csproj to include the new project and switch the Generator to net8.0-windows with WindowsForms and x86 platform target. Add many prebuilt OpenLiveWriter-related DLLs and XML docs under v2/dlls/Writer, and update an example export (RSCG.xlsx).
📝 WalkthroughWalkthroughAdds a new OpenLiveWriterPost project and integrates it into the post-writing flow of MultiGeneratorV2; updates Generator project to net8.0-windows/x86 with WinForms and many supporting config, XML docs, and HTML/CSS assets for Open Live Writer and related dependencies. Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant Gen as MultiGeneratorV2
participant WP as WritePost
participant OLWGen as OpenLiveWriterPostGenerator
participant OLWLib as OpenLiveWriter Libraries
participant FS as FileSystem
User->>Gen: trigger post generation
Gen->>Gen: render markdown -> temp file
Gen->>Gen: launch Notepad
Gen->>WP: GeneratePostFromHtml(name, html)
WP->>WP: build BlogPost object
WP->>FS: determine drafts folder & target .wpost path
WP->>OLWGen: SavePost(post, filePath)
OLWGen->>OLWLib: initialize OLW environment
OLWGen->>OLWLib: create PostEditorFile & BlogPostEditingContext
OLWGen->>FS: PostEditorFile.SaveBlogPost()
OLWGen->>FS: detect/move created .wpost to filePath
OLWGen-->>WP: return
WP-->>Gen: return
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~35 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Pull request overview
This PR adds an OpenLiveWriter integration path to the v2 generator so generated HTML can also be saved as OpenLiveWriter .wpost drafts, alongside adding an all.csv index and vendoring required OpenLiveWriter/related binaries under v2/dlls/Writer.
Changes:
- Introduces a new
OpenLiveWriterPostproject (net8.0-windows/x86, WinForms enabled) to generate.wpostdrafts from HTML. - Invokes OpenLiveWriter draft generation from
MultiGeneratorV2.WrotePost. - Adds
all.csvand a large set of OpenLiveWriter-related DLLs/config/templates underv2/dlls/Writer, and updates the solution/project references accordingly.
Reviewed changes
Copilot reviewed 31 out of 103 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| v2/OpenLiveWriterPost/WritePost.cs | Creates a BlogPost from HTML and attempts to save it as a .wpost draft in the OLW drafts folder. |
| v2/OpenLiveWriterPost/OpenLiveWriterPostGenerator.cs | Initializes OpenLiveWriter environment and saves a post via PostEditorFile. |
| v2/OpenLiveWriterPost/OpenLiveWriterPost.csproj | New helper project targeting net8.0-windows x86 with references to vendored OpenLiveWriter assemblies. |
| v2/Generator/MultiGeneratorV2.cs | Calls the OpenLiveWriter draft generation during WrotePost. |
| v2/Generator/Generator.csproj | Switches generator to net8.0-windows, enables WinForms/x86, and references OpenLiveWriterPost. |
| v2/Generator.sln | Adds the OpenLiveWriterPost project to the solution (and updates VS header). |
| v2/dlls/Writer/template/defaultstyle.css | Adds OLW template CSS asset. |
| v2/dlls/Writer/template/default.htm | Adds OLW template HTML asset. |
| v2/dlls/Writer/System.Net.Http.Primitives.xml | Adds XML docs for a vendored dependency. |
| v2/dlls/Writer/System.Net.Http.Extensions.xml | Adds XML docs for a vendored dependency. |
| v2/dlls/Writer/PostBuild.CreateInstaller.dll | Adds vendored OpenLiveWriter-related binary. |
| v2/dlls/Writer/PlatformSpellCheck.xml | Adds XML docs for a vendored dependency. |
| v2/dlls/Writer/PlatformSpellCheck.dll | Adds vendored spellcheck binary. |
| v2/dlls/Writer/OpenLiveWriter.Tests.dll | Adds vendored OpenLiveWriter binary. |
| v2/dlls/Writer/OpenLiveWriter.PostEditor.dll.config | Adds runtime binding redirects/config for a vendored assembly. |
| v2/dlls/Writer/OpenLiveWriter.exe.manifest | Adds manifest for vendored OpenLiveWriter executable. |
| v2/dlls/Writer/OpenLiveWriter.exe.config | Adds runtime binding redirects/config for vendored OpenLiveWriter executable. |
| v2/dlls/Writer/OpenLiveWriter.BlogClient.dll.config | Adds runtime binding redirects/config for a vendored assembly. |
| v2/dlls/Writer/Microsoft.Threading.Tasks.Extensions.xml | Adds XML docs for a vendored dependency. |
| v2/dlls/Writer/MarketXmlGenerator.exe.config | Adds config for vendored tool. |
| v2/dlls/Writer/MarketXmlGenerator.exe | Adds vendored tool binary. |
| v2/dlls/Writer/locutil.exe.config | Adds config for vendored tool. |
| v2/dlls/Writer/html/map.html | Adds vendored HTML asset. |
| v2/dlls/Writer/html/map-preview.html | Adds vendored HTML asset. |
| v2/dlls/Writer/Google.Apis.PlatformServices.dll | Adds vendored dependency binary. |
| v2/dlls/Writer/Google.Apis.Auth.PlatformServices.dll | Adds vendored dependency binary. |
| v2/dlls/Writer/DeltaCompressionDotNet.PatchApi.dll | Adds vendored dependency binary. |
| v2/dlls/Writer/DeltaCompressionDotNet.MsDelta.dll | Adds vendored dependency binary. |
| v2/dlls/Writer/DeltaCompressionDotNet.dll | Adds vendored dependency binary. |
| v2/dlls/Writer/BlogRunnerReporter.exe.config | Adds config for vendored tool. |
| v2/dlls/Writer/BlogRunnerReporter.exe | Adds vendored tool binary. |
| v2/dlls/Writer/BlogRunnerGui.exe.config | Adds config for vendored tool. |
| v2/dlls/Writer/BlogRunner.exe.config | Adds config for vendored tool. |
| v2/dlls/Writer/BlogRunner.exe | Adds vendored tool binary. |
| v2/dlls/Writer/ApprovalUtilities.xml | Adds XML docs for a vendored dependency. |
| v2/dlls/Writer/ApprovalUtilities.Net45.xml | Adds XML docs for a vendored dependency. |
| v2/dlls/Writer/ApprovalUtilities.Net45.dll | Adds vendored dependency binary. |
| v2/dlls/Writer/ApprovalTests.xml | Adds XML docs for a vendored dependency. |
| all.csv | Adds a root-level CSV index (appears duplicated with v2/Generator/all.csv). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| await File.WriteAllTextAsync(file, output); | ||
| Process.Start("notepad.exe", file); | ||
| //Console.WriteLine(output); | ||
| OpenLiveWriterPost.WritePost.GeneratePostFromHtml(it.Generator.Name ?? "", output); |
| Nr,Key,Source,Category | ||
| 1,ThisAssembly, https://github.com/devlooped/ThisAssembly,EnhancementProject | ||
| 2,RSCG_TimeBombComment, https://github.com/ignatandrei/RSCG_TimeBombComment,EnhancementProject | ||
| 3,System.Text.Json, https://github.com/dotnet/runtime,Serializer | ||
| 4,RSCG_Utils, https://github.com/ignatandrei/RSCG_Utils,FilesToCode |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Actionable comments posted: 14
🧹 Nitpick comments (3)
v2/OpenLiveWriterPost/OpenLiveWriterPost.csproj (1)
13-25: Consider adding<Private>consistently to all OpenLiveWriter references.Only
OpenLiveWriter.Interophas<Private>true</Private>. For clarity and to ensure all dependencies are copied to the output, consider adding this element to the other OpenLiveWriter references as well, or document why only Interop needs it.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@v2/OpenLiveWriterPost/OpenLiveWriterPost.csproj` around lines 13 - 25, The project currently only sets <Private>true</Private> for the OpenLiveWriter.Interop Reference; update the other OpenLiveWriter references (OpenLiveWriter.CoreServices, OpenLiveWriter.Extensibility, OpenLiveWriter.PostEditor) to include <Private>true</Private> as well so those DLLs are copied to the build output (or alternatively add a brief comment/documentation in the .csproj explaining why only OpenLiveWriter.Interop is private if that is intentional). Ensure you modify the Reference elements named OpenLiveWriter.CoreServices, OpenLiveWriter.Extensibility, and OpenLiveWriter.PostEditor to include the <Private>true</Private> element for consistent behavior.v2/dlls/Writer/OpenLiveWriter.exe.config (1)
4-27: Inconsistent config structure: multiple<assemblyBinding>elements.Unlike other config files in this PR (e.g.,
OpenLiveWriter.PostEditor.dll.config,OpenLiveWriter.BlogClient.dll.config) which use a single<assemblyBinding>element containing all<dependentAssembly>children, this file uses separate<assemblyBinding>blocks for each redirect.Consider consolidating for consistency, though this is functionally equivalent.
Proposed consolidation
<runtime> - <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> - <dependentAssembly> - <assemblyIdentity name="DeltaCompressionDotNet.MsDelta" publicKeyToken="46b2138a390abf55" culture="neutral" /> - <bindingRedirect oldVersion="0.0.0.0-1.1.0.0" newVersion="1.1.0.0" /> - </dependentAssembly> - </assemblyBinding> - <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> - <dependentAssembly> - <assemblyIdentity name="Google.Apis.Core" publicKeyToken="4b01fa6e34db77ab" culture="neutral" /> - <bindingRedirect oldVersion="0.0.0.0-1.39.0.0" newVersion="1.39.0.0" /> - </dependentAssembly> - </assemblyBinding> - <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> - <dependentAssembly> - <assemblyIdentity name="Google.Apis" publicKeyToken="4b01fa6e34db77ab" culture="neutral" /> - <bindingRedirect oldVersion="0.0.0.0-1.39.0.0" newVersion="1.39.0.0" /> - </dependentAssembly> - </assemblyBinding> - <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> - <dependentAssembly> - <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" /> - <bindingRedirect oldVersion="0.0.0.0-10.0.0.0" newVersion="10.0.0.0" /> - </dependentAssembly> - </assemblyBinding> + <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> + <dependentAssembly> + <assemblyIdentity name="DeltaCompressionDotNet.MsDelta" publicKeyToken="46b2138a390abf55" culture="neutral" /> + <bindingRedirect oldVersion="0.0.0.0-1.1.0.0" newVersion="1.1.0.0" /> + </dependentAssembly> + <dependentAssembly> + <assemblyIdentity name="Google.Apis.Core" publicKeyToken="4b01fa6e34db77ab" culture="neutral" /> + <bindingRedirect oldVersion="0.0.0.0-1.39.0.0" newVersion="1.39.0.0" /> + </dependentAssembly> + <dependentAssembly> + <assemblyIdentity name="Google.Apis" publicKeyToken="4b01fa6e34db77ab" culture="neutral" /> + <bindingRedirect oldVersion="0.0.0.0-1.39.0.0" newVersion="1.39.0.0" /> + </dependentAssembly> + <dependentAssembly> + <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" /> + <bindingRedirect oldVersion="0.0.0.0-10.0.0.0" newVersion="10.0.0.0" /> + </dependentAssembly> + </assemblyBinding> </runtime>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@v2/dlls/Writer/OpenLiveWriter.exe.config` around lines 4 - 27, The config uses multiple <assemblyBinding> elements each with xmlns instead of one consolidated block; consolidate all <dependentAssembly> entries (those containing <assemblyIdentity name="DeltaCompressionDotNet.MsDelta" ...>, <assemblyIdentity name="Google.Apis.Core" ...>, <assemblyIdentity name="Google.Apis" ...>, <assemblyIdentity name="Newtonsoft.Json" ...>) into a single <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> element, preserving each <bindingRedirect oldVersion="..." newVersion="..."> and <assemblyIdentity> exactly as-is, and remove the duplicate <assemblyBinding> blocks so the XML remains valid and consistent with the other config files.v2/Generator/Generator.csproj (1)
5-8: Platform restriction: Generator now requires Windows x86.The switch to
net8.0-windowswithUseWindowsFormsandPlatformTarget=x86means the Generator can no longer run on non-Windows platforms or as a 64-bit process. This is required due to the OpenLiveWriterPost dependency's use of legacy OpenLiveWriter DLLs.Consider whether this constraint is acceptable for all build/CI environments where the Generator runs.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@v2/Generator/Generator.csproj` around lines 5 - 8, The project file now forces net8.0-windows, UseWindowsForms and PlatformTarget=x86 which restricts builds to 32-bit Windows; either make these properties conditional or split the project: remove unconditional <TargetFramework>, <UseWindowsForms> and <PlatformTarget> entries and instead add an MSBuild Condition that sets net8.0-windows/UseWindowsForms/PlatformTarget="x86" only when the OpenLiveWriterPost dependency is present (or when a build property like UseOpenLiveWriter is true), or create a separate Windows-specific csproj that references the OpenLiveWriterPost bits so non-Windows/64-bit CI can build the main Generator project without the Windows/x86 constraint; update package/ItemGroup references to be conditioned on that same flag.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@v2/dlls/Writer/html/map-preview.html`:
- Line 77: The code uses string-eval in setTimeout which allows script injection
via the scene variable; instead, stop building executable code and pass a real
function that calls initBirdseyeScene with the scene value (capture the scene
variable in a closure or use an arrow function) and keep the 200ms delay; update
the call site for setTimeout and remove the string concatenation so setTimeout
receives a function instead of a string.
- Line 103: The onchangeview handler is detached using the wrong function
(setMapLoaded) while it was attached as setBirdeyeMapLoaded; update the
DetachEvent call to remove the same handler that was attached by replacing the
second argument to map.DetachEvent("onchangeview", ...) with setBirdeyeMapLoaded
so the attached listener is actually removed (references: setBirdeyeMapLoaded,
setMapLoaded, map.DetachEvent).
In `@v2/dlls/Writer/html/map.html`:
- Around line 202-208: In processEvent, guard the lookup of
propertyChangeHandlers[e.name] before invoking it: verify that
propertyChangeHandlers[e.name] exists and is a function (e.g., !==
undefined/null and typeof === "function") and only call it when valid; if
missing, optionally log or ignore the unknown property event to avoid throwing
and aborting the batch. This change should be applied around the invocation in
the processEvent function where propertyChangeHandlers[e.name](e) is currently
called.
- Around line 184-199: In processEvents(), don't block or drop remaining events
when evtCount > 25: remove the alert() and return. Instead, when the loop
exceeds the safety threshold (evtCount variable inside processEvents calling
jsMapController.NextEvent() and processEvent(e)), log a non-blocking warning
(e.g., console.warn) and reschedule continuation of processing (e.g., call
setTimeout(processEvents, 0) or requestIdleCallback) so remaining events are
processed asynchronously rather than being discarded; keep the existing
processEvent(e) and NextEvent() usage but replace the alert/return branch with a
non-blocking reschedule and break out of the tight loop.
In `@v2/dlls/Writer/OpenLiveWriter.BlogClient.dll.config`:
- Around line 9-16: There are two identical dependentAssembly entries for the
assemblyIdentity name="System.Net.Http.Primitives" with the same
bindingRedirect; remove the duplicate dependentAssembly block so only one
bindingRedirect remains for System.Net.Http.Primitives (locate the duplicate
dependentAssembly elements containing assemblyIdentity
name="System.Net.Http.Primitives" and bindingRedirect
oldVersion="0.0.0.0-4.2.29.0" newVersion="4.2.29.0" and delete one).
In `@v2/dlls/Writer/OpenLiveWriter.PostEditor.dll.config`:
- Around line 13-16: Remove the duplicate bindingRedirect for the assembly
DeltaCompressionDotNet.MsDelta and normalize the publicKeyToken casing; locate
the two <dependentAssembly> entries that reference assemblyIdentity
name="DeltaCompressionDotNet.MsDelta" (one using
publicKeyToken="46B2138A390ABF55" and the other "46b2138a390abf55"), delete one
of them and keep the other, ensuring the retained bindingRedirect has the
correct newVersion="1.1.0.0" and a consistently cased publicKeyToken (choose
either all-upper or all-lower) so only a single, unambiguous redirect for
DeltaCompressionDotNet.MsDelta remains.
- Around line 9-20: The config contains duplicate dependentAssembly entries for
the assemblyIdentity name="System.Net.Http.Primitives"; remove the redundant
dependentAssembly block (the second occurrence with the same assemblyIdentity
and bindingRedirect) so only one bindingRedirect for System.Net.Http.Primitives
(oldVersion="0.0.0.0-4.2.29.0" newVersion="4.2.29.0") remains, leaving other
entries like DeltaCompressionDotNet.MsDelta untouched.
In `@v2/dlls/Writer/template/default.htm`:
- Line 5: Replace the malformed META tag's content attribute so it declares the
charset correctly: update the Content-Type meta element (the META tag in
v2/dlls/Writer/template/default.htm) to include the "charset=" prefix before the
encoding value (e.g., change content="text/html; utf-8" to content="text/html;
charset=utf-8") and ensure proper casing/format for the attribute value.
In `@v2/dlls/Writer/template/defaultstyle.css`:
- Line 25: The CSS template contains raw template tokens ({body-font} and
{title-font}) in defaultstyle.css that break stylelint's CSS parsing
(CssSyntaxError); fix by updating the stylelint configuration to treat this file
as template-only—either add an ignoreFiles entry for the template path or add an
overrides entry that matches defaultstyle.css and sets a template-aware
customSyntax (e.g., postcss-html/postcss-markup) so stylelint will parse
template tokens instead of failing on {body-font}/{title-font}.
In `@v2/Generator.sln`:
- Around line 38-41: The solution maps the OpenLiveWriterPost project (GUID
DF7E2A72-9D51-4AC9-982D-5EDC36B5DA26) to Any CPU while the project declares
<PlatformTarget>x86</PlatformTarget> and manifest processorArchitecture="X86",
causing a platform mismatch; fix by making the platforms consistent: either
change the solution configuration entry for that project (the lines referencing
{DF7E2A72-9D51-4AC9-982D-5EDC36B5DA26}.Debug/Release|Any CPU.*) to use x86, or
update the project properties to use PlatformTarget AnyCPU and adjust the
manifest processorArchitecture accordingly if the x86-specific dependencies
allow it, then re-run a clean build to verify no runtime or dependency issues
remain.
In `@v2/OpenLiveWriterPost/OpenLiveWriterPostGenerator.cs`:
- Line 24: PostEditorFile.CreateNew is being passed a new DirectoryInfo
constructed from Path.GetDirectoryName(filePath) which can be null/empty; guard
against that by validating filePath and the result of
Path.GetDirectoryName(filePath) before creating DirectoryInfo (e.g., throw
ArgumentException or choose a safe default), and use the validated directory
when calling PostEditorFile.CreateNew so DirectoryInfo is never constructed from
a null path.
- Around line 19-20: The call to ApplicationEnvironment.Initialize uses
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles),
@"\Windows Live\Writer\") which passes a rooted second segment and causes the
ProgramFiles prefix to be dropped; update the second argument to a relative path
(e.g. "Windows Live\\Writer" or "Windows Live/Writer") or build the path with
Path.Combine parts (e.g. Path.Combine(..., "Windows Live", "Writer")) so the
Program Files base path is preserved when calling
ApplicationEnvironment.Initialize(Assembly.GetExecutingAssembly(), ...).
In `@v2/OpenLiveWriterPost/WritePost.cs`:
- Around line 25-29: The code currently deletes the existing draft (File.Delete
on filePath1) before calling OpenLiveWriterPostGenerator.SavePost, risking data
loss if SavePost throws; instead, write the new content to a temporary file
(e.g., filePath1 + ".tmp") via OpenLiveWriterPostGenerator.SavePost(post1,
tempPath), verify the save succeeded, then atomically replace the original (use
File.Replace or move/rename with overwrite) from tempPath to filePath1 so the
original is preserved on failure; ensure exceptions from SavePost leave the
original file intact and clean up the temp file on success or failure.
- Around line 22-23: The code builds fileName1 using post1.Title directly which
may contain invalid filesystem characters; update the logic that constructs
fileName1 so it sanitizes post1.Title (e.g., remove or replace
Path.GetInvalidFileNameChars() and trim/normalize whitespace, and ensure a
non-empty fallback name) before composing $"{sanitizedTitle}.wpost" and passing
it to Path.Combine with draftsFolder1; locate the fileName1 assignment and
replace it with the sanitized-title construction while keeping the rest
(Path.Combine(draftsFolder1, fileName1)) unchanged.
---
Nitpick comments:
In `@v2/dlls/Writer/OpenLiveWriter.exe.config`:
- Around line 4-27: The config uses multiple <assemblyBinding> elements each
with xmlns instead of one consolidated block; consolidate all
<dependentAssembly> entries (those containing <assemblyIdentity
name="DeltaCompressionDotNet.MsDelta" ...>, <assemblyIdentity
name="Google.Apis.Core" ...>, <assemblyIdentity name="Google.Apis" ...>,
<assemblyIdentity name="Newtonsoft.Json" ...>) into a single <assemblyBinding
xmlns="urn:schemas-microsoft-com:asm.v1"> element, preserving each
<bindingRedirect oldVersion="..." newVersion="..."> and <assemblyIdentity>
exactly as-is, and remove the duplicate <assemblyBinding> blocks so the XML
remains valid and consistent with the other config files.
In `@v2/Generator/Generator.csproj`:
- Around line 5-8: The project file now forces net8.0-windows, UseWindowsForms
and PlatformTarget=x86 which restricts builds to 32-bit Windows; either make
these properties conditional or split the project: remove unconditional
<TargetFramework>, <UseWindowsForms> and <PlatformTarget> entries and instead
add an MSBuild Condition that sets
net8.0-windows/UseWindowsForms/PlatformTarget="x86" only when the
OpenLiveWriterPost dependency is present (or when a build property like
UseOpenLiveWriter is true), or create a separate Windows-specific csproj that
references the OpenLiveWriterPost bits so non-Windows/64-bit CI can build the
main Generator project without the Windows/x86 constraint; update
package/ItemGroup references to be conditioned on that same flag.
In `@v2/OpenLiveWriterPost/OpenLiveWriterPost.csproj`:
- Around line 13-25: The project currently only sets <Private>true</Private> for
the OpenLiveWriter.Interop Reference; update the other OpenLiveWriter references
(OpenLiveWriter.CoreServices, OpenLiveWriter.Extensibility,
OpenLiveWriter.PostEditor) to include <Private>true</Private> as well so those
DLLs are copied to the build output (or alternatively add a brief
comment/documentation in the .csproj explaining why only OpenLiveWriter.Interop
is private if that is intentional). Ensure you modify the Reference elements
named OpenLiveWriter.CoreServices, OpenLiveWriter.Extensibility, and
OpenLiveWriter.PostEditor to include the <Private>true</Private> element for
consistent behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 14feaa0c-4f25-455a-ad79-b5a0a32ed02f
⛔ Files ignored due to path filters (65)
all.csvis excluded by!**/*.csvv2/dlls/Writer/ApprovalTests.dllis excluded by!**/*.dllv2/dlls/Writer/ApprovalUtilities.Net45.dllis excluded by!**/*.dllv2/dlls/Writer/ApprovalUtilities.dllis excluded by!**/*.dllv2/dlls/Writer/BlogRunner.Core.dllis excluded by!**/*.dllv2/dlls/Writer/BlogRunner.exeis excluded by!**/*.exev2/dlls/Writer/BlogRunnerGui.exeis excluded by!**/*.exev2/dlls/Writer/BlogRunnerReporter.exeis excluded by!**/*.exev2/dlls/Writer/DeltaCompressionDotNet.MsDelta.dllis excluded by!**/*.dllv2/dlls/Writer/DeltaCompressionDotNet.PatchApi.dllis excluded by!**/*.dllv2/dlls/Writer/DeltaCompressionDotNet.dllis excluded by!**/*.dllv2/dlls/Writer/Google.Apis.Auth.PlatformServices.dllis excluded by!**/*.dllv2/dlls/Writer/Google.Apis.Auth.dllis excluded by!**/*.dllv2/dlls/Writer/Google.Apis.Blogger.v3.dllis excluded by!**/*.dllv2/dlls/Writer/Google.Apis.Core.dllis excluded by!**/*.dllv2/dlls/Writer/Google.Apis.Drive.v3.dllis excluded by!**/*.dllv2/dlls/Writer/Google.Apis.PlatformServices.dllis excluded by!**/*.dllv2/dlls/Writer/Google.Apis.dllis excluded by!**/*.dllv2/dlls/Writer/ICSharpCode.SharpZipLib.dllis excluded by!**/*.dllv2/dlls/Writer/MarketXmlGenerator.exeis excluded by!**/*.exev2/dlls/Writer/Microsoft.Threading.Tasks.Extensions.Desktop.dllis excluded by!**/*.dllv2/dlls/Writer/Microsoft.Threading.Tasks.Extensions.dllis excluded by!**/*.dllv2/dlls/Writer/Microsoft.Threading.Tasks.dllis excluded by!**/*.dllv2/dlls/Writer/Microsoft.VisualStudio.QualityTools.UnitTestFramework.dllis excluded by!**/*.dllv2/dlls/Writer/Mono.Cecil.Mdb.dllis excluded by!**/*.dllv2/dlls/Writer/Mono.Cecil.Pdb.dllis excluded by!**/*.dllv2/dlls/Writer/Mono.Cecil.Rocks.dllis excluded by!**/*.dllv2/dlls/Writer/Mono.Cecil.dllis excluded by!**/*.dllv2/dlls/Writer/NUnit3.TestAdapter.dllis excluded by!**/*.dllv2/dlls/Writer/Newtonsoft.Json.dllis excluded by!**/*.dllv2/dlls/Writer/NuGet.Squirrel.dllis excluded by!**/*.dllv2/dlls/Writer/OpenLiveWriter.Api.dllis excluded by!**/*.dllv2/dlls/Writer/OpenLiveWriter.ApplicationFramework.dllis excluded by!**/*.dllv2/dlls/Writer/OpenLiveWriter.BlogClient.dllis excluded by!**/*.dllv2/dlls/Writer/OpenLiveWriter.BrowserControl.dllis excluded by!**/*.dllv2/dlls/Writer/OpenLiveWriter.Controls.dllis excluded by!**/*.dllv2/dlls/Writer/OpenLiveWriter.CoreServices.dllis excluded by!**/*.dllv2/dlls/Writer/OpenLiveWriter.Extensibility.dllis excluded by!**/*.dllv2/dlls/Writer/OpenLiveWriter.FileDestinations.dllis excluded by!**/*.dllv2/dlls/Writer/OpenLiveWriter.HtmlEditor.dllis excluded by!**/*.dllv2/dlls/Writer/OpenLiveWriter.HtmlParser.dllis excluded by!**/*.dllv2/dlls/Writer/OpenLiveWriter.InternalWriterPlugin.dllis excluded by!**/*.dllv2/dlls/Writer/OpenLiveWriter.Interop.Mshtml.dllis excluded by!**/*.dllv2/dlls/Writer/OpenLiveWriter.Interop.SHDocVw.dllis excluded by!**/*.dllv2/dlls/Writer/OpenLiveWriter.Interop.dllis excluded by!**/*.dllv2/dlls/Writer/OpenLiveWriter.Localization.dllis excluded by!**/*.dllv2/dlls/Writer/OpenLiveWriter.Mshtml.dllis excluded by!**/*.dllv2/dlls/Writer/OpenLiveWriter.PostEditor.dllis excluded by!**/*.dllv2/dlls/Writer/OpenLiveWriter.SpellChecker.dllis excluded by!**/*.dllv2/dlls/Writer/OpenLiveWriter.Tests.dllis excluded by!**/*.dllv2/dlls/Writer/OpenLiveWriter.UnitTest.dllis excluded by!**/*.dllv2/dlls/Writer/OpenLiveWriter.exeis excluded by!**/*.exev2/dlls/Writer/PlatformSpellCheck.dllis excluded by!**/*.dllv2/dlls/Writer/PostBuild.CreateInstaller.dllis excluded by!**/*.dllv2/dlls/Writer/Splat.dllis excluded by!**/*.dllv2/dlls/Writer/Squirrel.dllis excluded by!**/*.dllv2/dlls/Writer/System.Net.Http.Extensions.dllis excluded by!**/*.dllv2/dlls/Writer/System.Net.Http.Primitives.dllis excluded by!**/*.dllv2/dlls/Writer/YamlDotNet.dllis excluded by!**/*.dllv2/dlls/Writer/Zlib.Portable.dllis excluded by!**/*.dllv2/dlls/Writer/locutil.exeis excluded by!**/*.exev2/dlls/Writer/nunit.engine.api.dllis excluded by!**/*.dllv2/dlls/Writer/nunit.engine.dllis excluded by!**/*.dllv2/dlls/Writer/nunit.framework.dllis excluded by!**/*.dllv2/rscg_examples_site/static/exports/RSCG.xlsxis excluded by!**/*.xlsx
📒 Files selected for processing (38)
v2/Generator.slnv2/Generator/Generator.csprojv2/Generator/MultiGeneratorV2.csv2/OpenLiveWriterPost/OpenLiveWriterPost.csprojv2/OpenLiveWriterPost/OpenLiveWriterPostGenerator.csv2/OpenLiveWriterPost/WritePost.csv2/dlls/Writer/ApprovalTests.xmlv2/dlls/Writer/ApprovalUtilities.Net45.xmlv2/dlls/Writer/ApprovalUtilities.xmlv2/dlls/Writer/BlogRunner.exe.configv2/dlls/Writer/BlogRunnerGui.exe.configv2/dlls/Writer/BlogRunnerReporter.exe.configv2/dlls/Writer/Google.Apis.Auth.xmlv2/dlls/Writer/Google.Apis.Blogger.v3.xmlv2/dlls/Writer/Google.Apis.Core.xmlv2/dlls/Writer/Google.Apis.Drive.v3.xmlv2/dlls/Writer/Google.Apis.xmlv2/dlls/Writer/ICSharpCode.SharpZipLib.xmlv2/dlls/Writer/MarketXmlGenerator.exe.configv2/dlls/Writer/Microsoft.Threading.Tasks.Extensions.Desktop.xmlv2/dlls/Writer/Microsoft.Threading.Tasks.Extensions.xmlv2/dlls/Writer/Microsoft.Threading.Tasks.xmlv2/dlls/Writer/Newtonsoft.Json.xmlv2/dlls/Writer/OpenLiveWriter.BlogClient.dll.configv2/dlls/Writer/OpenLiveWriter.PostEditor.dll.configv2/dlls/Writer/OpenLiveWriter.exe.configv2/dlls/Writer/OpenLiveWriter.exe.manifestv2/dlls/Writer/PlatformSpellCheck.xmlv2/dlls/Writer/System.Net.Http.Extensions.xmlv2/dlls/Writer/System.Net.Http.Primitives.xmlv2/dlls/Writer/YamlDotNet.xmlv2/dlls/Writer/Zlib.Portable.xmlv2/dlls/Writer/html/map-preview.htmlv2/dlls/Writer/html/map.htmlv2/dlls/Writer/locutil.exe.configv2/dlls/Writer/nunit.framework.xmlv2/dlls/Writer/template/default.htmv2/dlls/Writer/template/defaultstyle.css
| { | ||
| map.LoadMap(); | ||
| mapElement.runtimeStyle.display = "none"; | ||
| setTimeout("initBirdseyeScene('" + scene + "')", 200); |
There was a problem hiding this comment.
Remove string-eval timeout to prevent query-parameter script injection.
Line 77 builds executable code from scene and passes it to setTimeout. Use a function callback instead.
Suggested fix
- setTimeout("initBirdseyeScene('" + scene + "')", 200);
+ setTimeout(function () { initBirdseyeScene(scene); }, 200);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| setTimeout("initBirdseyeScene('" + scene + "')", 200); | |
| setTimeout(function () { initBirdseyeScene(scene); }, 200); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@v2/dlls/Writer/html/map-preview.html` at line 77, The code uses string-eval
in setTimeout which allows script injection via the scene variable; instead,
stop building executable code and pass a real function that calls
initBirdseyeScene with the scene value (capture the scene variable in a closure
or use an arrow function) and keep the 200ms delay; update the call site for
setTimeout and remove the string concatenation so setTimeout receives a function
instead of a string.
| } | ||
|
|
||
| function setBirdeyeMapLoaded(){ | ||
| map.DetachEvent("onchangeview", setMapLoaded); |
There was a problem hiding this comment.
Detach the same handler that was attached for onchangeview.
Line 103 detaches setMapLoaded, but Line 97 attaches setBirdeyeMapLoaded. The current detach is ineffective and leaves the handler active.
Suggested fix
- map.DetachEvent("onchangeview", setMapLoaded);
+ map.DetachEvent("onchangeview", setBirdeyeMapLoaded);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@v2/dlls/Writer/html/map-preview.html` at line 103, The onchangeview handler
is detached using the wrong function (setMapLoaded) while it was attached as
setBirdeyeMapLoaded; update the DetachEvent call to remove the same handler that
was attached by replacing the second argument to map.DetachEvent("onchangeview",
...) with setBirdeyeMapLoaded so the attached listener is actually removed
(references: setBirdeyeMapLoaded, setMapLoaded, map.DetachEvent).
| function processEvents() | ||
| { | ||
| var evtCount = 0; | ||
| var e = jsMapController.NextEvent(); | ||
| while(e != null) | ||
| { | ||
| evtCount++; | ||
| if(evtCount > 25) | ||
| { | ||
| alert("Bug alert: recursive event loop: " + e); | ||
| return; | ||
| } | ||
|
|
||
| processEvent(e); | ||
| e = jsMapController.NextEvent(); | ||
| } |
There was a problem hiding this comment.
Don't convert normal event bursts into a blocking alert() and dropped updates.
Any queue longer than 25 events exits early and leaves the rest unprocessed. A larger pushpin batch or property burst will stall synchronization and surface a modal dialog in the editor.
Proposed fix
function processEvents()
{
- var evtCount = 0;
- var e = jsMapController.NextEvent();
- while(e != null)
- {
- evtCount++;
- if(evtCount > 25)
- {
- alert("Bug alert: recursive event loop: " + e);
- return;
- }
-
- processEvent(e);
- e = jsMapController.NextEvent();
- }
+ var evtCount = 0;
+ var maxEventsPerTick = 25;
+ var e = jsMapController.NextEvent();
+ while(e != null && evtCount < maxEventsPerTick)
+ {
+ processEvent(e);
+ evtCount++;
+ e = jsMapController.NextEvent();
+ }
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@v2/dlls/Writer/html/map.html` around lines 184 - 199, In processEvents(),
don't block or drop remaining events when evtCount > 25: remove the alert() and
return. Instead, when the loop exceeds the safety threshold (evtCount variable
inside processEvents calling jsMapController.NextEvent() and processEvent(e)),
log a non-blocking warning (e.g., console.warn) and reschedule continuation of
processing (e.g., call setTimeout(processEvents, 0) or requestIdleCallback) so
remaining events are processed asynchronously rather than being discarded; keep
the existing processEvent(e) and NextEvent() usage but replace the alert/return
branch with a non-blocking reschedule and break out of the tight loop.
| function processEvent(e) | ||
| { | ||
| if(e.type == 1) //propertyChanged | ||
| { | ||
| //alert("propertyChanged: " + e.name + " " + e.value); | ||
| //invoke the handler for the specified property | ||
| propertyChangeHandlers[e.name](e); |
There was a problem hiding this comment.
Guard unknown property names before invoking a handler.
propertyChangeHandlers[e.name] can be undefined. One unexpected host event will throw and abort the current batch.
Proposed fix
if(e.type == 1) //propertyChanged
{
//alert("propertyChanged: " + e.name + " " + e.value);
//invoke the handler for the specified property
- propertyChangeHandlers[e.name](e);
+ var handler = propertyChangeHandlers[e.name];
+ if(handler != null)
+ handler(e);
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@v2/dlls/Writer/html/map.html` around lines 202 - 208, In processEvent, guard
the lookup of propertyChangeHandlers[e.name] before invoking it: verify that
propertyChangeHandlers[e.name] exists and is a function (e.g., !==
undefined/null and typeof === "function") and only call it when valid; if
missing, optionally log or ignore the unknown property event to avoid throwing
and aborting the batch. This change should be applied around the invocation in
the processEvent function where propertyChangeHandlers[e.name](e) is currently
called.
| <dependentAssembly> | ||
| <assemblyIdentity name="System.Net.Http.Primitives" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> | ||
| <bindingRedirect oldVersion="0.0.0.0-4.2.29.0" newVersion="4.2.29.0" /> | ||
| </dependentAssembly> | ||
| <dependentAssembly> | ||
| <assemblyIdentity name="System.Net.Http.Primitives" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> | ||
| <bindingRedirect oldVersion="0.0.0.0-4.2.29.0" newVersion="4.2.29.0" /> | ||
| </dependentAssembly> |
There was a problem hiding this comment.
Duplicate binding redirect for System.Net.Http.Primitives.
Lines 9-12 and 13-16 contain identical binding redirects for System.Net.Http.Primitives. Remove the duplicate entry.
Proposed fix - remove duplicate
<dependentAssembly>
<assemblyIdentity name="System.Net.Http.Primitives" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.29.0" newVersion="4.2.29.0" />
</dependentAssembly>
- <dependentAssembly>
- <assemblyIdentity name="System.Net.Http.Primitives" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
- <bindingRedirect oldVersion="0.0.0.0-4.2.29.0" newVersion="4.2.29.0" />
- </dependentAssembly>
<dependentAssembly>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <dependentAssembly> | |
| <assemblyIdentity name="System.Net.Http.Primitives" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> | |
| <bindingRedirect oldVersion="0.0.0.0-4.2.29.0" newVersion="4.2.29.0" /> | |
| </dependentAssembly> | |
| <dependentAssembly> | |
| <assemblyIdentity name="System.Net.Http.Primitives" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> | |
| <bindingRedirect oldVersion="0.0.0.0-4.2.29.0" newVersion="4.2.29.0" /> | |
| </dependentAssembly> | |
| <dependentAssembly> | |
| <assemblyIdentity name="System.Net.Http.Primitives" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> | |
| <bindingRedirect oldVersion="0.0.0.0-4.2.29.0" newVersion="4.2.29.0" /> | |
| </dependentAssembly> |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@v2/dlls/Writer/OpenLiveWriter.BlogClient.dll.config` around lines 9 - 16,
There are two identical dependentAssembly entries for the assemblyIdentity
name="System.Net.Http.Primitives" with the same bindingRedirect; remove the
duplicate dependentAssembly block so only one bindingRedirect remains for
System.Net.Http.Primitives (locate the duplicate dependentAssembly elements
containing assemblyIdentity name="System.Net.Http.Primitives" and
bindingRedirect oldVersion="0.0.0.0-4.2.29.0" newVersion="4.2.29.0" and delete
one).
| {DF7E2A72-9D51-4AC9-982D-5EDC36B5DA26}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
| {DF7E2A72-9D51-4AC9-982D-5EDC36B5DA26}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
| {DF7E2A72-9D51-4AC9-982D-5EDC36B5DA26}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||
| {DF7E2A72-9D51-4AC9-982D-5EDC36B5DA26}.Release|Any CPU.Build.0 = Release|Any CPU |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Check platform configuration across solution and project files
echo "=== Solution platform configurations for OpenLiveWriterPost ==="
rg "DF7E2A72-9D51-4AC9-982D-5EDC36B5DA26" v2/Generator.sln -A 1
echo -e "\n=== Project PlatformTarget setting ==="
rg "PlatformTarget" v2/OpenLiveWriterPost/OpenLiveWriterPost.csproj
echo -e "\n=== Manifest processorArchitecture ==="
rg "processorArchitecture" v2/dlls/Writer/OpenLiveWriter.exe.manifestRepository: ignatandrei/RSCG_Examples
Length of output: 862
Platform configuration mismatch between solution and project files.
The solution (v2/Generator.sln) maps the OpenLiveWriterPost project to Any CPU configuration, while the project file specifies <PlatformTarget>x86</PlatformTarget> and the manifest declares processorArchitecture="X86". This mismatch will cause build inconsistencies or runtime issues, particularly with the x86-specific OpenLiveWriter dependencies.
Resolve by either:
- Changing the solution platform configuration to
x86, or - Updating the project's
PlatformTargettoAnyCPU(if compatible with x86-specific dependencies)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@v2/Generator.sln` around lines 38 - 41, The solution maps the
OpenLiveWriterPost project (GUID DF7E2A72-9D51-4AC9-982D-5EDC36B5DA26) to Any
CPU while the project declares <PlatformTarget>x86</PlatformTarget> and manifest
processorArchitecture="X86", causing a platform mismatch; fix by making the
platforms consistent: either change the solution configuration entry for that
project (the lines referencing
{DF7E2A72-9D51-4AC9-982D-5EDC36B5DA26}.Debug/Release|Any CPU.*) to use x86, or
update the project properties to use PlatformTarget AnyCPU and adjust the
manifest processorArchitecture accordingly if the x86-specific dependencies
allow it, then re-run a clean build to verify no runtime or dependency issues
remain.
| if(File.Exists(filePath1)) File.Delete(filePath1); | ||
|
|
||
| Console.WriteLine("Creating hardcoded blog post..."); | ||
|
|
||
| OpenLiveWriterPostGenerator.SavePost(post1, filePath1); |
There was a problem hiding this comment.
Avoid deleting the existing draft before confirming save succeeds.
Line 25 deletes the existing .wpost first. If Line 29 fails, the old draft is lost.
Suggested fix
- if(File.Exists(filePath1)) File.Delete(filePath1);
-
- Console.WriteLine("Creating hardcoded blog post...");
-
- OpenLiveWriterPostGenerator.SavePost(post1, filePath1);
+ var backupPath = filePath1 + ".bak";
+ if (File.Exists(filePath1))
+ File.Copy(filePath1, backupPath, overwrite: true);
+
+ Console.WriteLine("Creating hardcoded blog post...");
+
+ try
+ {
+ OpenLiveWriterPostGenerator.SavePost(post1, filePath1);
+ if (File.Exists(backupPath)) File.Delete(backupPath);
+ }
+ catch
+ {
+ if (File.Exists(backupPath))
+ File.Move(backupPath, filePath1, overwrite: true);
+ throw;
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if(File.Exists(filePath1)) File.Delete(filePath1); | |
| Console.WriteLine("Creating hardcoded blog post..."); | |
| OpenLiveWriterPostGenerator.SavePost(post1, filePath1); | |
| var backupPath = filePath1 + ".bak"; | |
| if (File.Exists(filePath1)) | |
| File.Copy(filePath1, backupPath, overwrite: true); | |
| Console.WriteLine("Creating hardcoded blog post..."); | |
| try | |
| { | |
| OpenLiveWriterPostGenerator.SavePost(post1, filePath1); | |
| if (File.Exists(backupPath)) File.Delete(backupPath); | |
| } | |
| catch | |
| { | |
| if (File.Exists(backupPath)) | |
| File.Move(backupPath, filePath1, overwrite: true); | |
| throw; | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@v2/OpenLiveWriterPost/WritePost.cs` around lines 25 - 29, The code currently
deletes the existing draft (File.Delete on filePath1) before calling
OpenLiveWriterPostGenerator.SavePost, risking data loss if SavePost throws;
instead, write the new content to a temporary file (e.g., filePath1 + ".tmp")
via OpenLiveWriterPostGenerator.SavePost(post1, tempPath), verify the save
succeeded, then atomically replace the original (use File.Replace or move/rename
with overwrite) from tempPath to filePath1 so the original is preserved on
failure; ensure exceptions from SavePost leave the original file intact and
clean up the temp file on success or failure.
There was a problem hiding this comment.
🧹 Nitpick comments (3)
v2/OpenLiveWriterPost/OpenLiveWriterPostGenerator.cs (3)
12-15: Consider adding parameter validation.The public entry point does not validate
postorfilePath. While the current caller (WritePost.GeneratePostFromHtml) provides valid values, adding null checks would make this method more robust for future callers.💡 Suggested validation
public static void SavePost(BlogPost post, string filePath) { + ArgumentNullException.ThrowIfNull(post); + ArgumentException.ThrowIfNullOrWhiteSpace(filePath); + SavePostAsStructuredStorage(post, filePath); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@v2/OpenLiveWriterPost/OpenLiveWriterPostGenerator.cs` around lines 12 - 15, SavePost currently forwards to SavePostAsStructuredStorage without validating inputs; add guards at the start of the public SavePost(BlogPost post, string filePath) to validate that post is not null (throw ArgumentNullException(nameof(post))) and that filePath is not null or empty/whitespace (throw ArgumentException or ArgumentNullException for nameof(filePath))); after validation call SavePostAsStructuredStorage(post, filePath) as before. This ensures the public entrypoint is robust for future callers.
49-72: Consider simplifying with LINQ and HashSet.The nested loop has O(n×m) complexity. While acceptable for typical draft counts, a HashSet lookup would be cleaner and O(n).
💡 Simplified implementation
private static string FindCreatedDraftPath(string directoryPath, string[] existingDrafts) { string[] currentDrafts = Directory.GetFiles(directoryPath, "*.wpost"); + var existingSet = new HashSet<string>(existingDrafts, StringComparer.OrdinalIgnoreCase); - foreach (string currentDraft in currentDrafts) - { - bool existedBefore = false; - foreach (string existingDraft in existingDrafts) - { - if (string.Equals(currentDraft, existingDraft, StringComparison.OrdinalIgnoreCase)) - { - existedBefore = true; - break; - } - } - - if (!existedBefore) - { - return currentDraft; - } - } + var newDraft = currentDrafts.FirstOrDefault(d => !existingSet.Contains(d)); + if (newDraft != null) + return newDraft; throw new IOException($"Unable to determine the draft file created in '{directoryPath}'."); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@v2/OpenLiveWriterPost/OpenLiveWriterPostGenerator.cs` around lines 49 - 72, The nested loops in FindCreatedDraftPath produce O(n×m) comparisons and are verbose; replace the manual existence check by constructing a HashSet<string> (using OrdinalIgnoreCase) from existingDrafts and then iterate currentDrafts to return the first draft not contained in that set (currentDrafts = Directory.GetFiles(...)). This simplifies logic, improves lookup to O(1) per item, and preserves the existing exception behavior when no new draft is found.
30-46: Minor race window in draft detection.The snapshot-compare approach (lines 31, 37) to identify the created draft has a small race window where another process could create a
.wpostfile between the snapshot and the post-save scan, potentially causing incorrect file identification. For single-user desktop tooling this is likely acceptable, but worth noting.Additionally, the delete-then-move pattern (lines 40-45) has a TOCTOU window. Consider using
File.Movewith overwrite if targeting .NET 5+.💡 Simplify with File.Move overwrite (if .NET 5+)
- if (!string.Equals(createdDraftPath, filePath, StringComparison.OrdinalIgnoreCase)) - { - if (File.Exists(filePath)) - { - File.Delete(filePath); - } - - File.Move(createdDraftPath, filePath); - } + if (!string.Equals(createdDraftPath, filePath, StringComparison.OrdinalIgnoreCase)) + { + File.Move(createdDraftPath, filePath, overwrite: true); + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@v2/OpenLiveWriterPost/OpenLiveWriterPostGenerator.cs` around lines 30 - 46, The draft-detection uses a pre-save snapshot + post-save scan (DirectoryInfo/FindCreatedDraftPath after PostEditorFile.CreateNew/pef.SaveBlogPost) which has a race window, and the current delete-then-move pattern (File.Delete then File.Move) has a TOCTOU; fix by either (A) making PostEditorFile.CreateNew or pef.SaveBlogPost return the exact created file path (preferred) so you can use that directly, or (B) if changing those signatures is undesirable, create a unique marker/target name in the target directory before saving (so you can reliably identify the new file) or use a FileSystemWatcher to detect the created .wpost; then replace the delete-then-move with the atomic overwrite-capable API: File.Move(createdDraftPath, filePath, overwrite: true) (requires .NET 5+), referencing FindCreatedDraftPath, PostEditorFile.CreateNew, pef.SaveBlogPost, createdDraftPath and filePath.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@v2/OpenLiveWriterPost/OpenLiveWriterPostGenerator.cs`:
- Around line 12-15: SavePost currently forwards to SavePostAsStructuredStorage
without validating inputs; add guards at the start of the public
SavePost(BlogPost post, string filePath) to validate that post is not null
(throw ArgumentNullException(nameof(post))) and that filePath is not null or
empty/whitespace (throw ArgumentException or ArgumentNullException for
nameof(filePath))); after validation call SavePostAsStructuredStorage(post,
filePath) as before. This ensures the public entrypoint is robust for future
callers.
- Around line 49-72: The nested loops in FindCreatedDraftPath produce O(n×m)
comparisons and are verbose; replace the manual existence check by constructing
a HashSet<string> (using OrdinalIgnoreCase) from existingDrafts and then iterate
currentDrafts to return the first draft not contained in that set (currentDrafts
= Directory.GetFiles(...)). This simplifies logic, improves lookup to O(1) per
item, and preserves the existing exception behavior when no new draft is found.
- Around line 30-46: The draft-detection uses a pre-save snapshot + post-save
scan (DirectoryInfo/FindCreatedDraftPath after
PostEditorFile.CreateNew/pef.SaveBlogPost) which has a race window, and the
current delete-then-move pattern (File.Delete then File.Move) has a TOCTOU; fix
by either (A) making PostEditorFile.CreateNew or pef.SaveBlogPost return the
exact created file path (preferred) so you can use that directly, or (B) if
changing those signatures is undesirable, create a unique marker/target name in
the target directory before saving (so you can reliably identify the new file)
or use a FileSystemWatcher to detect the created .wpost; then replace the
delete-then-move with the atomic overwrite-capable API:
File.Move(createdDraftPath, filePath, overwrite: true) (requires .NET 5+),
referencing FindCreatedDraftPath, PostEditorFile.CreateNew, pef.SaveBlogPost,
createdDraftPath and filePath.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: b27e64d9-bdef-458e-a51e-14834d9c4a66
📒 Files selected for processing (3)
v2/Generator.slnv2/OpenLiveWriterPost/OpenLiveWriterPostGenerator.csv2/OpenLiveWriterPost/WritePost.cs
✅ Files skipped from review due to trivial changes (1)
- v2/Generator.sln
🚧 Files skipped from review as they are similar to previous changes (1)
- v2/OpenLiveWriterPost/WritePost.cs
Add all.csv index and integrate an OpenLiveWriterPost helper into the v2 generator. Introduces a new OpenLiveWriterPost project (csproj + generator + WritePost helper) that saves generated HTML as OpenLiveWriter .wpost drafts, and invokes it from MultiGeneratorV2. Update Generator.sln and Generator.csproj to include the new project and switch the Generator to net8.0-windows with WindowsForms and x86 platform target. Add many prebuilt OpenLiveWriter-related DLLs and XML docs under v2/dlls/Writer, and update an example export (RSCG.xlsx).
Summary by CodeRabbit
New Features
Documentation