Desktop Material

User Guide

The everyday repository loop from inspecting changes through safe synchronization

The safest rhythm is simple: inspect, stage, commit, then synchronize. The tools below add power without changing that basic loop.

A conceptual safe Git workflow from working files to reviewed cloud synchronization

A task-oriented tour of Desktop Material's features. It assumes you already know the basic GitHub Desktop workflow (clone, commit, push, branch, pull request) — that all still works. This guide focuses on what Desktop Material adds on top.

Desktop Material is supported, built, packaged, released, and accepted on Windows only. Use its published Windows x64 installer. The retained Linux TUI prototype, package notes, parity ledger, and Xvfb captures are historical July 27 evidence, not a supported edition or Windows-release blocker.

Feature guide

The numbered roadmap now extends through M27. M0–M21 and M23 have published receipts; M22 keeps its separate visual refresh, and the acceptance/publication state for M24–M27 is recorded in the repository's ROADMAP.md and HANDOFF.md. The July 22 feature continuation is published at f7b4760a13 with exact-source CI, code scanning, Pages/wiki, and six-asset Windows installer Release verification. Updater recovery commits 241cc90ce9 and 04246fdf12 are also published and were accepted from an installed legacy build through the new alphabetic-z release lane. July 23 Cheap LFS registry storage and automatic push batching and the responsive Releases correction are published through c22e29a03a. Exact-source cloud, CI, CodeQL, Pages, and installer run 30057456712 passed; the latter published immutable six-asset Release v3.6.3-beta3-zadthusbjk. The live Bambu cloud/verifier and initial 10/10 clone integrity proofs are complete; the serialized materialization correction keeps its final UI receipt in HANDOFF.md.

The July 27 app-hosted browser, exact-90% Cheap LFS restore look-ahead, and private-repository lock passed the final focused 760/760 across 58 files gate, 14/14 verifier contracts, full TypeScript, the exact Windows production build, and isolated hidden-desktop interaction/privacy review. The source and captures are pushed through 2abccae8fd, and Pages/wiki publication is verified live. Packaged Windows E2E is verified. Installer/Release evidence remained pending at that dated checkpoint; the archived TUI compatibility work is outside the current Windows acceptance boundary.

The temporary-submodule changeset completed its local ten-pass, final post-build child/Back, and fresh-bundle duplicate Open/Back race inspections, including read-only mutation boundaries and owned headless-resource cleanup. Initial remote CI caught a macOS error-ordering defect without publishing; correction 98d93ccc passed its full remote CI gate and published v3.6.3-beta3-b0000000165. Exact publication receipts are in HANDOFF.md.

The Guided Feature Gallery declares the canonical 91-scene Windows visual target: every catalogued function or state must own one distinct screenshot rather than borrow an overview image. The current-source updater frame is accepted and published as its own target; it does not replace the immutable historical migration frame. Five earlier Linux/Xvfb captures remain archived outside that target set.


Advanced Git and collaboration workflows

M21 adds progressively disclosed controls around the familiar Desktop flow:

The complete 30-item feature ledger links to behavior, recovery, security, and test details for every workflow.

Advanced tag lifecycle workspace with local, pushed, and remote-only tags


Historical Linux TUI prototype (unsupported)

The commands below reproduce the July 27 prototype acceptance record. They are not a current installation recommendation; Desktop Material's supported product and package path is Windows. At that checkpoint the prototype required Git, Python 3.10–3.13, and uv.

Linux shell:

git clone https://github.com/Ding-Ding-Projects/desktop-material.git && cd desktop-material && uv tool install ./tui && uv tool update-shell

Windows PowerShell:

git clone https://github.com/Ding-Ding-Projects/desktop-material.git; if ($LASTEXITCODE -ne 0) { throw 'git clone failed' }; Set-Location .\desktop-material; uv tool install .\tui; if ($LASTEXITCODE -ne 0) { throw 'uv tool install failed' }; uv tool update-shell

Close and reopen the terminal afterward so the updated PATH is loaded. Then run github /path/to/repository on Linux or github C:\path\to\repository on Windows. The interactive acceptance target was Linux-first; the Windows Terminal launch path and cross-platform core were also tested at that checkpoint.

The same launcher provides Cheap-LFS-aware Git commands:

github -C /path/to/repository push --dry-run
github -C /path/to/repository push origin main
github -C /path/to/repository pull --ff-only
github -C /path/to/repository git status --short

Open/Create also has a clickable folder browser. Bracketed or Textual clipboard paste unwraps one matching pair of single or double quotes immediately without evaluating shell syntax; submission applies the same normalization as a fallback. See the complete TUI installation guide for package, upgrade, Docker, security, and failure-mode details.


Install on Windows

Desktop Material's automated releases provide an x64 per-user installer. The Windows package command also creates GitHub Desktop-x64.zip, and the gated release job requires that portable ZIP beside the installer and update-feed assets. Open Windows PowerShell 5.1 or PowerShell 7 as your normal user and run:

Microsoft.PowerShell.Utility\Invoke-RestMethod 'https://raw.githubusercontent.com/Ding-Ding-Projects/desktop-material/main/script/install-windows.ps1' | Microsoft.PowerShell.Utility\Invoke-Expression

The command loads the tracked installer script from this repository. The script then:

  1. queries Ding-Ding-Projects/desktop-material for its newest non-draft, non-prerelease GitHub release;
  2. requires exactly one installer matching the native Windows architecture and validates that its HTTPS URL belongs to this repository;
  3. checks the reported byte count and GitHub SHA-256 release-asset digest, then requires any Authenticode signature to be valid;
  4. runs the per-user Squirrel installer with its supported --silent flag;
  5. requires Squirrel to exit zero and verifies the installed postcondition; and
  6. removes the installer from its unique, bounded temporary directory.

For automation, invoke the reviewed script as a script block and choose the explicit current-user operation:

$installer = [scriptblock]::Create((Microsoft.PowerShell.Utility\Invoke-RestMethod 'https://raw.githubusercontent.com/Ding-Ding-Projects/desktop-material/main/script/install-windows.ps1'))
& $installer -Operation Install -InstallScope CurrentUser
& $installer -Operation Update -InstallScope CurrentUser
& $installer -Operation Uninstall -InstallScope CurrentUser

Install may refresh a complete existing installation. Update requires one. Install uses the downloaded, digest-verified setup asset. Update invokes the installed updater against the exact immutable release-tag feed with --update=<url> --silent, preserving normal Squirrel update behavior instead of performing a full reinstall. Uninstall is idempotent when the app is already absent and otherwise invokes the installed Update.exe --uninstall --silent; no executable is downloaded for removal. Every operation refuses a partial installation and asks for a running app to be closed normally instead of force-killing it. Squirrel has a 15-minute process deadline and its installed/removed postcondition has a one-minute deadline; nonzero exit codes and missing postconditions are failures. Use -ResolveOnly to inspect the exact plan without changing the machine.

The application scope is always CurrentUser under %LOCALAPPDATA%\GitHubDesktop. AllUsers is rejected. The generated MSI is a machine deployment bootstrapper which schedules the same per-user setup at logon, not a conventional machine-wide Desktop Material payload. Run mutating operations in a normal, non-administrator PowerShell session; the script rejects elevation before Squirrel can show its unsupported-elevation error. It also preflights Squirrel's .NET Framework 4.5 minimum instead of opening a framework installer or reboot prompt during an unattended install.

Windows release workflows permanently publish unsigned x64 installers. Packaging and publication require the setup executable and MSI to report NotSigned; release notes warn that Windows may show SmartScreen or an unknown-publisher prompt. The script reports the missing signature after verifying the GitHub digest, and it stops rather than selecting a different package on ARM64 or 32-bit Windows. To inspect or download the asset yourself, use the latest release page. When the portable ZIP is present, download and extract it before starting the packaged executable; it does not run the Squirrel installer. The published baseline already contains the required installer, feed, and portable ZIP assets. The verified updater-migration Releases contain all six required installer, feed, NuGet, MSI, and portable-ZIP assets.

While GitHub Actions is actively building a newer exact main commit, the About updater can show New update coming soon (or the selected Cantonese or bilingual equivalent). This remote state is never persisted and cannot make an update installable. Once the Release exists, the next normal update check takes over. Each automated Release lists bounded, sanitized commit subjects from the previous installer release through its exact target SHA. CI and Pages invocations use independent per-run groups, so a newer run never cancels or replaces an older one. The installer publisher shares one group on purpose, so that two runs cannot choose the same release code name; it never cancels a running publisher, but only one run waits behind it, so a third push supersedes the one still queued.

Automatic and Super Express installers now use the same fixed-width alphabetic z version lane. This lets an older Super Express installation move forward instead of treating a newer ordinary Release as a downgrade. Live acceptance started from 3.6.3-beta3-s000000000201: the ordinary updater automatically selected, downloaded, and applied 3.6.3-beta3-zadtberjmv; a following same-source Super Express Release then progressed visibly through Downloading update… to Quit and Install Update.

The current-source development frame below separately proves runtime source b069384ad7d8a65d1192ee06859a705fe484c9c8 reaching the ready state through the real Electron/Squirrel event path. Its disclosed verifier-owned payload was inert, so this is UI-path evidence rather than a claim that the payload was published. Promotion e3967f1b81ec039624500797dca40a1ab6d98598 records the inspected 960×660, 47,086-byte PNG with SHA-256 0fc9caf5b13eb5b914121090f403c394545e02ea4303b11dd4598afcb3a2dfca.

Current-source About frame showing the automatic updater ready through the real Electron and Squirrel event path with a disclosed verifier-owned inert payload

目前原始碼開發版畫面已驗收:真 Electron/Squirrel 路徑用驗證器自有、已披露嘅無害 payload;呢個係 UI 路徑證明,唔係話個 payload 已經發佈。

The immutable frame below remains the separate installed legacy-to-shared-lane migration record.

Historical About frame showing an automatic update ready for a legacy Super Express installation

Creating a GitHub release

Open Repository → Release Manager and select New release. Enter the tag, target, name, and notes. Publish immediately is enabled by default; leave it enabled, select Review changes, verify that Publication says Publish immediately, and select Publish release. Desktop Material sends one direct public-release request rather than creating a draft first. Turn Publish immediately off only when you intentionally want an unpublished draft. Review the tracked script before running any remote command.


CI test reliability

