Port ImageEditor: Local ShareX -> XerahS
Use this workflow whenever XerahS needs to catch up with the current ShareX.ImageEditor
state from the local ShareX repo.
Source of truth
On the known workstations, do not clone ShareX again. The local ShareX checkout is the upstream reference.
ShareX and XerahS are not always siblings. Resolve $ShareXRepo and $XerahSRoot
independently. The editor cwd is often C:\WINDOWS\system32 or another unrelated folder —
never run git there.
Session paths (resolve first)
User-specified paths win. Then probe, in order, until git rev-parse --is-inside-work-tree
succeeds.
$ShareXRepo (the ShareX git root, the directory that contains ShareX.ImageEditor/):
- Path the user named (file or folder; if they named
...\ShareX Team\ShareX, use that) C:\Users\Public\source\repos\ShareX Team\ShareXC:\Users\liveu\source\repos\ShareX Team\ShareX/Users/mike/Projects/ShareX Team/ShareX
$XerahSRoot (the XerahS git root, the directory that contains the ShareX.ImageEditor
submodule):
- Path the user named
git rev-parse --show-toplevelfrom any path under a XerahS checkoutC:\Users\Public\source\repos\KovaForge\XerahS(KovaForge workstation)C:\Users\Public\source\repos\ShareX Team\XerahS(ShareX Team workstation)C:\Users\liveu\source\repos\ShareX Team\XerahS/Users/mike/Projects/ShareX Team/XerahS
Both Public checkouts are first-class. Prefer the one the user named, otherwise the one that exists and is a git work tree. Do not assume XerahS is a sibling of ShareX.
Cloud/CI fallback (no local ShareX checkout)
When the skill runs on a host without any of the ShareX layouts above, the "do not clone" rule does not apply. Instead:
- Clone upstream once, blobless for speed:
git clone --filter=blob:none https://github.com/ShareX/ShareX.git /tmp/ShareX - Use
/tmp/ShareXas$ShareXRepo. - Use the current XerahS working tree as
$XerahSRoot, and rungit submodule update --init ShareX.ImageEditorfirst if the submodule directory is empty. - Create a base worktree for divergence triage (see step 2e-triage):
git -C "$ShareXRepo" worktree add <sharex-base> <last_synced_sharex_hash>Use/tmp/sharex-baseon Linux/macOS, or a sibling of$ShareXReposuch as...\ShareX Team\sharex-baseon Windows ($env:TEMPis fine if the disk is local).
Linux hosts build the ImageEditor project, the desktop solution, and the test suite fine
(EnableWindowsTargeting is already configured), so all verification gates below apply
unchanged.
Path map
| Role | Path |
|---|---|
| Upstream ShareX repo | $ShareXRepo |
| Upstream source tree | $ShareXRepo/ShareX.ImageEditor |
| XerahS root | $XerahSRoot |
| XerahS ImageEditor repo | $XerahSRoot/ShareX.ImageEditor |
| XerahS ImageEditor code root | $XerahSRoot/ShareX.ImageEditor/src/ShareX.ImageEditor |
All command examples use forward slashes. They work on Windows, macOS, and Linux for
git -C, cd, and dotnet build.
Set both roots once per session:
# Windows PowerShell — example from a Public + KovaForge layout
$ShareXRepo = 'C:\Users\Public\source\repos\ShareX Team\ShareX'
$XerahSRoot = 'C:\Users\Public\source\repos\KovaForge\XerahS'
# macOS / Linux shell — sibling layout
ShareXRepo="/Users/mike/Projects/ShareX Team/ShareX"
XerahSRoot="/Users/mike/Projects/ShareX Team/XerahS"
Core rules
- The newest relevant upstream commit must be resolved from the local ShareX repo's git history, not guessed.
- Diff against the mapped XerahS code root. The upstream source lives at
ShareX/ShareX.ImageEditor/...; the target code lives atXerahS/ShareX.ImageEditor/src/ShareX.ImageEditor/.... - Scan every relevant upstream commit from the previous sync point through the newest relevant commit, not just the tip commit.
- Build a holistic understanding of the bugs fixed and features added across that whole commit window before changing XerahS.
- Before implementing anything, publish a concise implementation manifest that lists every bug fix and enhancement identified from the new ShareX commits.
- For every manifest item, check whether XerahS already fixed it, implemented it differently, partially implemented it, or has a better local behavior that must be preserved.
- Do not start implementing an individual bug fix or enhancement until it appears in the manifest with its source commit, affected files, XerahS status, and intended XerahS mapping.
- Prefer the best combined design over a direct ShareX copy: preserve superior XerahS behavior, import superior ShareX behavior, and write a custom implementation when that is the cleanest integration.
- Read the affected upstream and XerahS files to clarify intent, control flow, rendering behavior, and wiring before re-implementing anything ambiguous.
- Preserve XerahS-only repository-level differences such as the submodule's
src/layout, multi-targeting, and any confirmed host integration changes. - Do not overwrite XerahS-specific fixes blindly. If a target file already diverged for Avalonia or host integration, port the upstream intent instead of doing a raw replace.
- This is not a blind cherry-pick workflow. Review the upstream change set, understand the behavior being introduced or fixed, and then map that behavior into the Avalonia submodule.
- Re-implement the upstream behavior in the XerahS ImageEditor submodule after understanding it, instead of mechanically transplanting diffs.
- Build before claiming completion.
- Commit each completed bug fix and enhancement separately in the
ShareX.ImageEditorsubmodule. Do not bundle unrelated manifest items into one port commit. - If verification passes and the user did not ask to pause, push the submodule commits and then commit and push the XerahS root pointer update.
- Do not add a
ProjectReferencetoShareX.Avalonia,ShareX.Avalonia.Tools, orShareX.Tools. XerahS ImageEditor is a standalone Avalonia/Skia library. RewriteShareX.AvaloniaUI.*usings andavares://ShareX.Avalonia/...URIs back to ImageEditor types (Presentation.Theming,Presentation.Rendering,Hosting). - Do not rename XerahS
Hosting//AvaloniaIntegrationto upstreamIntegration//ImageEditorIntegration. Port new host APIs intoHosting/AvaloniaIntegration.cs. - XerahS is English-only and must not gain multi-language support. Never port ShareX
Localization/(default or satellite.resx,Strings.Designer.cs,EffectBrowserLocalization.cs, translation READMEs,ValidateTranslations.ps1) and never add resx generator /EmbeddedResourcelocalization items to the XerahS ImageEditor csproj. When ShareX replaces UI text withStrings.*or{x:Static res:Strings.*}, keep or restore the English literal — take the wording from the defaultStrings.resxvalue if needed. Mark those upstream commitsskipin the manifest. IfLocalization/is already in the XerahS submodule from a prior port, do not grow it: no new cultures, no resx syncs, no newStrings.*call sites.
Step 0 - Resolve the upstream commit range
0a0 - Align the XerahS root and submodule safely
Before resolving the ShareX range, make sure the XerahS root branch and the
ShareX.ImageEditor submodule are in a predictable state.
git -C "$XerahSRoot" status --short --branch
git -C "$XerahSRoot" submodule status
git -C "$XerahSRoot/ShareX.ImageEditor" status --short --branch
If the root branch is behind and there are no conflicting local changes, prefer a fast-forward pull that does not recurse into submodules:
$env:GIT_TERMINAL_PROMPT = '0'
git -C "$XerahSRoot" pull --ff-only --no-recurse-submodules
This avoids a root pull hanging on nested submodule fetches. If a previous pull or
fetch stalls with no lock held, stop only the stale git processes and retry the
root fast-forward with --no-recurse-submodules.
After the root is current, align the submodule branch with its remote before
porting. If a local submodule commit is a cherry-pick duplicate, let git pull --rebase skip it rather than preserving a duplicate patch:
$env:GIT_TERMINAL_PROMPT = '0'
git -C "$XerahSRoot/ShareX.ImageEditor" pull --rebase
Do not stage the root submodule pointer update that results from this housekeeping until after the port build gates pass.
0a - Confirm the local ShareX checkout is current
git -C "$ShareXRepo" status --short
git -C "$ShareXRepo" branch --show-current
git -C "$ShareXRepo" rev-parse HEAD
git -C "$ShareXRepo" fetch --prune
git -C "$ShareXRepo" status --short --branch
Use the checked-out branch as the default upstream branch unless the user requests a different ref.
If the local ShareX checkout is behind its upstream tracking branch, pull it before resolving the ImageEditor range:
git -C "$ShareXRepo" pull --ff-only
If ShareX has local uncommitted changes, do not overwrite them. Prefer:
- If the changes are unrelated and the checkout is only needed for read-only upstream assessment, use
git pull --rebase --autostashso the newest source is available locally. - If the changes conflict with the pull or look relevant to
ShareX.ImageEditor, stop and report that the local ShareX checkout must be cleaned or reviewed before porting.
After pulling, record the updated ShareX HEAD and use that local source for the rest of the assessment. This keeps the upstream code local and fast to inspect without cloning ShareX again.
0b - Find the latest ShareX commit that touches ShareX.ImageEditor
git -C "$ShareXRepo" `
log -1 --format="%H %cs %s" -- ShareX.ImageEditor
This is the latest relevant upstream commit. Record it.
0c - Find the last recorded sync point in XerahS
Read $XerahSRoot/ShareX.ImageEditor/PORT_STATUS.md.
Expected fields:
ShareX.ImageEditor commit: <hash>XerahS submodule last synced to: <hash>
If the file is missing or stale, derive the baseline from repo history and note the assumption in the final update.
0d - List pending upstream commits
git -C "$ShareXRepo" `
log --reverse --oneline <last_synced_sharex_hash>..HEAD -- ShareX.ImageEditor
Use this list to decide whether the catch-up is:
- Low risk: isolated bug fix in a small file
- Medium risk: touches controllers, view models, or multiple files
- High risk: adds files, changes tooling or rendering, or updates editor interaction behavior
Do not treat this commit list as a queue for blind cherry-picks. Use it as a review list for semantic porting. Read the whole queue from oldest to newest so you understand how fixes and features build on each other.
Step 1 - Map source paths to target paths
The ShareX tree and XerahS submodule do not have the same repository layout.
| Upstream path | Target path |
|---|---|
ShareX.ImageEditor/Assets/... |
ShareX.ImageEditor/src/ShareX.ImageEditor/Assets/... |
ShareX.ImageEditor/Core/... |
ShareX.ImageEditor/src/ShareX.ImageEditor/Core/... |
ShareX.ImageEditor/Hosting/... |
ShareX.ImageEditor/src/ShareX.ImageEditor/Hosting/... |
ShareX.ImageEditor/Integration/... |
ShareX.ImageEditor/src/ShareX.ImageEditor/Hosting/... (keep XerahS folder name; ImageEditorIntegration.cs maps to AvaloniaIntegration.cs) |
ShareX.ImageEditor/Localization/... |
Skip. XerahS is English-only (core rule 19). Do not copy this tree. |
ShareX.ImageEditor/Presentation/... |
ShareX.ImageEditor/src/ShareX.ImageEditor/Presentation/... |
ShareX.ImageEditor/ShareX.ImageEditor.csproj |
ShareX.ImageEditor/src/ShareX.ImageEditor/ShareX.ImageEditor.csproj |
Do not diff the upstream folder against the submodule repo root. Always diff it against
src/ShareX.ImageEditor.
Step 2 - Inspect the exact upstream delta
2a - List files changed since the last sync
git -C "$ShareXRepo" `
diff --name-only <last_synced_sharex_hash>..HEAD -- ShareX.ImageEditor
2b - Review each pending commit with stats
git -C "$ShareXRepo" `
show --stat --summary --oneline <sharex_commit>
Also inspect the actual patch for behavior-critical commits:
git -C "$ShareXRepo" `
show <sharex_commit> -- ShareX.ImageEditor
For a real catch-up, do this for every commit in the pending range from the last synced hash to the newest relevant hash. Summarize for yourself:
- which user-visible features were added
- which bugs were fixed
- which commits depend on earlier commits in the range
- which files are the authoritative implementation points
2c - Publish the implementation manifest before editing
Before changing files, post a concise manifest in chat. This is a mandatory gate for the port session.
The manifest must include every bug fix and enhancement identified in the pending ShareX commits, grouped by implementation item rather than by raw file. Each item must include:
- Type:
Bug fix,Enhancement,Refactor with behavior impact, orInfrastructure - Source commit(s): the ShareX hash and short subject
- Upstream behavior: what changed for users or editor internals
- XerahS status:
missing,already fixed,implemented differently,partially implemented, orconflicts with XerahS - Target files: the mapped XerahS files expected to change
- Decision: raw sync, manual merge, custom implementation, keep XerahS behavior, or intentional skip with reason
- Rationale: why that decision gives XerahS the best behavior
- Commit plan: the intended standalone submodule commit subject for this item, or
nonewhen the item is kept/skipped
Use this shape:
## ImageEditor Port Manifest
- [ ] Bug fix: <short name>
Source: `<hash>` <subject>
Upstream behavior: <one sentence>
XerahS status: <missing/already fixed/implemented differently/partially implemented/conflicts>
Target files: `<mapped/file.cs>`, `<mapped/file.axaml>`
Decision: <raw sync/manual merge/custom implementation/keep XerahS/skip>
Rationale: <why this is the best XerahS outcome>
Commit plan: `[ShareX.ImageEditor] [Fix] <short description> from ShareX@<hash>`
- [ ] Enhancement: <short name>
Source: `<hash>` <subject>
Upstream behavior: <one sentence>
XerahS status: <missing/already fixed/implemented differently/partially implemented/conflicts>
Target files: `<mapped/file.cs>`
Decision: <raw sync/manual merge/custom implementation/keep XerahS/skip>
Rationale: <why this is the best XerahS outcome>
Commit plan: `[ShareX.ImageEditor] [Enhancement] <short description> from ShareX@<hash>`
During implementation, work against this manifest. Announce the item being implemented before editing it, and update the item status as it is completed or intentionally skipped.
Do not use raw sync as the default. It is only acceptable after comparing the mapped XerahS file and confirming there is no local fix, no different design, and no host-specific behavior worth preserving.
When multiple manifest items share prerequisite scaffolding, such as a new enum, helper, or control required by later items, either:
- include the shared scaffold in the first item that needs it when that keeps the commit coherent, or
- create a separate preparatory commit with
[ShareX.ImageEditor] [Infrastructure] ...before the dependent bug-fix/enhancement commits.
Do not create one large "sync latest ShareX changes" submodule commit unless the pending range has exactly one cohesive manifest item.
Grouping is acceptable when the range has 30+ ImageEditor commits, 50+ changed files, or
when central files (MainViewModel.cs, EditorCore.cs, EditorView.axaml,
AnnotationToolbar.axaml) are touched by many items at once. Commit each cleanly
file-scoped feature (new partial-class features, EditorBuiltInToolbars, asset syncs)
separately, and land the interleaved central files in one final [Port] commit that
lists the covered items in its body. Note in PORT_STATUS.md that intermediate commits
only build as a batch. Prefer the manifest to still list every item individually even
when commits are grouped. Do not create a localization-catalog commit (core rule 19).
2d - Read code to remove ambiguity
If the commit message or patch alone is not enough, read the upstream implementation files and the current XerahS counterparts before editing.
Typical files to inspect:
- controllers
- view models
- rendering and visual factory code
- annotation model classes
- views and control markup
Strings.*/{x:Static res:Strings.*}call sites (restore English literals; do not copyLocalization/)Hosting/AvaloniaIntegration.csvs upstreamIntegration/ImageEditorIntegration.cs- the target
.csprojfor new files or assets (never for resx generator entries)
2e - Compare mapped files, not raw repo roots
For each changed upstream file ShareX.ImageEditor/<relative_path> compare it to:
$XerahSRoot/ShareX.ImageEditor/src/ShareX.ImageEditor/<relative_path>.
If the target file does not exist, it is a net-new addition and therefore high risk.
Integration/* that already exists under XerahS Hosting/ is a rename, not NEW.
2e-triage - Automate divergence triage for large ranges
For ranges with dozens of changed files, do not eyeball each file. Check out the last synced upstream commit into a worktree:
git -C "$ShareXRepo" worktree add <sharex-base> <last_sync>
Use /tmp/sharex-base on Linux/macOS, or a sibling of $ShareXRepo on Windows. Remove
the worktree when the session finishes.
Classify every upstream-changed file:
NEW: absent from the XerahS code root — sync it in as a new file, exceptIntegration/*(map toHosting/),Localization/*(skip, core rule 19), and files that exist only because ShareX extracted them intoShareX.Avalonia.SAFE_SYNC: the XerahS file equals the upstream base version after normalizing the license-header region, BOM, CRLF-vs-LF, and trailing whitespace — a raw sync from upstream HEAD is safe because XerahS never diverged in content. NeverSAFE_SYNCLocalization/.AVALONIA_NS: the upstreambase -> headdelta is only ausingmove fromShareX.ImageEditor.Presentation.ThemingorHostingtoShareX.AvaloniaUI.*. Keep XerahS. Do not raw-sync. The 2026-08-18 range marked ~200 ImageEffects files DIVERGED for this two-line change.SKIP_I18N: anything underLocalization/. Skip; do not treat as NEW or SAFE_SYNC.DIVERGED: real content differences — these carry XerahS adaptations and require a manual merge.
For each DIVERGED file, produce two normalized diffs: base -> xerahs (the XerahS
adaptation to preserve) and base -> head (the upstream change to port). Files whose
base -> xerahs delta is only blank lines or an encoding artifact can be treated as
SAFE_SYNC. The 2026-07-11 port turned a 149-file range into 19 real merges; the
2026-08-18 port was 380 files, mostly AVALONIA_NS plus a Localization/ tree that
must not be synced again.
2e-headers - Header, BOM, and EOL policy during syncs
Upstream ShareX files carry a UTF-8 BOM, CRLF line endings, and the ShareX license
header. The XerahS submodule stores UTF-8 without BOM, LF endings, and a mix of the
ShareX.ImageEditor - The UI-agnostic Editor library for ShareX header and headerless
files. When syncing:
- Strip the BOM and convert CRLF to LF.
- For existing files, preserve the target file's current header state byte-for-byte (including "no header") and replace only the body below it.
- For new
.csfiles, add the submodule-standardShareX.ImageEditorheader wording. - Never let a raw copy replace the XerahS header with the upstream ShareX wording.
2f - Compare behavior, not just text
For every manifest item, answer these questions before editing:
- Is the same bug already fixed in XerahS under a different implementation?
- Is the ShareX fix better, worse, or complementary to the XerahS behavior?
- Does XerahS have host integration, Avalonia-specific behavior, tests, or persistence hooks that ShareX does not have?
- Can the best result be achieved by combining both implementations instead of copying either one wholesale?
- Is a small custom implementation clearer than copying the upstream patch?
If XerahS already has an equal or better implementation, keep it and record the item as keep XerahS behavior or skip.
If both implementations solve different parts of the problem, create a manual merge or custom implementation and record the rationale in PORT_STATUS.md.
Step 3 - Port or sync safely
3a - When a raw file sync is acceptable
Raw file sync is an exception, not the normal path. You may replace the target file with the upstream version only when all of these are true:
- The file lives under
Core/,Presentation/,Hosting/, orAssets/and maps cleanly intosrc/ShareX.ImageEditor(neverLocalization/) - The target file was compared and does not contain a relevant XerahS fix, alternate implementation, host integration, persistence hook, test-supported behavior, or Avalonia-specific adaptation that would be lost
- The upstream change is exactly what XerahS needs and there is no repo-layout-only difference inside the file
- The manifest item explicitly marks the decision as
raw syncwith a short rationale
3b - When manual porting is required
Port the intent instead of copying the whole file when any of these are true:
- The target file contains XerahS-specific Avalonia, SkiaSharp, or host wiring that is not present upstream
- The target file already diverged beyond the pending upstream commit range
- The upstream file assumes repository or project settings that do not match the submodule
- The upstream commit adds a feature partially present in XerahS and a direct replace would regress local behavior
- XerahS already has a different fix for the same bug and the best result needs parts of both implementations
- The upstream code is correct for ShareX but a custom XerahS implementation would be simpler, safer, or better aligned with current XerahS architecture
Manual porting usually means:
- keep the XerahS file as the base
- apply the upstream behavior in small, reviewable hunks
- re-implement the bug fix or feature after understanding the whole upstream flow
- keep any superior XerahS behavior and merge only the missing upstream behavior
- add or update tests for bugs where the upstream fix differs from the XerahS implementation
- rebuild after behavior-critical controller, view model, rendering, or view changes
- only replace the whole file when the diff is layout-only and no XerahS adaptation would be lost
When a transformed upstream patch cannot apply because of expected XerahS divergence, a mapped final-state sync is acceptable as an implementation aid, but only after the manifest has been posted. After such a sync, immediately re-apply and verify these known XerahS adaptations before building:
Annotation.cs: keepJsonPolymorphic(TypeDiscriminatorPropertyName = "$type"),JsonIgnoreon computed/runtime state, and XerahSStepTailStyle; add any new upstreamJsonDerivedTypeentries such as cursor annotations.BaseEffectAnnotation.cs/ImageAnnotation.cs: keep[JsonIgnore]on the runtime bitmap properties (EffectBitmap,ImageBitmap) and theSystem.Text.Json.Serializationusing.EditorCore.cs: keepGetAnnotationsSnapshotForPersistence(),RestoreAnnotations(...), and number-counter resync after restore/renumbering.MainViewModel.cs: keepApplicationNameandEditorTitlebecause XerahS host windows bind to that title contract.BuildWindowTitlemust useEditorTitle, not a ShareX product title string (that wording says "ShareX").MainViewModel.ImageState.cs: keepCreateSourceImageCopyForPersistence()andGetAnnotationSnapshotForPersistence().EditorView.CoreBridge.cs: keepGetAnnotationSnapshot()andRestoreAnnotations(...)(with number-counter and history-state resync).AvaloniaIntegration.cs: preserve XerahS task-mode behavior, especiallyShowFileMenu = !taskModeand start-screen suppression, while adding new upstream event wiring.EditorIcons.cs: preserve XerahS-only icon constants such as tail-style icons (TailStyleTriangle,TailStyleArrow) when upstream icon syncs replace the file.NumberAnnotation.cs/StepControl.cs: XerahS has the tail-style system (StepTailStyleTriangle/Arrow,TryGetArrowTailOutline,TryGetCircleSegmentExitPoint, arrow tail rendering). Merge upstream step features (StepType, IsBold, TailEnabled, tail geometry changes) into that system instead of replacing it; upstream deletesTryGetCircleSegmentExitPoint, but the XerahS arrow tail still needs it.- Theming file names: keep XerahS
ImageEditorStyles.axaml/ImageEditorTheme.axaml. Upstream has usedAppStyles/AppThemeand laterEditorStyles/ AvaloniaShareXTheme. Rewrite those URIs back to the XerahS names. RootXerahS.UI(MainWindow resources),XerahS.RegionCapture, andEditorView.axamlStyleInclude all bind to the XerahS URIs.XerahS.UIThemeServicealready callsThemeManager.SetTheme; do not delete the standalone editor theme panel unless the user asks. EditorView.axaml: keep the XerahS<StyleInclude Source="avares://ShareX.ImageEditor/Presentation/Theming/ImageEditorStyles.axaml"/>block. Upstream applies styles app-wide fromAvaloniaIntegration.Initialize(), which does not cover XerahS embedded hosting (MainWindow editor tab, RegionCapture overlay).EffectBrowserPanel.axaml: keep the XerahS-only StyleInclude added by submodule commit72ff989(tile spacing in standalone hosts).- Root integration: when
IAnnotationToolbarAdaptergains members, updatesrc/desktop/app/XerahS.RegionCapture/ViewModels/RegionCaptureAnnotationViewModel.csin the same session. RegionCapture is not in the submodule, but the full XerahS build depends on that adapter matching the submodule interface. Note:XerahS.RegionCapturedeclares its ownBorderStyleenum in the parent namespace, which wins simple-name lookup over ausing BorderStyle = ...alias; use a distinctly named alias such asAnnotationBorderStyle. - Root integration: when
MainViewModelevent signatures change (for example the 2026-07 move toFunc<Task>CopyRequestedandFunc<Task<string?>>SaveRequested/SaveAsRequested), updatesrc/desktop/app/XerahS.UI/Services/MainViewModelHelper.cswiring in the same session, returning the saved path so editor notifications can display it. - Root sweep for removed members: when upstream removes API (for example
TextUnderline/IsUnderlinein the 2026-07 range), grepsrc/andtests/for the removed names;OverlayWindow.Canvas.csandtests/XerahS.Tests/RegionCapture/*are common consumers. - Package additions: when the upstream csproj gains
PackageReferenceitems, add the versions to the submodule's ownShareX.ImageEditor/Directory.Packages.props(nearestDirectory.Packages.propswins for CPM), not the XerahS root props. The XerahS root props intentionally disable central management for the submodule path. Do not dropMicrosoft.ML.OnnxRuntime.DirectML/Vortice.DXGIwhile XerahS still ships background-remover in the submodule. - Localization / i18n: skip the entire ShareX
Localization/tree (core rule 19). Port English UI wording only. Do not replay the add-then-deleteEmojiCatalog_*experiment. AnnotationToolbar.ShowToolOptions: XerahSIAnnotationToolbarAdapteralready hasShowToolOptionsmeaning "current tool has option widgets." Name the host chrome overrideShowToolOptionsPanel(or bind the control property only withElementName).EditorView.UseBuiltInToolbars: default true. XerahS hosts donew EditorView()andEditorWindowwithout setting the flag. Workspace embeds can set it false.- Tool windows extracted upstream (combiner, hash, QR, video, background remover,
comparer, icon converter, screen color picker): keep them in ImageEditor unless
XerahS.UI already owns that tool and the standalone
ShareX.ImageEditor.slnapp no longer needs theAvaloniaIntegration.Show*entry point. Combiner already lives inXerahS.UI— do not add it back to the submodule. - RegionCapture: XerahS is N overlay windows +
RegionCaptureAnnotationViewModel+ a localEditorCore, not ShareX's embeddedEditorView. Port annotation/core behavior (for exampleSmartEraserAnnotation.ConfigureFill) intoOverlayWindow.Canvas.csin the same session. Do not portRegionCaptureWindow. - Win32-only helpers (
DllImport("user32.dll")cursor-screen math, GDI magnifier): re-implement with AvaloniaTopLevel.Screens/ a cross-platform fallback. Do not copy the P/Invoke.
PowerShell versions in this repo may not support Set-Content -Encoding utf8NoBOM. For mechanical header normalization during mapped syncs, use
[System.Text.UTF8Encoding]::new($false) with [System.IO.File]::WriteAllText(...)
instead of relying on that encoding name.
3c - When to write a custom implementation
Write a custom implementation when the upstream patch exposes the desired behavior but the XerahS architecture calls for a different shape.
Common cases:
- XerahS already has a related service, adapter, or persistence hook that should own the behavior.
- ShareX solves the bug in UI code, but XerahS should solve it in
EditorCoreor a shared controller. - The upstream change duplicates logic already centralized in XerahS.
- The upstream behavior is useful, but its exact code would regress RegionCapture, embedded editor mode, tests, serialization, or host integration.
- Combining both implementations would otherwise create duplicated state or unclear ownership.
Record custom implementations in PORT_STATUS.md with the source ShareX commit, the XerahS files changed, and the reason for not copying upstream code.
3d - Preserve known XerahS repository-level differences
Keep these unless the user explicitly asks to change them:
src/ShareX.ImageEditorrepository layoutHosting/+AvaloniaIntegration(notIntegration/+ImageEditorIntegration)- Lucide, cursor
.curfiles,ThemeManager,CursorAssetLoader, andBitmapConversionHelpersinside the submodule - Tool windows still hosted by
AvaloniaIntegration.Show* - XerahS-specific solution or project structure
- XerahS multi-targeting or packaging differences
- Any host integration already verified in XerahS
- English-only UI: do not import ShareX multi-language resources (core rule 19)
3e - New-file checklist
For each new upstream file:
- If it lives under
Localization/, skip it (core rule 19). - Create the mapped target directory if needed.
- Add the file under
src/ShareX.ImageEditor. - Update the target
.csprojonly if the new file requires an explicit item entry. Do not addPublicResXFileCodeGeneratoror other resx generator items. - Search for references to the new type or view and port the wiring in the same session.
Step 4 - Verification gates
4a - Targeted ImageEditor build
cd "$XerahSRoot"
dotnet build "ShareX.ImageEditor/src/ShareX.ImageEditor/ShareX.ImageEditor.csproj" -m:1
If it genuinely stalls, stop it, clear the lock, and retry with single-node compilation when appropriate.
4b - Full solution build
cd "$XerahSRoot"
dotnet build "src/desktop/XerahS.sln" -m:1
This must finish with 0 errors before any push.
4c - Standalone submodule solution and tests
The submodule ships its own solution with a standalone app project that consumes
AvaloniaIntegration; API changes that pass the XerahS build can still break it:
cd "$XerahSRoot/ShareX.ImageEditor"
dotnet build "ShareX.ImageEditor.sln" -m:1
Also run the XerahS test suite; RegionCapture adapter and editor behavior tests live there:
cd "$XerahSRoot"
dotnet test "tests/XerahS.Tests/XerahS.Tests.csproj" -m:1
On Windows, Linux/macOS tests that start /bin/sh or assert POSIX/file:/// path
shapes can fail without any ImageEditor change. Do not block the port on those.
Block only on ImageEditor, RegionCapture adapter, or editor-behavior failures.
Step 5 - Update tracking
After the catch-up:
- Update
$XerahSRoot/ShareX.ImageEditor/PORT_STATUS.md - Record:
- latest upstream ShareX commit used
- previous recorded sync point
- files added or updated
- risk summary
- adaptations kept for XerahS
Suggested status block:
## Port Activity (2026-04-09)
- Previous recorded ShareX sync: `<old_hash>`
- Latest upstream ShareX commit touching ShareX.ImageEditor: `<new_hash>`
- Result: `Caught up through <new_hash>`
- Notes: `<manual adaptations or intentional skips>`
Step 6 - Commit discipline
The submodule is a shared library repo, so submodule commits do not use the XerahS version prefix.
Commit each completed manifest item separately in the ShareX.ImageEditor submodule.
Use these submodule commit formats:
[ShareX.ImageEditor] [Fix] <bug fix description> from ShareX@<hash>
[ShareX.ImageEditor] [Enhancement] <feature description> from ShareX@<hash>
[ShareX.ImageEditor] [Infrastructure] <shared prerequisite description>
[ShareX.ImageEditor] [Port] <description> from ShareX@<hash>
Use [Port] only for cohesive changes that are not cleanly a fix or enhancement, such as a behavior-impacting refactor. If one upstream commit contains several unrelated user-visible fixes or enhancements, split the XerahS commits by manifest item, not by upstream commit.
Before each submodule commit:
- Stage only the files/hunks for that manifest item.
- Run the smallest relevant build or test if the item is behavior-critical and fast enough.
- Commit with the planned subject.
- Continue to the next manifest item.
If a later item needs to amend a previous item because of a discovered integration issue, prefer a new targeted follow-up commit unless history has not been pushed and the scope is still clearly the same manifest item.
Then update the XerahS root repo to point to the new submodule commit.
Step 7 - Push discipline
After verification succeeds:
- Confirm every implemented manifest item has its own
ShareX.ImageEditorsubmodule commit, except items explicitly kept or skipped. - Push the submodule branch after the full verification gates pass.
- Stage the updated submodule pointer and any root tracking or skill changes in
XerahS. - Commit the XerahS root repo using the next unreleased XerahS version prefix. The root commit may summarize the batch because it only records the final submodule pointer and host/test integration.
- Push the XerahS root branch.
Do not stop after a local commit unless the user explicitly asks to pause before push.
Fast path for this repo
For the common "catch up XerahS to the latest local ShareX state" task:
- Resolve
$ShareXRepoand$XerahSRootindependently (user paths first). Use the cloud/CI fallback clone only when no local ShareX checkout exists. - Read
$XerahSRoot/ShareX.ImageEditor/PORT_STATUS.mdto get the last synced ShareX hash. - Run
git -C "$ShareXRepo" fetch --pruneand checkgit -C "$ShareXRepo" status --short --branch. - If the local ShareX checkout is behind, run
git -C "$ShareXRepo" pull --ff-only; use--rebase --autostashonly for unrelated local ShareX changes that must be preserved. - Run
git -C "$ShareXRepo" log -1 --format="%H %cs %s" -- ShareX.ImageEditor. - Run
git -C "$ShareXRepo" diff --name-only <last_sync>..HEAD -- ShareX.ImageEditor. - Map each changed upstream file into
$XerahSRoot/ShareX.ImageEditor/src/ShareX.ImageEditor. MapIntegration/toHosting/. SkipLocalization/(core rule 19). - For large ranges, run the 2e-triage classification (NEW / SAFE_SYNC / AVALONIA_NS / SKIP_I18N / DIVERGED) against a base worktree before deciding sync strategy per file.
- Review every upstream commit in the range so you understand the complete feature and bug-fix set.
- For each item, compare the upstream behavior against the current XerahS behavior and decide whether it is missing, already fixed, implemented differently, partially implemented, or conflicting.
- Post the ImageEditor Port Manifest listing every identified bug fix and enhancement, including XerahS status, decision, and rationale, before editing.
- Read upstream and XerahS code where needed to confirm how the behavior works.
- Port, manually merge, keep XerahS behavior, or write a custom implementation as appropriate; do not blind cherry-pick or raw-copy diverged Avalonia files.
- Commit each completed bug fix/enhancement as a separate
ShareX.ImageEditorsubmodule commit, keeping shared infrastructure separate when needed. - Build the ImageEditor project, then the XerahS solution.
- Update
PORT_STATUS.md, then push the submodule commits and commit/push the root pointer separately.