The supported Windows build and the retained Linux TUI checks install their declared dependencies from the committed manifests. The Linux Python 3.13 check runs non-UI tests together and gives every UI test file a fresh Python interpreter because Textual's native syntax state can otherwise segfault after several app-heavy files share one process. All test files still run; only the process boundary changes. Python 3.10 and 3.12 use the ordinary full-suite command.

The same runner can be inspected at tui/tools/run-tests-isolated.py. It discovers test files rather than maintaining a hand-written allowlist, so adding a new UI file does not silently remove it from CI.


The shell

Desktop Material rebuilds the GitHub Desktop shell around Material Design 3. The chrome you work in every day is made of a few pieces:

The whole shell has an animated light/dark theme. Everything below tells you how to drive it. On Windows, one uninterrupted bare Alt press toggles the application menu. A held key remains one press, while Alt plus another key, another modifier, a handled event, or a modal transition cancels that sequence cleanly so a stale release cannot affect the next Alt press. While an app update downloads, a thin indeterminate progress bar appears at the top of the workspace. Choose Settings → Appearance → Update progress color to inherit the accent or select blue, violet, teal, green, amber, or rose. These update controls, the current-commit CI status tooltip, and the temporary submodule-navigation copy use the explicit language mode saved under Language: English, playful Hong Kong Cantonese, or a compact bilingual presentation. English is the fallback; the operating-system locale does not silently replace the saved choice.

Desktop Material workspace with a profile-customized app identity and favorite repository tab

Narrow toolbar with Build and Run and Commit and Push available from More without clipping


Material first run

The welcome page is a Material task surface rather than a separate stock onboarding skin. It keeps the Sign in with GitHub.com, GitHub Enterprise, and Continue without signing in routes in one focused card, preserves keyboard focus and sign-in progress, and explains that repositories stay local to the device. A tonal preview introduces the repository-focused workspace; at compact window sizes the preview steps away so the setup task remains unclipped.

Material first-run welcome with a focused setup card and tonal workspace preview


Your own words for things

Settings → Appearance → Personal vocabulary takes a local JSON file that renames the words the app shows you. Nothing ships with it, so until you load one every surface reads exactly as it always has.

{
  "version": 1,
  "terms": {
    "Fetch origin": "Check for changes",
    "Stash": "Set aside"
  }
}

At most 2000 terms and 1 MB. The whole file is checked before a single word of it is applied, and a file that fails any check changes nothing — not even partially, and never at the expense of a vocabulary you already had working. The control tells you what is in effect: a term count when one is loaded, and the exact reason when one is refused.

It is read on this computer and stays here. It is never uploaded, and it never appears in an export, a log, a diagnostic report, a screenshot, or this app's own history — which is also why the refusal message tells you what was wrong without quoting your words back at you.

School mode turns the whole feature off, along with the rest of the language settings.


Choosing the narrator's voice

If you turn the spoken narrator on (Settings → Sound), you also choose which voice reads it. English and Cantonese pick separately, because liking a particular English voice says nothing about which Cantonese voice should read the other half of a bilingual line. Speaking rate and pitch are there too.

Leave either on Choose automatically and the app picks the closest match your computer has.

The picker tells you what is actually going to be heard, which matters more often than it sounds:

If the list looks empty for a moment when you open the settings, give it a second. Windows and macOS both report their voices asynchronously, and the picker fills in as soon as they arrive.


The dim sum surprise

About one launch in ten, a small photograph of a Hong Kong dim sum dish appears in the bottom-left corner, named in English and Traditional Chinese — Classic Har Gow · 蝦餃 — and clears itself after about nine seconds.

It is a delight, not a system. It never delays the app becoming usable, never takes keyboard focus, and is not a dialog you have to dismiss; the labelled close button only saves you the wait. It stays away entirely on a first run, when startup has failed, while an update is in flight, while a blocking dialog is open, and inside your configured quiet hours. A launch it stays away from simply has no surprise; it does not lie in wait for the dialog to close.

Each dish's name is a fact rather than a joke: it reads identically in every language mode and at every playfulness level, and only the order changes (Cantonese mode leads with 蝦餃). The copy around the name follows your two playfulness sliders, each language at its own level. The photographs ship with the app — twelve of them, no network request, no tracking — and the picture's alt text names the dish in both languages so it reaches screen-reader users too.

There is no setting to switch it off, and if an older profile stored one, it is deleted on the next launch so that profile simply rejoins the draw.

Full detail: The dim sum surprise.


Signing in

Open Settings → Accounts to manage sign-ins. Desktop Material supports multiple accounts at once and, unlike stock GitHub Desktop, multiple identities on the same host — for example two GitHub.com accounts, or a work and a personal GitHub Enterprise identity side by side.

Add a GitHub / GitHub Enterprise account

  1. In Settings → Accounts, choose Add account.
  2. Pick GitHub.com or GitHub Enterprise and complete the browser sign-in.
  3. The new identity appears in the accounts list. You can add another on the same host without signing the first one out.

Each account keeps its own tabs, repositories, and settings. Switching the active account switches the whole workspace to that identity's context.

The Active chip and Make active action in Settings → Accounts use the same one-account-global rule as the rail switcher, including across GitHub.com, Enterprise, GitLab, and Bitbucket sections. A repository with an explicit Repository account binding remains on that exact identity until a user deliberately promotes another GitHub identity on the same API endpoint while that repository is selected; the selected repository then follows the choice for its next authenticated operation. Other repositories, hosts, and providers keep their existing bindings.

The account picker and the navigation-rail account switcher are rich rows rather than login-only menus: they show the friendly name, @login · host, provider, plan, and a visible display email when one exists, with the active identity marked. Private email values are not searchable, and self-hosted accounts keep their self-hosted label. Their search bar is plain-text by default and offers fuzzy, substring, and bounded safe-regex modes through the anchored Regex Builder. Search by name, login, host, provider, plan, or visible email; use Arrow keys, Home/End, Enter, and Escape without leaving the surface. Invalid patterns are announced inline and cannot activate the first row; no-match states leave Add another account available. The searchable metadata never includes a credential token. Stable endpoint#id row identities also keep a login rename from selecting a different account.

Open the repositories side sheet to narrow cloned repositories by Repository account and Repository service. The filters combine: for example, choose one exact account and GitLab, or choose No available account and Local only. Signed-out/stale bindings remain explicit under Unknown or signed out; the app does not guess a provider from a hostname.

GitHub browser sign-in requests only the feature scopes used by Desktop Material: repository/user access, workflow-file updates, notifications, read-only organization membership, and GHCR package read/write for Cheap LFS. It does not request repository deletion, package deletion, administrative keys, Codespaces, audit-log access, or gists. An older sign-in without package access is offered reauthorization before GHCR storage can use that account.

Add a self-hosted GitLab account

Desktop Material signs in to self-hosted GitLab with an endpoint + personal access token (PAT) rather than a browser OAuth flow:

  1. In Settings → Accounts, choose Add account → GitLab (self-hosted).
  2. Enter your instance endpoint (for example https://gitlab.example.com).
  3. Paste a personal access token with the scopes you need (typically api, read_repository, write_repository).
  4. Save. The GitLab identity now behaves like any other account — its repos are browsable and cloneable, and it can own its own tabs.

Bitbucket and hosted GitLab integrations follow the same pattern. Tokens are stored with your platform credential store and are never exposed through the agent API.

GitLab and Bitbucket account controls

Browse organizations and publish into one

When a GitHub account belongs to organizations, the clone view loads the account's organization list and adds filter chips. Select an organization to browse its complete repository list; if one organization fails to load, the view reports that error without hiding the repositories already available.

The Publish repository dialog presents that ownership choice as a searchable listbox. None publishes to the personal account, while the other rows select an organization. The search supports fuzzy, substring, and bounded safe-regex modes plus the full Regex Builder. Invalid regex keeps every owner reachable and reports the pattern problem inline. Arrow keys, Home/End, and Enter/Space work from the keyboard. On narrow windows the search tools wrap above a list with its own scroll, so the owner rows and the dialog actions remain reachable.

Bilingual Publish repository dialog with a visible, searchable organization listbox

Assign an account to a repository

Repository-bound provider features use Repository settings → Repository account as their one source of truth. If Provider Triage opens while the repository is unassigned, Desktop Material automatically uses the account only when exactly one signed-in identity matches the provider and endpoint. With multiple matches, choose a labelled account and select Use this account; this prevents cross-account API calls. With no match, use Sign in or Manage accounts.

If the saved account has been signed out, lost permission, or needs organization SSO, the tool asks you to re-authenticate or authorize SSO instead of claiming the repository is unassigned. Changing the account in Repository Settings propagates to Provider Triage immediately; an existing valid explicit binding is never silently replaced.

The same repository account controls HTTPS Git network work: normal and scheduled Fetch, Pull, Push, the post-push refresh, pull-request refspec fetches, and remote default-branch lookup. A saved account that is unavailable fails safely rather than falling back to another GitHub.com identity. For an older unassigned repository, Desktop Material verifies same-host accounts against the remote and prefers an account that can push before a read-only account, then saves that verified choice for later operations.

When GitHub reports that the repository was renamed or transferred, Desktop Material updates the exact matched default remote before network work. It keeps SSH as SSH and accepts an HTTP(S) change only on the same exact scheme, host, and port. An explicit push URL moves only when it exactly matched the old fetch URL; a separate deployment or write-only target remains untouched. Concurrent remote edits win, unsafe candidates are refused, and a provider/config failure leaves the current URL in place for a later explicit retry. See Automatic remote URL refresh for the complete safety and retry behavior.

During background fetch, a local refs/remotes/<remote>/HEAD is reused only when it points inside that exact remote namespace and its target still exists. This avoids an expensive online default-branch scan on every scheduled refresh. Missing, malformed, dangling, or cross-remote refs still run one lookup with the repository's selected account. An explicit Fetch always refreshes the default with a five-second discovery deadline plus one final five-second process-cleanup grace window, so a generic host's rename is detected even when its old branch still exists and a missing child-process close event cannot hang the completed fetch. Concurrent preparations for the same remote URL share one in-flight system proxy lookup instead of multiplying resolver work after a timeout. Clone cancellation remains stricter and waits for the owned process to close completely.

Transfer a repository to another account

Open Repository → Transfer repository…, choose Transfer repository from the repository-list context menu or Command Palette, or use the button in Repository settings → Remote. The dialog starts with the source repository and offers every signed-in GitHub identity. Choose Sign in to another account… when the destination identity is not present; the normal GitHub.com or GitHub Enterprise sign-in flow returns to this dialog without exposing the credential to the page.

Choose a personal or organization owner, keep the existing repository name or enter a custom provider-safe name, and choose whether the destination is private. Full history creates a temporary bare clone and pushes every local branch and tag. Clean state creates one new root commit from the current files, pushes the current branch, and keeps the previous tip in a local refs/desktop-material/transfer-backups/ recovery ref. Both modes require a valid Git operation state; full-history mode requires a clean worktree, while clean-state mode intentionally includes the current Git-visible changes. Both show real progress, verify the destination branch, and only then retarget origin; the original source remains available as upstream when the repository did not already have one.

The final review names the destination, mode, privacy, and remote change. Two independent confirmations plus the full-range authorization slider are required. If provider creation or publication succeeds but local retargeting fails, the dialog says that the destination may already exist and leaves the old remote in place when possible. See the repository transfer feature article for recovery, security, and verification details.


App-hosted browser

Published source acceptance: combined tests, verifier contracts, full TypeScript, the exact Windows production build, and hidden-desktop interaction/privacy checks pass. The source and accepted capture are pushed through 2abccae8fd, and Pages/wiki publication is verified live. Packaged Windows E2E is verified. Installer/Release evidence remained pending at that dated checkpoint; archived TUI compatibility work does not block this Windows feature.

Open Settings → Advanced → Open web links and choose:

The selection persists, follows profile settings restore, and applies to later links. An explicitly saved internal-browser choice remains selected. Authentication is marked explicitly by the app; Desktop Material does not guess from a hostname or URL path.

If Windows rejects an HTTP(S) launch, Desktop Material shows one non-blocking, detail-free notice whether the link came from app content, the native Help menu, or the app-hosted browser's external escape. It never includes the failed URL and never silently falls back to the app-hosted browser.

The app-hosted window provides browser tabs, New tab, close, Back, Forward, Refresh/Stop, a labelled address field, Go, ordinary bookmarks, and Open externally. A bare host such as example.com/docs becomes HTTPS. Arbitrary words are not sent to a search engine. HTTP(S) redirects stay in the current tab; a page's window.open target is captured into a new app-hosted tab instead of receiving an unrestricted popup. Use the toolbar search button or Ctrl+F to search the active page. Plain text is the default and highlights through Chromium; regex is an explicit opt-in with the shared regex builder, case matching, previous/next navigation, and bounded context results. Regex reads capped page text in an isolated world and does not mutate remote page DOM, so it reports matches without pretending to highlight them.

Sign-in tabs show a SIGN IN marker and private-session notice. They cannot be bookmarked, use an in-memory session shared only with their sign-in popups, clear its storage/cache after the authentication browser closes, and always offer Continue in system browser. A valid Desktop Material callback returns to the app. Ordinary bookmarks persist, but their query strings and fragments are removed first so OAuth codes and signed parameters do not land in bookmark storage. Open tabs and their current URLs are not restored.

Remote pages are not part of the trusted Desktop Material renderer. Each lives in a sandboxed WebContentsView with Node and preload disabled, context isolation and web security enabled, permissions denied, downloads blocked, and certificate failures refused. A blocked download explains that the page must be opened externally. Load, certificate, download, and renderer failures appear in the browser chrome with a refresh or external-browser recovery path.

香港粵語速讀。 Settings → Advanced → Open web links 預設兼建議用系統 瀏覽器,特別係 app 內瀏覽器一片空白嗰陣;真係想用先明確揀 app 入面。App 入面有分頁、網址列、前後頁、重新整理、Go、書籤同外部逃生門;網頁 自己就鎖喺 sandbox view,冇 Node、冇 app IPC、冇權限,壞憑證唔會夾硬放行。登入 分頁係記憶體工作階段、加唔到書籤,閂咗會清資料,亦永遠可以轉去系統瀏覽器。撳工具列搵嘢掣或者 Ctrl+F 就可以搵目前頁面;預設係純文字高亮,亦可以揀 regex 建構器、大小寫、上一個/下一個同配對摘要。 呢 本機正式 build 同 hidden-desktop 驗收已經過關;遠端發佈證據另外計。

See App-hosted browser for persistence limits, complete failure behavior, security boundaries, and the pending acceptance gates.


Local Ollama model management

Open Settings → Copilot → Providers, choose Add provider…, select Ollama (local), and save the preset. Its default URL is http://127.0.0.1:11434/v1; Ollama runs locally without an API key. Choose Manage models on the saved provider to open the lifecycle workspace.

The manager keeps service discovery, inventory, and mutations explicit:

  1. Check the endpoint health and Ollama version. Installed and running models load independently, so a partial response does not erase usable data.
  2. Search all installed models or filter to running models. While a query is present, Clear search removes it without changing the selected filter mode or case-sensitivity setting. Select one to inspect bounded size, digest, family, format, parameter, quantization, capability, license, and runtime details when Ollama reports them.
  3. Pull a model by name and follow streamed progress; cancel only that pull if needed. Copy a model, or rename it through copy-then-delete with a visible partial result if the original cannot be removed.
  4. Load or unload the selected model. Delete requires inline confirmation that names the exact model and warns that the action cannot be undone.

After an inventory-changing operation, Desktop Material synchronizes the installed Ollama names back to that provider's selectable Copilot models. The installed inventory is authoritative, while settings for still-matching model identifiers are retained. A successful Ollama request followed by a failed provider-settings update is reported as a split outcome rather than a complete success.

Only HTTP or HTTPS loopback endpoints (localhost, 127.0.0.1, or [::1]) are accepted. The saved provider path must be exactly /v1; the manager derives that loopback origin and appends only fixed native /api/* routes. Every remote host, arbitrary prefix, saved /api base, embedded credential, query string, and URL fragment is rejected. Stale requests are aborted when the provider changes, response text stays bounded, and credentials are never added to management URLs or logs.

Every label, validation message, progress announcement, confirmation, and accessible name follows the selected English, playful Hong Kong Cantonese, or English / 香港粵語 mode. The manager is keyboard reachable and reflows for compact Preferences windows.

The accepted 1452×1001 off-screen scene exercised health, inventory, search, running-state filtering, pull cancellation and rollback, completed pull, copy, rename, load, unload, confirmed deletion, and provider synchronization. It uses synthetic fixture data, passes privacy inspection, and has no overlapping or horizontally overflowing manager controls.

Ollama model manager with endpoint health, installed and running inventory, details, and lifecycle controls


Repository tabs

The workspace is browser-like and tabbed. Each tab is bound to a repository and an account, so a tab always opens in the identity that owns it.

Open a tab

Tabs persist per account — reopening the app restores that account's tabs.

Auto-detect repositories in a folder

Open File → Add local repository… and choose Auto-detect repositories…. Pick a parent folder rather than one repository. Desktop Material performs a bounded scan that does not follow symbolic links or junctions, skips generated/dependency folders, and stops descending when it finds a repository. Review the relative paths in the result, then add all discovered repositories in one step. You can still edit the path and use the normal single-repository flow at any time.

Rename a tab

Double-click a tab's title (or use its context menu → Rename) to edit the label inline. The rename is local to the tab and does not touch the repository name on disk or on the remote.

Style a tab's title

Open a tab's context menu → Text style to open the styling popover. Per tab you can set:

The picker keeps a short recent-colors row for reuse. Styling is per tab, so you can give a production repository a strong background while leaving a scratch repository muted and italic to tell them apart at a glance.

Tab layout and styling are part of your per-account settings, so every change auto-commits to that account's local settings repo. Open Edit → Settings History… or press Ctrl+Alt+Z if you ever need to inspect, undo, redo, or restore an earlier state.

Word-style tab appearance editor with typography, alignment, and independent text and background palettes

Group tabs

Right-click any repository tab and choose Add tab to new group… to name a group and choose one of six curated colors. The group appears as a visible chip before its first member, with its name, member count, color, active state, and a chevron. Select the chip—or focus it and press Enter/Space—to collapse its real member tabs and select it again to restore them.

The same tab menu moves a tab into an existing group, removes it, expands or collapses the group, or deletes only the organizational label. Deleting a group never closes its repositories. Groups stay contiguous and cannot cross the protected pinned/unpinned boundary; pin or unpin the tab before moving it to a group on the other side. Names, membership, colors, collapse state, and future compatible metadata persist per profile/window across open, close, bulk-close, reload, history restore, and session import. All group copy and announcements follow English, playful Hong Kong-style Cantonese, or bilingual mode.

Dragging or using a named move action within a group's run keeps membership; moving a member outside the run ungroups only that tab. One-shot label, opened-time, status, and favorite arrangements keep every named group together as a stable block.

Restart-restored named tab-group chip with its visible repository member

Close matching tabs safely

The tab-strip menu keeps both directions explicit:

Pinned tabs are protected in both bulk-close directions. Unpin one explicitly before including it in a bulk close.

Pin and arrange tabs

Use the star control or context menu to mark a tab as a Favorite, and use Pin tab when it must remain in the leading group. Open Arrange tabs to:

Each sort is a one-time edit: later repository-status changes do not reshuffle the strip. The saved order remains manually editable and restores with the account/window tab state. Pin or unpin a tab explicitly before moving it across the group boundary.

Use the strip's Search tabs button to find and switch to an open repository by its visible name, alias, local path, or clone URL. Arrange tabs has a separate Filter tabs field for narrowing the manual-order rows; the one-shot sort buttons still apply to every open tab and say so explicitly.

Arrange tabs surface with pinned and manual movement controls plus one-shot label, opened-date, and repository-status sorts

Runtime repository-tab search matching an active repository by name and path

Export or import the current tabs

Choose File → Export current tabs… to save a portable JSON description of the open tab order, active tab, aliases, pins, favorites, and per-tab appearance. The file includes local repository paths but never account tokens or credentials. Profile-local group definitions and each tab's groupId are intentionally omitted, because membership without its destination profile's definition would dangle. Choose File → Import current tabs… to validate a file, preview it, then replace the current tabs or merge with them; missing folders are skipped without destructively clearing a usable current session, and the destination profile's existing groups remain intact.


Command palette

Press Ctrl+Shift+P to search every named app function. Ctrl+Shift+F opens the current repository in its folder. The palette covers the whole app (Material 3's full-screen search view): results on the left, a detail pane on the right naming what the highlighted command does and where it lives, and a footer with the match count and keyboard hints. Each comfortable row shows a leading Material Symbol, localized title, a compact home label ("Toolbar", "Settings › Appearance"), optional search-term line, and a localized Navigate, Repository, Branch, Changes, Edit, or App chip. Compact density fits more commands by suppressing the secondary line.

Rows that are settings carry their live control inline — a switch for a boolean, a text box for free text, a numeric box for a bounded number, a select for a choice — so the value can be read and changed without leaving the palette. Clicking any row (or pressing Enter) teleports to the control that owns the feature: the owning surface opens, the control scrolls into view, gets a brief spotlight ring, and receives focus. Ctrl+Enter or the row's Run button executes the command instead; destructive and network commands (push, force push, discard…) never fire from a teleport.

Use Customize appearance beside the filter and regex controls to choose comfortable or compact rows and independently show/hide icons, group chips, and the keyword line. The compact, left-aligned editor also offers Random per repository: each repository gets one stable derived layout that survives restarts instead of reshuffling every time the palette opens. Manual controls remain visible but disabled while that mode is active. Changes apply immediately and persist for later palette sessions; malformed stored values repair field by field. Escape closes only the anchored appearance editor and returns focus to its toggle, leaving the palette open.

The palette shell, groups, appearance controls, accessibility names, and the Ollama model manager, Copilot/provider preferences, and background queue entries follow English, playful Hong Kong-style Cantonese, or bilingual mode. English fallback titles and localized group names both remain searchable.

Full-app command palette showing matched commands with their live settings controls beside the compact aligned row appearance editor


Appearance customization

Ordinary preferences and visual owners

Settings → Appearance contains ordinary preferences only: explicit English, Hong Kong Cantonese, or bilingual language mode; theme and scale; repository-list behavior; branch sorting; formatting; and diff tab size. Visual customization belongs to the thing being changed.

Shift+right-click an actual element—or focus it and press the Context Menu key or Shift+F10—to open its editor beside that owner. Supported owners include the app identity/workspace, update progress bar, toolbar, repository list, repository tab strip, code/diff surface, each reviewed Material feature entry point, each repository name and logo, each tab title, and the temporary-submodule Back control. Ordinary right-click remains available to the native or component-specific Git context menu.

Each owner has one strict setting.json in its own local Git repository. The anchored editor shows and copies that exact path and opens the owner's History manager. Undo, redo, and restore append audit commits; they never reset or rewrite a successful timeline. Switching profiles switches the profile, feature, repository-element, and tab-element repository roots and closes stale editors.

Rapid slider, palette, and typography events are collapsed into the latest normalized owner value before one durable write and state update. A queued get() read, explicit flush, or History action remains an ordering barrier, and separately awaited changes keep their original sequence.

Scheduled language and appearance

Settings → Appearance → Scheduled settings adds local-time rules for the language mode, theme, and appearance values. Use the native date picker for optional date bounds, the native time picker for an exclusive end time, and either selected weekdays or Every day when only the time window matters. Equal start and end times mean the selected day in full; a window that crosses midnight continues into the following local day. Later active rules win only for fields they set.

The source can be a local value, a versioned HTTPS API document, or a Home Assistant boolean entity. API responses are bounded and allowlisted. A Home Assistant value is used only while the configured entity reports on; its access token is stored by the main process in the operating-system credential vault, never in the schedule export or profile JSON. Offline, invalid, and failed sources are skipped so the normal profile appearance remains usable.

See the detailed scheduled settings feature article for the response shape, persistence boundary, security limits, and verification contract.

The app identity editor covers the code-native logo and in-app name, geometry, color, typography, spacing, emphasis, and effects. It does not rename the signed executable or operating-system icon.

Profile-customized app identity restored in the Material workspace

Repository, logo, and tab owners

Shift+right-click the selected repository workspace, toolbar, or tab-strip background for its repository-specific values and an Edit profile default route. Shift+right-click the actual repository-list name for Word-style typography or its actual logo for the safe vector studio. You can also focus an owner and press the Context Menu key or Shift+F10. A repository can inherit the profile owner; Edit profile default keeps that profile editor anchored beside the same real logo.

If you would rather find everything in one place, Repository settings → Appearance lists the same five repository owners — list name, logo, tabs, toolbar, and workspace colors — using the very same editors. It is a hub, not a second copy: each section says whether the value is inherited or repository-owned, shows a live preview, offers Reset to default for that one owner, and commits straight to the owner's own local Git repository, so the History, undo, and restore you see there are the same ones the anchored editor shows. The hub never changes a profile default; for that, Shift+right-click the element itself, use its keyboard gesture, or use Settings.

The toolbar editor includes text color, curated font family, 10–20 px title size, bold, italic, underline, strikethrough, small caps, case, character spacing, text effects, alignment, and a live toolbar preview. Profile controls can return to the Material theme. A repository stores only the properties it changes, so individual controls can inherit the profile or Inherit profile can clear the complete local typography layer.

A local desktop-material.appearance-id UUID identifies the working copy across path moves. Each workspace, toolbar, tabs, list-name, and logo value still owns a separate local Git repository. The old aggregate desktop-material.appearance payload is read only as a migration/startup compatibility source, not as the mutable history.

The vector logo workbench provides:

Logo JSON is capped at 16 KiB, text and layer counts are bounded, and every value is normalized to the supported model. The studio never stores uploaded image bytes, HTML, or executable/raw SVG. Tabs and repository-list rows render only the app's code-generated SVG projection. Shift+right-click an actual tab title, use its keyboard context-menu gesture, or choose the tab command menu's Customize Appearance… action for its own typography/color editor, dedicated repository, and history; structural tab state remains separate. Desktop Material initializes the clicked title before opening its editor. If an account/profile transition is still rebuilding that title owner, the editor stays closed and a localized status asks you to try again instead of taking down the app window.

Layered custom repository-logo studio with a live preview and safe vector controls

Appearance editor anchored beside its actual owner with History, a dedicated local Git path, and burst-safe persistence


Settings history

Desktop Material records ordinary per-account preferences and structural tab state in the account settings repository. Open Edit → Settings History… or press Ctrl+Alt+Z for that non-modal timeline. Visual owners deliberately do not share it: use the History action in an anchored appearance editor for that element's narrower Git repository.

Live Settings history side sheet


Non-modal dialogs

Desktop Material's dialogs are non-modal floating surfaces — the main window stays fully interactive while a dialog is open.

Preferences is the reference surface: an MD3 940×660 dialog with browser-style horizontal tabs, an Active chip on the current section, and a pill footer. The same shared tab surface is used by Repository settings and Stash Manager, so opening a new page, closing a page, finding overflow, and moving with Left, Right, Home, or End behaves consistently across all three dialogs. The open-page session is local to each surface; filtering the strip does not close hidden pages, and stale stored page IDs are discarded safely. The repository and branch pickers open as MD3 side sheets rather than blocking modals. The repository sheet keeps Add and Select visible, while More holds group creation, workspace sync, and commit/push-all so all five actions stay available without wrapping into a cluttered multi-line block. Its Filters disclosure folds account, service, status, search, and regex controls together; active values remain applied and are counted on the pill while the panel is collapsed.

Global Settings as an MD3 dialog with browser-style tabs, close actions, search, and overflow

Repository Settings with the shared browser-style tabs and Remote page selected

The Settings and Repository Settings tab rails are dockable from the visible Settings tab position control. Choose Left, Top, Bottom, or Right; left is the default, and Preferences and Repository Settings remember their choices separately. Top and bottom use a horizontal scrollable strip, while left and right keep the vertical strip and its matching arrow keys. Invalid or missing local values safely return to the left rail. See the detailed settings tab docking article for the storage keys and verification contract.

Preferences as an MD3 dialog

Dark repository side sheet with collapsed Filters and compact Add, Select, and More actions

Branch navigation and status side sheet


Power workflows

Multi-clone

The multi-clone window clones many repositories in one pass.

  1. Open Clone → Multiple repositories (or the multi-clone entry in the repositories menu).
  2. The list shows every repository available to the active account. Use the search bar — with filter chips, regex mode, and the regex builder (see the Regex Guide) — to narrow it down.
  3. Use org filter chips to limit the list to a specific organization.
  4. Tick the checkboxes for the repositories you want.
  5. Choose the clone mode:
    • Parallel — clone all selected repos at once (fast; heavier on network/disk).
    • One-by-one — clone sequentially (gentler; easier to watch progress and spot failures).
  6. Start the clone. Progress is shown per repository.

Optional post-clone Actions runner

For one selected private GitHub or GitHub Enterprise repository, expand Runner provisioning before cloning, opt in, confirm that its workflow authors are trusted, and choose Windows or Linux via WSL. Desktop Material completes the clone first, then creates a repository-scoped runner without an elevated prompt. Public repositories, multi-clone queues, and background auto-clones cannot create runners. A setup failure leaves the clone available and directs you to the repository's Actions tab for recovery.

Because a runner executes workflow code as your Windows user, remove it from the Actions tab before changing its repository to public. The app rechecks privacy before setup and before a stopped runner can restart.

Changing the account clears repository selections from the previous identity before loading the new account's list. If a provider refresh fails, use Try again in the same view; a stale repository cannot remain selected for cloning under the replacement account.

Safe RE2 builder with bounded live matching and capture previews

On a compact or zoomed viewport, the builder stacks its category and building-block areas before cards clip. Its body scrolls vertically while the live tester and footer actions remain reachable; the dialog does not require page-level horizontal scrolling.

Export / import repo lists

Background auto-clone

Open Settings → Clone queue to manage the policy after the Clone dialog closes. Each signed-in hosted account has its own card: choose an absolute base directory, select Parallel — up to 3 at once or Sequential — one at a time, then enable Automatically clone new repositories. The GitHub clone view can configure the same policy. Desktop Material records the current provider list as a baseline; it does not immediately clone every existing repository. Repositories discovered after that baseline are queued into the chosen directory in the background.

Discovery continues for the app lifetime after the Clone window closes. It refreshes periodically, does not open a progress dialog on its own, and posts a notification when a background queue starts or when refresh needs attention. The policy is account-specific and rejects invalid directories, oversized provider lists, duplicate/unsafe URLs, and URLs containing embedded credentials. Changing the directory or clone mode while the switch is on updates that account immediately. Turning it off stops new automatic batches without cancelling a clone that is already running.

Pause, resume, and crash recovery

The batch progress surface can be hidden while cloning continues. Choose Pause remaining to stop new queue items from starting; clones already running finish, and Resume safely continues the pending work. Cancel remaining marks work that has not started as skipped, while Retry failed starts only failed items again.

Queue transitions are durably journaled. If the renderer or app closes during a clone, the next launch restores the queue in a paused state and labels formerly running rows interrupted. Resume then inspects each destination before invoking Git again:

Recovery never deletes or moves destination contents. The bounded journal stores queue metadata and stable account references, never provider tokens or credential-bearing clone URLs.


Guided Git and GitHub functions

Desktop Material turns useful Git, GitHub CLI, and GitHub API capabilities into named, task-specific workflows. You choose an action, complete a focused form, review any destructive or worktree-changing step, and receive the result in the app. Expert API integration is kept in the separate repository-contextual Explorer below; it does not turn the guided Git workflows into a raw command console.

GitHub API Explorer

Open API in the repository rail, or Repository tools → API functions, to work against the GitHub host and account explicitly bound to the selected repository. The Explorer never falls back to another identity on the same host. Eligible repositories receive a small set of safe read functions automatically, and each appears as a runnable button.

Choose Hide API tab to remove the rail item for this repository. Show API tab in the API functions tool restores it. The full REST/GraphQL operation catalog and manual request builder are available only after choosing Add or edit an API function.

Save API requests as app functions

The App functions panel turns a validated REST catalog request or named GraphQL operation into a reusable extension of Desktop Material. Enter a lowercase function name and description, then choose Add current request as function. Each saved function includes a generated argument schema and can be run, edited from the current request, or removed in the same panel.

Functions follow the active profile and appear only for the exact repository, remote, GitHub host, and account binding used when they were created. Read functions can run from the panel or through the local Agent API as github_api_<function-name> tools. A write or destructive function must run through the API tab's visible mutation-review flow; an agent cannot bypass that confirmation. Credentials are neither accepted in a function template nor stored with one.

Named repository-bound API app functions with reviewed execution

Repository-contextual GitHub API Explorer with a searchable operation catalog, REST request builder, and bounded redacted response

Shallow clone

Open File → Clone repository… → URL when you need only recent history:

  1. Enter the repository URL and local path.
  2. Enable Shallow clone.
  3. Set Commit depth to the number of commits to fetch.
  4. Review the summary, then choose Clone.

The form explains that it fetches the current branch and recursive submodules. If you need older history later, use the named deepen-history action in Repository tools.

Shallow clone with a commit-depth control

Deepen shallow history

Open the Tools rail, find History depth, and choose Check history status. When Git reports a shallow boundary, choose Review bounded deepen to fetch a specific number of older commits or Review full history to remove the boundary deliberately. The review names the selected remote, scope, and consequence before the bundled Git runtime starts. Progress is cancellable, and the app rechecks the marker when the fetch completes.

The production fixture began with 3 visible commits and finished with all 15; the clean screenshot below shows the final state. The raw verification receipt is retained in the P0 run manifest.

Repository Tools showing full history after a verified deepen

Repository Tools owns its vertical scroll region. At normal, minimum, short-height, and 150% zoom layouts, scrolling reaches the exact final results surface without moving the whole document or clipping controls below the viewport.

Short Repository Tools workspace scrolled to its reachable final results surface

Sparse checkout

Open Repository → Manage sparse checkout… to keep selected directories in a large worktree. The side panel reports whether sparse checkout is enabled and guides each change through Choose/Adjust/Restore → Review selection → Apply and refresh:

  1. Choose/Adjust/Restore one repository-relative directory root per line. Slashes are normalized, while absolute paths, traversal, option-like input, control characters, blanks, duplicates, and over-limit selections are rejected. State-aware guidance says whether the selection is empty, needs correction, or is ready for review.
  2. Review selection freezes the normalized input before Git changes the worktree. The bounded review shows every selected root. When cone mode is already enabled, it also reports added, removed, and unchanged selection entries; these counts describe directory-root entries rather than predicting individual local files.
  3. Apply and refresh the reviewed operation. Cancellation remains available while Git is changing the worktree. Success, cancellation, or failure stays on the result phase until you edit the selection or request a manual refresh.

The three-step guide remains visible above the scrolling editor and review body. On a compact window, its steps stack inside the sheet instead of covering content or forcing sideways scrolling.

Choose Review enable only after the valid-directory count and exact normalized review match your intent. Reapply and disable have their own review confirmations; disabling restores the full tracked working tree without changing commits or history. The verified disabled state below leaves all working-tree paths eligible to appear locally.

Guided sparse-checkout sheet in its disabled Choose state with the persistent three-step guide and directory editor

Guided sparse-checkout sheet with Review active, the editor locked, and the exact normalized docs selection

These forms wrap labels and stack actions as space narrows. Page-level sideways scrolling is not part of the workflow; only inherently spatial content such as code, diffs, and logs may scroll horizontally when preserving columns is necessary.

Preview an ordinary pull

Right-click the primary Pull remote button in the toolbar, or choose Pull from the application menu, when the current branch has incoming work. A plain left click on the toolbar button pulls immediately without opening the review. For the reviewed path, Desktop Material fetches the configured remote first; if that fetch fails, the review stops instead of displaying an older remote-tracking ref as fresh data.

The review identifies the local and upstream branches and their captured object IDs, shows ahead/behind counts and the effective fast-forward, merge, rebase, merge-preserving rebase, interactive-rebase, or blocked fast-forward-only route, and lists up to 25 incoming commits and 100 incoming changed files. The file list describes the upstream side from the merge base, so local-only files do not appear as incoming. Overflow counts keep larger pulls explicit.

The initial fetch has no safe cancellation path, so the modal cannot be closed with a button, Escape, or the backdrop while loading. Cancel appears after preparation finishes and dismisses only the displayed review.

Pull reviewed commit is available only for a completely clean worktree. After confirmation, Desktop Material refreshes state and revalidates the local branch, upstream ref, and both full OIDs. It integrates the exact reviewed upstream OID already fetched, without a second network fetch; if either side changed, the stale review is cleared until Refresh preview succeeds. The modal review remains open while Git starts and runs, preventing an in-app branch switch from changing the reviewed pull destination.

The sheet follows English, playful Hong Kong-style Cantonese, or bilingual language mode. It applies to ordinary manual pull actions only. Scheduled Automatically pull runs and explicitly noninteractive local-agent pull commands keep their direct automation paths and safety checks, while Pull All and batch sync retain their own review and result surfaces.

Reviewed ordinary Git pull with exact branch identities, incoming commits, changed files, and a clean-worktree confirmation gate

Rebase the current branch

Commit checkpoints moving in order onto a newer main-line history

Open Branch → Rebase current branch…, then search for and select the target/base branch. The review shows the current→target relationship, ahead/behind context, and a bounded preview of commits that would be replayed.

Before Git starts, Desktop Material refreshes repository state and blocks unresolved conflicts, dirty changes, and another ongoing operation. It also revalidates the exact current and target refs, so a stale branch picker cannot launch a different rebase. You can cancel while that preflight is running. If Git reports conflicts after start, resolve them through the existing continue/abort flow. Protected branches receive explicit guidance, and the app never force-pushes automatically; review any later force-with-lease decision separately.

Reviewed current-branch rebase showing current to target, ahead and behind counts, and a bounded commit preview

Create a pull request

Open Branch → Create pull request while the head branch is checked out:

  1. Confirm the exact target repository and signed-in account.
  2. Choose the base branch; the current local branch is the fixed head.
  3. Enter a title and optional Markdown description, and choose whether to create a draft.
  4. Select Review pull request and verify repository, account, base/head, title, body, and draft state.
  5. Select Create pull request. The success receipt offers Done and Open on GitHub.

The app rejects ambiguous remote syntax and routes account problems to repository settings. The workflow does not expose gh pr create, editable arguments, or a raw REST request.

Native pull-request creation success with wrapped content

Inspect effective branch rules

Open Repository → Inspect branch rules…. The non-modal sheet combines classic protection and rulesets for the checked-out branch into plain-language sections for reviews, checks, deployments, merge queue, verified signatures, linear history, update/delete/force policy, bypass context, and source rulesets. Use Refresh to load the same exact branch again.

If no matching account is signed in, Open account settings is shown. If more than one account matches a legacy repository, Open repository settings opens the real repository-account picker; saving one records its stable endpoint#id identity. Unknown or partial policy evidence is stated instead of guessed.

Effective branch rules with long checks and policy details wrapped


One-click commit & push

A short-lived feature branch passing review and merging into the stable line

For quick, low-ceremony commits, use one-click commit & push:

  1. Stage the changes you want (or commit everything shown in Changes).
  2. Press the one-click commit & push action.
  3. Copilot writes the commit message from your staged diff.
  4. Desktop Material commits and pushes in one guided operation. A large ordinary selection can produce several proven commit/push batches.

This is a convenience path built on the normal commit machinery — you can always fall back to writing the message yourself in the Changes view. For scheduled, unattended commits and pushes, see Automation.

When many ordinary small files approach a decimal 1.5 GB (1,500,000,000-byte) push, even the normal Commit action automatically becomes a commit-and-push sequence. Desktop Material uses a conservative 1.4 GB changed-blob budget, reserves the rest for Git/path overhead, and separately bounds path count and proof output. It keeps stable file order, commits only the next group, records a durable pending ref, performs a normal fast-forward push, proves that exact commit at the remote tip, and only then creates the next commit. A failed or unproven push stops before another local commit exists; after fixing the remote or authentication issue, retry Commit or Push and the pending batch is finished first. This is an app safety policy rather than a claimed universal Git-host pack-size limit.

Push also checks unpushed history made by an older Desktop Material. Safe existing commits keep their IDs, authors, timestamps, messages, and signatures, and are pushed and proven one at a time before new working-tree batches begin. An already pending current batch resumes without entering that rewriter. An individually oversized commit can be rebuilt automatically only on a linear, clean, local-only branch with an exact configured or resolved destination and no operation in progress. A compare-and-swap backup ref protects the original tip; Desktop Material never force-pushes or rolls back a batch already proven remote. Rebuilt oversized history preserves the reviewed message and final tree, but has new commit IDs, does not preserve cryptographic commit signatures, and does not promise the original author timestamp on each replacement batch. Desktop Material proves the final tree and every replacement path's mode/object ID. Commit commands disable automatic Git packing only for that process with -c gc.auto=0. If Git creates the exact verified commit and only later reports a maintenance failure, the app keeps the commit and warns once instead of duplicating it.

When a selected file is larger than GitHub's ordinary 100 MiB object limit and the configured Cheap LFS backend is available, every commit entry point prepares it before invoking Git. The compact terminal below Commit reports up to three sanitized worker rows, active and queued counts, hashing/preparation/upload/verification phases, selected/recommended providers and the recommendation reason, per-file and aggregate bytes, observed elapsed time, renderer-measured throughput and ETA, honest manual-handoff phases, and success/failure counts. Long recommendations use a native keyboard-focusable disclosure; 100% appears only after the provider accepts and the app verifies the object. A setting under Repository settings → Build & Run switches between sequential operation and at most three transfers. A failed raw file stays selected for the next try, while unrelated safe changes and successful pointers can commit. Use the Large files Changes filter to isolate files over the same threshold.

Every failure says why. Each settled failure adds its own row to the terminal naming the file and the reason — including the provider's HTTP status where there is one — and the same reason is repeated in the "Some large files were not pinned" notification, so a run can never end on a bare pinned 0 · failed 10. Provider text is trimmed and scrubbed of URLs and credentials before it is shown.

On the GitHub published prerelease backend a release tag has to point at a commit GitHub already has. If the current branch has never been pushed, Desktop Material publishes that branch first and re-reads the remote to confirm it landed, then uploads. If it genuinely cannot — no GitHub repository, no push remote, a detached HEAD, or a branch with no commits yet — it stops before uploading anything and names that exact reason on every affected file instead of retrying. Publish the repository or branch, then commit again.

Changes sidebar with the Large files filter and a three-worker Cheap LFS terminal showing queue, provider, reason, timing, ETA, and manual controls

The same settings page chooses GitHub published prerelease, GHCR · one OCI image, or Docker Hub · one OCI image. The terminal recommends ordinary Git, Releases, GHCR, or Docker Hub from the selected byte total and detected local setup without changing your choice. A configured credential does not prove live quota, billing, organization policy, or service health. The Git repository stores only the pointer; the original bytes live in Release assets or registry layers.

Before committing a private registry pointer, Desktop Material proves its exact tracked shared key. An unsafe selected path remains blocked unless a fresh, repository-bound Git status proves that the exact Windows-hostile legacy path is currently deleted. The exception is deletion-only: a current nondeleted unsafe path, an absent or mismatched status proof, and a real OCI pointer under a control-plane path all remain fail-closed.

Desktop Material first uses a bounded exact-length gh api upload when GitHub CLI exists in its trusted Program Files location. This avoids opening Electron's native upload pipe, which can crash the app if its remote data-pipe consumer closes during a write. The app uses the selected account and host, streams and hashes only the validated file range, passes the token through an isolated temporary child environment rather than the command line, and tears down the CLI on cancel or app quit. Before uploading again, it scans all ten bounded Release pages once; if an exact-name object exists, later checks poll only its ID. A completed exact-size/digest asset is reused, while a persistent starter asset fails closed so it cannot be overwritten or mistaken for success.

If the trusted CLI is unavailable, Desktop Material retains Electron's memory-bounded chunked compatibility transport. Uploads on either transport run with no stall or runtime timeout — a slow connection can take as long as it needs, and a transfer ends only on completion, a transport failure, or your explicit Cancel; choose the manual flow below if it cannot complete.

For explicit recovery, choose Manual upload beside the progress controls. Desktop Material stops that attempt and places all remaining files into one temporary upload-these-files folder. Whole-file assets use verified same-volume hardlinks or bounded copies; the browser folder never contains symlinks or zero-byte helpers. Files above the Release limit become ordered .partNNN range copies made with a bounded buffer. Every staged file is rechecked as a regular file with its exact expected size before Explorer opens it. The commit button shows live preparation percentage while the app hashes and stages those bytes. It checks the temporary volume before the worst-case handoff copy and reports insufficient space instead of filling the disk. It opens the exact selected Release edit/upload page first, then puts that folder in front: select all prepared files, drag them onto GitHub's asset drop zone, and let the browser finish the upload (then choose its save/update action if shown). A retry verifies and reuses exact prior parts, starts progress with their completed bytes, and stages only missing names; digest-less prior parts receive a bounded download/hash check. Incomplete starter objects remain blocking and the Release editor can be used to wait for or delete them. The app freshly fences every exact asset ID before pointer writes, rechecks every source, writes the pointers, and resumes the same commit automatically. Older GitHub Enterprise versions safely fall back to the repository Releases listing. Cancel stops either path until the verified pointer commit begins; that short final mutation phase finishes as one reviewed operation. The app waits for and verifies every multipart asset before writing the pointer. A file fitting the 500 MiB new-write cap initially uses one raw asset, while a larger file uses ordered raw ranges of at most 500 MiB each. Legacy pointers with parts up to 2 GiB remain readable. Public repositories then automatically receive one owned, SHA-pinned workflow file in Changes for review. Compression starts only after you commit and push that caller. Private repositories stay off until you explicitly enable Cloud compression in the Large files manager or Repository Settings. That persisted opt-in installs no workflow in the private repository and spends none of its Actions minutes; compression is routed through the encrypted public builder, whose external registration must be completed before it runs. Unknown visibility stays off.

The GitHub Action downloads and raw-DEFLATEs one Release object at a time, uses no Actions artifacts or caches, and adopts a verified side asset only when it is strictly smaller. A failed or non-beneficial object keeps its exact raw pointer and asset, so it remains cloneable; other objects continue and a multipart pointer may safely be mixed. Raw assets remain available for older commits. GitHub Actions never decompresses. On clone, pull, user-requested fetch, open, or Materialize, Desktop Material downloads compressed bytes and expands them on your local PC under the pointer's exact output cap, then verifies every original part and the complete file before replacing the pointer. To refresh an older clone that still contains only pointer text, update Desktop Material and reopen the repository with Download large files after cloning enabled, or choose Large files → Materialize all. Explicitly public GitHub.com Release pointers can restore while signed out using read-only anonymous requests with no Authorization header. Private, unknown-visibility, and GitHub Enterprise Release pointers still require the repository-selected account; anonymous Release mutations are never allowed.

Exact-90% two-lane restore progress

Published source acceptance: combined tests, verifier contracts, TypeScript, the exact production build, and hidden-desktop acceptance pass. The source and accepted capture are pushed through 2abccae8fd, and Pages/wiki publication is verified live. Packaged Windows E2E is verified. Installer/Release evidence remained pending at that dated checkpoint; archived TUI compatibility work does not block this Windows feature.

For Release-backed pointers, one restore batch now shares a coordinator with a hard limit of two HTTP downloads. The current file or multipart part owns the first lane. When the provider reports that transfer at 90% or greater, the next file or part may start in the look-ahead lane: 89.9% stays single-lane, while exactly 90% opens it. If a provider has no usable progress total, the next item waits for the current transfer to settle.

The detailed restore panel shows overall progress and separate Current and Next at 90% lanes, plus repository, provider, current phase, file and part ordinals, logical restored bytes, actual downloaded/compressed bytes when known, succeeded/failed/remaining/queued counts, elapsed time, download rate, ETA, bounded per-file failures, and cancellation. Downloading, decrypting, decompressing, verifying, materializing, and canceling remain distinct instead of sharing one ambiguous percentage. For an encrypted-and-compressed Release object, the truthful transform order is Downloading → Decrypting → Decompressing → Verifying → Materializing. Plain bilingual copy renders the second phase as Decrypting · 解密緊; playfulness may change the voice but never the phase, path, byte counts, or order.

The second lane does not weaken validation. Desktop Material still verifies every part's size and SHA-256, assembles parts in pointer order, verifies the whole file, and replaces only the unchanged pointer. Cancellation aborts queued and active work, drains both lanes, cleans owned temporary files, and leaves unverified pointers in place. Visible counters update exactly; screen-reader announcements are grouped into meaningful 10% transitions, reduced-motion mode removes active shimmer, and bilingual/narrow layouts wrap instead of clipping.

香港粵語速讀。 下載真係到 90%,下一個檔或者 part 先可以入第二條 lane;成個 batch 最多兩個下載。面板會分目前/下一條 lane、檔同 part 次序、邏輯 同實際位元組、階段、速度、ETA、排隊、成功、失敗同取消。加密兼壓縮嘅檔案會照實 顯示「下載 → 解密 → 解壓 → 驗證 → 落盤」,雙語係 Decrypting · 解密緊。預取只係慳中間嗰幾秒,每 part 同全檔 SHA-256、大小、指標 冇變先換檔,全部照舊要過關。

New buckets are published prereleases, so collaborators can fetch them while they remain outside the stable installer /releases/latest feed. For compatibility, older draft discovery is bounded to 100 pages of 100 releases because GitHub's direct tag lookup does not expose drafts. Desktop Material publishes a revalidated legacy draft before a new pin only when the exact managed-bucket sentinel or a valid legacy asset label proves its Cheap LFS provenance. The compression Action accepts only a published managed prerelease; it refuses a draft, stable Release, or unrelated same-tag prerelease before download or upload and leaves the raw pointer unchanged. The same safe fallback applies when the Release has reached GitHub's 1,000-asset capacity and therefore has no free slot for a compressed side asset. Cheap LFS retains the raw historical object instead of deleting it to force compression.

Live public automatic and private explicit-opt-in runs each reduced the 1 MiB acceptance object to a verified 1,033-byte side asset. Both compressed pointers were restored manually through Desktop Material to the exact original SHA-256; an earlier failed draft lookup also left its raw pointer materializable through the same production UI.

Live 14.8 GB Bambu build acceptance

The public codingmachineedge/bambu-build exercise used the real Changes and Clone UI for exactly 8,305 payload files and 14,809,588,162 bytes. Desktop Material created four ordered commit/push batches below its conservative limit. The first ordinary push received HTTP 408 but kept the exact pending commit durable; retry pushed that same immutable SHA before creating the next batch.

Managed cloud run 30048474438 compressed each of the 13 Release objects one at a time and reported 13 compressed, 0 kept raw, and 0 failed. All 13 raw originals remain beside the 13 compressed assets, so older pointers retain raw fallback. The final real-UI manifest commit 712ad85 passed verifier run 30054805137, which checked all 8,305 files, ten pointers, and 26 assets and published immutable manifest Release bambu-build-verify-30054805137.

A fresh real-UI clone restored all ten logical SHA-256 values while the Git commit still held only 370–514-byte pointer blobs. Its first explicit Materialize all overlapped clone/open automatic restoration and produced two hash-identical compare-and-swap recovery copies. The bytes remained correct, but Desktop Material now serializes automatic and manual materialization per repository; HANDOFF.md records the corrected UI acceptance separately from that initial integrity proof. Canceling Materialize all now cancels every batch queued for that repository — including automatic restores enqueued by a concurrent fetch or pull — so a canceled download cannot silently restart, the panel reloads the pinned-file list after a cancel, and a batch that finishes with failures reports how many files were left as pointers instead of claiming unconditional success.

Live public Bambu build Cheap LFS inventory with ten tracked Release-backed pointer objects

Each Cheap LFS Release holds at most 1,000 assets. Desktop Material counts all ten asset pages and uses assets, then assets-2, assets-3, and later buckets as needed. A multipart file or one manual batch always stays together; if the current bucket lacks enough slots, every asset in that group moves to the next Release and its pointer stores the exact tag. Assets still marked starter by GitHub reserve a slot but remain unavailable until GitHub reports them uploaded. New asset buckets are published prereleases, so they cannot replace the stable installer update feed.

GHCR and Docker Hub image storage

Registry storage keeps the repository's complete current object set in one logical <source-name>-cheap-lfs image. One stable tag identifies the newest snapshot, but committed pointers always name an immutable manifest digest. One current snapshot is explicitly bounded to 4,096 objects, 8,192 layers, and 8 MiB each for its canonical config and manifest JSON; a larger structural plan fails before publish rather than pretending that "one image" is unbounded. Adding or removing files cannot append to that digest: Desktop Material creates a new manifest, reuses every unchanged content-addressed blob, uploads only new layers, verifies the result, and moves the tag. Pointer-form files move to that new digest; verified materialized raws stay in the working tree with their valid older pointer metadata. Each digest also receives a deterministic desktop-material-cheap-lfs-sha256-<64hex> retention tag before the stable tag moves. Historical retention tags are not deleted, so older manifests remain referenced for mixed current generations and older Git commits instead of depending on a registry to preserve untagged data.

New layers start at 1.5 GiB. GHCR documents a 10 GB per-layer maximum and a ten-minute upload timeout, so Desktop Material bounds each ORAS operation below that deadline. A timed-out layer is not resumable or mutable; the app rebuilds that object's layers at half the previous size, down to 8 MiB, and safely re-encounters already accepted blobs by digest. Docker Hub publishes no general hard layer-size or upload-time cap that Desktop Material can encode. Its current plan, pull, storage, abuse, and fair-use limits still apply, and the app uses no more than three concurrent transfers for either provider. Docker's current pull-rate table lists 100 pulls per six hours for unauthenticated users, 200 for authenticated Personal users, and unlimited pulls for authenticated Pro, Team, and Business users, subject to fair use.

For a verified-private source repository, every registry chunk is encrypted with AES-256-GCM. Desktop Material intentionally commits the shared key at .desktop-material/cheap-lfs-registry-key-v1 so collaborators who can read the private Git repository can decrypt the image. This protects against a registry- only leak; it does not protect against anyone who can read the repository, an old clone, fork, backup, or Git history. Do not make that history public or discard an old key while immutable historical pointers still need it. New private pointers bind their key with key-id sha256:...; Desktop Material force-includes and proves the required tracked key even when an ignore rule or selection checkbox would otherwise omit it.

GHCR publishing uses the selected GitHub.com account. Docker Hub publishing uses the signed-in Docker Desktop credential helper; only a first publish defaults to that account namespace. Later same-provider updates keep the exact package already named by the pointers, including an organization/collaborator namespace. Changing providers requires every old pointer to be an exact, unedited materialized raw; the app re-hashes local bytes and publishes a fresh full snapshot without pulling from or deleting the old provider. Tokens are passed to the trusted bundled ORAS process through standard input, not command arguments. Public images restore anonymously. Private images need the matching provider credential and tracked key. A first public GHCR package is refused before upload because GitHub creates it private and has no supported visibility-change API; choose published Releases or Docker Hub, or first make an existing exact-linked GHCR package public in GitHub package settings. A first private GHCR push also waits for GitHub to report that exact source link; if it does not, the app leaves the stable tag and pointers unchanged so you can link the package in settings and retry. Its verified digest-specific retention tag remains, allowing immutable blobs to be reused safely.

GitHub's OAuth scope reference says write:packages grants package upload and download, but its Container registry page separately says Packages supports PAT classic only. Desktop Material requests and validates write:packages, and its selected OAuth token passed a non- mutating GHCR challenge; no live package mutation was part of that acceptance. A provider rejection therefore fails closed before the stable tag or pointers move rather than claiming PAT-classic compatibility.

Windows installers ship the digest-pinned ORAS 1.3.2 executable and its verified Apache-2.0 license. The ARM64 installer currently carries the same audited x64 ORAS binary as the x64 installer and depends on Windows 11 x64 emulation; if it cannot start, registry storage fails closed and published Release storage remains available.

The compatibility Electron path reads even a multi-gigabyte part incrementally instead of retaining one in-process request body.

Open the repository rail's Large files destination to manage the pointers directly. It lists and searches original repository-relative paths, pins reviewed files, and materializes one or all pointer files without requiring you to find their backing assets in GitHub Releases or a registry. Each row identifies published Release, GHCR, or Docker Hub storage. Release rows also show Raw, Compressed, or Mixed and the public/private cloud policy. OCI rows offer Remove from image, which publishes a survivor-only snapshot and updates the other pointers before the path is removed. The managed Release workflow is added to Changes for your review rather than committed silently. Materialize all reuses one paginated inventory per Release instead of repeating the same API pages for every pointer. Choose Open Cheap LFS settings to jump straight to Repository settings → Cheap LFS, the dedicated tab (right after Build & run) that holds the storage provider, auto-pin, auto-download, parallel-upload, and cloud-compression preferences. The Large files page owns the repository view's vertical scroll, so every row and action in a long inventory remains reachable without a competing nested page scroll.

The prepared folder is flat because GitHub Release assets cannot contain subfolders. Cheap LFS still remembers every original repository-relative path: files in nested folders return to those exact paths, and duplicate basenames receive distinct hash-suffixed asset names. The flat names are reserved case-insensitively, so names that differ only by Windows casing remain distinct too.


Build & Run output controls

Open Build & Run and start the selected project profile. The log panel keeps three output navigation/display choices in its header:

Auto-scroll and truncation persist locally when the panel or app is reopened. Their pressed states and accessible names follow English, playful Hong Kong Cantonese, and bilingual language mode.


Repair a failed build with Codex or OpenCode

When Build & Run fails, choose Fix with Codex or Fix with opencode. The launch sheet lets you switch between the two local CLIs; that choice is saved for this repository and is also available under Repository settings → Build & Run. Send to Codex/OpenCode uses the same selector for a bounded free-form repository request.

Nothing starts when the button merely appears. If the selected CLI is absent, Desktop Material shows its exact npm install command and waits for consent. If it is not signed in, open a terminal and run the displayed codex login or opencode auth login command. Never paste a key, password, or token into the dialog.

Auto-approve is a separate per-run choice and defaults off. With it off, an agent can stop when an action needs approval. With it on, Codex still retains its repository workspace-write sandbox and OpenCode retains its repository permission block. Both agents stream bounded output to Build & Run, and Stop terminates the owned process tree and prevents the verification rerun from starting. A selected nested project folder is included in the bounded prompt, while the repository root remains the actual process and sandbox root.

Unless you cancelled it, Desktop Material starts Build & Run again after the agent exits. Trust the rerun result—not an agent's message or exit code—to decide whether the repair worked. Review Changes after cancellation or a failed rerun because partial edits may remain. A trusted repository's .codex/config.toml can still enable MCP servers, so review project Codex configuration before using repair; Desktop Material does not claim to isolate project MCPs.


Notification centre

The bell in the app chrome opens the notification centre side panel. Its two named views keep local app events separate from GitHub inbox items:

The account, filters, and empty state are part of the guided inbox workflow rather than a gh command or GitHub API search screen.

Filtered Local notifications selected for bulk triage

GitHub notification view with the no-signed-in-account state

Error presentation

Acknowledgement-only failures appear as dismissible red notices in the bottom-right corner by default, without blocking the task underneath. A notice can expose bounded diagnostic details when they differ from the user-facing message. In Settings → Notifications, choose either Bottom-right notice or Blocking dialog for these acknowledgement-only errors. Failures that need a retry, authentication choice, external remediation, or another real decision remain dialogs regardless of that preference.

When Git reports the affected repository's exact stale .git/index.lock, the notice includes Remove lock file. Desktop first confirms that its own repository operations are idle, then refuses recent, linked, non-file, or changed locks before atomically quarantining and removing the verified stale file. Retry the original Git operation after the notice closes.

Bottom-right Git lock error notice with Remove lock file recovery


GitHub Actions panel

The Actions panel brings CI into the app:

Material workflow-run cancellation review naming the exact run, ref, actor, and commit

Actions artifact with digest match and attestation-presence context

Actions cache manager with usage totals, refs, wrapped keys, and delete controls

Actions caches can be listed and deleted, but GitHub exposes no supported API for downloading a cache archive. Download unavailable says so directly; use a workflow artifact when output must be downloadable and integrity-reported.

Headless Actions run pagination with the page-two sentinel retained

Headless Actions artifact inventory with bounded pagination

Headless Actions sentinel evidence with wrapped content and no clipping

Actions workflow-run pagination with 51 filtered runs retained after Refresh

Actions artifact page two with a deliberately long wrapping sentinel name

Attempt-aware Actions job pagination with an exact recovered page-two job

Pending Actions deployment environments with long reviewer and protection details

The historical M0–M19 production gates exercised 50→51 filtered runs, 30→31 artifacts, and current/historical 50→51 job pages, including a deliberate later-page 503→200 retry. Exact job log/re-run, deployment review, and fork approval mutations ran only against the isolated provider. At regular and short windows plus a requested 200% base with auto-fit, document and body widths matched and the measured Actions surfaces had no clipping, overlap, outside controls, oversized text, or page-level sideways scrolling. Modal focus and scrim ownership were also contained. These are named app controls; there is no gh command, API-path, or GraphQL editor.

Job-log downloads follow GitHub's signed redirect automatically. Desktop Material's main-process request filter tracks the original request and removes authentication, authorization, and cookie headers before any cross-origin hop. Download errors also omit signed URLs and query strings. The live Windows x64 proof below shows the resulting searchable, collapsible log viewer.

Windows x64 GitHub Actions job log loaded securely in the searchable in-app viewer

If GitHub is still preparing a completed job's archive, the log endpoint can temporarily return HTTP 404. The viewer now retries that API response with a bounded 250/750/1,500 ms schedule and obtains a fresh signed redirect on each attempt. When the bound is exhausted, it explains the provider state and keeps Retry and Open on GitHub available; it never sends the API bearer to the cross-origin log host.

Actions job-log recovery state with Retry and Open on GitHub

Actions job log loaded after the provider archive becomes available


Repository Releases

Open Releases from the repository rail to work with the selected GitHub repository without losing its account context. The dashboard summarizes the releases currently loaded, including stable, prerelease, and draft counts, combined asset/download totals, and the latest stable release. The desktop catalog uses a wider 420–560 px pane and larger rows and controls; below 900 px it stacks above the detail pane instead of squeezing text. At the corrected 800×560 combined small-width/short-height gate, the header and metrics compact, the list moves ahead of overview/ detail content, and one complete release row stays visible at 125% (768×528 CSS), 150% (640×440 CSS), and 200% (480×330 CSS) in one 960×660 physical window. The compact layout keeps 9–16 px text, 30–34 px controls, a 176 px tools panel, and at least a 52 px release row instead of shrinking labels to 7–8 px. Metrics reflow into three columns, and the saved English, playful Hong Kong Cantonese, or bilingual tools label can wrap without clipping.

Releases dashboard with status summary, searchable catalog, selected metadata, and assets

The accepted compact image is the 200% frame from the successful 100%, 125%, 150%, and 200% constant-physical-size gate. It is a 960×660 physical capture of the CSS 480×330 viewport with zero horizontal overflow and one complete row.

Compact Repository Releases at 200 percent scale with one complete row and the keyboard-reachable Filters and selection disclosure

Repository Packages

Switch the repository's Distribution surface from Releases to Packages to browse npm, Maven, RubyGems, Docker, NuGet, and container packages. Desktop Material first refreshes the repository's numeric GitHub ID and excludes every owner package without that exact association. Package and version lists support ordinary, fuzzy, substring, and regular-expression search, case sensitivity, and the full Regex Builder.

On GitHub.com, Publish a file package reviews one local file and publishes it as an app-owned GHCR/OCI artifact under a new unique tag, then reports the immutable digest reference. Download file accepts only an exact digest and only after verifying the app artifact type, source repository, one safe title, and the layer's size and SHA-256; the final save never overwrites an existing path. Other ecosystems and general container images remain metadata/link workflows for their normal registry clients. See the GitHub Packages explorer contract for limits, failure recovery, and credential handling.


UI scaling

Desktop Material scales its whole interface independently of the OS:

Combined with the animated light/dark theme, this lets you tune the workspace for a laptop panel, a 4K monitor, or a shared screen without touching system display settings.

The responsive toolbar gate measures the space its controls actually need. As the window narrows, Build & Run enters More toolbar actions first and Commit & Push enters next; the repository, worktree, branch, and sync controls remain available in the app bar. Opening More keeps the moved actions keyboard reachable. After the surface closes and the window widens, Commit & Push and then Build & Run return to their original positions, with no page-level horizontal overflow.

The Pages gallery also has a dedicated accessibility/clipping check at 960×660 and 390×844. It passes with zero axe violations, matching document/body widths, and no horizontally outside elements.

Requested 200 percent UI scale auto-fitted to 96 percent at the minimum window size

Responsive regression proof at 1450 by 997 with toolbar and Changes controls fully contained and no horizontal overflow

Measured narrow toolbar with its complete More actions surface


Automation and merge-all

Two conflicting file streams reconciled into one verified result

Open Settings → Automation to configure the two background schedules:

  1. Turn on Automatically commit and push and/or Automatically pull.
  2. Pick an interval for each enabled operation.
  3. Under Account overrides, let an identity inherit the global value or override its enabled state and interval.
  4. For one repository, open Repository settings → Automation and inherit or override the two schedules again.

Automation targets the selected repository only. Before each run it checks repository state, upstream availability, conflicts, in-progress Git operations, and draft commit text. An unsafe repository is skipped rather than overwritten. See Automation for the complete guard table.

Scheduled Git is non-interactive. It uses an already selected/stored credential or fails through a notification without opening a login, credential-manager, SSH, or pinentry window. Scheduled commit, pull, and push skip repository hooks; automatic commits and pull-created commits also disable signing, and post-push SSH deployment uses batch mode. Manual Git actions remain interactive and continue to honor normal hook and signing choices. Repositories that require hooks or signatures should use reviewed manual operations and remote branch protection.

The Branches and Worktrees views also expose Merge all branches and Merge all worktrees. Confirm the target, follow each row's progress, and review any skipped or failed target. When Copilot conflict assistance is available, it participates inside the same guarded workflow.

When the current branch is main, choose Choose a branch to merge into main from the Branches menu to open the merge sheet. Its Not updated with main chip filters by Git ancestry: a branch remains visible when its tip does not contain the current main tip, while a diverged branch that already contains main stays out of the result. The chip uses the same English, Hong Kong-style Cantonese, and bilingual language modes as the rest of the app and composes with text and regex filtering. If Git cannot resolve the default tip, the chip is withheld and the ordinary branch list remains available.

Merge into main chooser with Not updated with main active

Automation preferences with global and account overrides

Merge all branches with per-target progress


History search and graph

Open History and use the search field to match a commit's title, message, tag, or hash. The results retain the normal commit detail view. Toggle Show commit graph to add ancestry lanes and merge edges beside the unfiltered list; turn the graph off when a compact list is more useful. For readable three-column ancestry work, choose Graph in the repository navigation rail: it is a dedicated full-width page with the same scope, filters, lane controls, selection, and commit actions.

Right-click a commit row for reset, checkout, reorder, revert, branch, tag, cherry-pick, copy, and provider actions. The row's named More actions button, the Context Menu key, and Shift+F10 open the same action set. Invoking an unselected commit targets only that row; invoking a member of the current multi-selection preserves the selection for eligible multi-commit actions. Hovering or keyboard-focusing a commit also shows its exact authored timestamp and an auto-updating relative line such as 2 minutes ago.

History search and commit graph

History also exposes the two view modes as explicit, keyboard-accessible tabs: choose Commit list for the dense review list or Graph for the continuous branch lanes. The selected view persists with the repository tab, and arrow, Home, and End keys move through the tab strip.

History Commit list tab from the built Windows app

History Graph tab from the built Windows app

History commit row with its named More actions control and hover hint

History commit hover card showing an exact timestamp and relative age


Multiple stashes

Create a stash from the Changes workflow whenever work must be set aside. Desktop Material keeps all stash entries instead of treating only the newest one as available; the inventory has no Desktop entry-count cap:

  1. Expand Stashes in Changes and select an entry by its label.
  2. Inspect that stash's file list and individual diffs before acting.
  3. Choose apply or pop for the exact entry, or review rename, create-branch, and delete actions.
  4. If Git completes only part of an operation, keep the reported state visible and inspect the repository before retrying instead of assuming an all-or-nothing result.

Switching branches can still offer to stash local work, and the resulting entry appears in the same list.

Stash Manager with browser-style Manage, Export, History, and Appearance and voice pages

The full manager is rendered through the shared dialog layer, keeping every tab and export control centered and usable above the Changes pane. The hidden Windows capture below is the accepted 1443×992 runtime proof.

Centered stash manager dialog with Manage, Export, History, and Appearance and voice tabs

Choose Open full manager for the complete workflow. Its browser-style tabs keep Manage, Export, History, and Appearance and voice as independently closable pages; use the plus and overflow actions to reopen a page without losing the current stash review. Its tabs provide a regex-capable search and multi-selection export to a directory, ZIP, or 7z. The 7z panel exposes compression method, level, dictionary, match finder, fast bytes, solid mode, threads, split volumes, password, and encrypted headers. The History tab keeps exact object IDs visible for recovery review, while Appearance and voice exposes the shared language mode and both independent funny-level sliders before routing to full appearance settings.


Repository power tools

At compact heights, the Repository Tools workspace itself scrolls vertically so the Diagnostics section, results, and later actions remain reachable without a horizontal page scrollbar.

SSH and non-authentication failures never start account fallback. The selected stable account key stays in the app's internal trampoline map; its selector field is removed from the Git options before spawn and never enters child, hook, Git LFS, or log environments. A missing same-origin selection fails closed, while credential requests from cross-origin submodules retain normal account resolution. Successful fallback uses the neutral result Pull completed using another signed-in account.

Pull all completing with another signed-in account without exposing its identity

Named Repository Tools administration hub

Reviewed named-remote administration

Clone-style Add Submodule review with a synthetic URL, checkout path, and tracked branch

Initialized submodule opened temporarily with a context bar and Back control to the persisted root repository

Reviewed gitignore template catalogue


Multi-window workflows

Right-click a repository and choose Open in new window. Worktree context menus offer Open Worktree in New Window as well. Each window maintains its own selected repository and repository tabs, and native/menu/CLI actions route to the correct window. Closing and reopening the app restores the persisted window tab state.

Open a repository or worktree in another window


Agent access and CLI

Open Settings → Agent access and turn on Enable local agent server. The panel shows the random loopback address, MCP URL, and bearer token; reveal/copy the token only for a trusted local client, and use Regenerate token to disconnect existing clients immediately.

For the phone-first site, select Paired LAN devices, start the server, and choose Open mobile connection page in the Mobile connection card. Desktop Material replaces any old pairing code and opens a fresh five-minute one-use /connect link in the default browser. The secret stays in the URL fragment, so it is not sent to the site server, and the mobile page removes it from browser history before exchange. Selecting the button again invalidates the previous code. In Local-only or stopped-server state, the card remains visible and explains which prerequisite is missing instead of opening an unusable link.

Set the remote-site address and optional HTTPS gateway in the same panel before pairing. Direct LAN HTTP authenticates but does not encrypt traffic; use a trusted private network or configured HTTPS gateway. A browser-open failure reports a generic error without copying the pairing secret into logs.

The contract covers account/repository/tab discovery, repository status, single/batch clone, commit, fetch/pull/push, branch creation/merge, tab selection, automation status/runs, Actions workflow dispatch, and the active profile's repository-bound named API read functions. It never returns provider credentials. See Agent API for command and security details.

Agent access connection and token controls


Next: Automation · Submodules · Regex Guide · Developer Guide