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

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.
- The shell
- Historical Linux TUI prototype
- Install on Windows
- Material first run
- Signing in
- App-hosted browser
- Local Ollama model management
- Repository tabs
- Command palette
- Appearance customization
- Scheduled language and appearance
- Settings history
- Non-modal dialogs
- Multi-clone
- Advanced Git and collaboration workflows
- Guided Git and GitHub functions
- GitHub API Explorer
- One-click commit & push
- Build & Run output controls
- Repair a failed build with Codex or OpenCode
- Notification centre
- GitHub Actions panel
- Repository Releases
- Repository Packages
- UI scaling
- Automation and merge-all
- History search and graph
- Multiple stashes
- Repository power tools
- Multi-window workflows
- Agent access and CLI
Advanced Git and collaboration workflows
M21 adds progressively disclosed controls around the familiar Desktop flow:
- Use the repository sheet to search, filter by account/provider/status, pin or hide repositories, and run Pull/Fetch All only after reviewing the exact selected subset. History can switch between the current branch and all refs to reveal commits that exist only on remote branches or tags.
- Open a pull request workspace to inspect its summary, checks, changed-file tree, and expanded diff context; comment and reply, resolve conversations, submit an approval or change request, and update supported metadata. The creation composer discovers bounded repository templates and optional metadata before an immutable final review. Fork checkout separately reviews the exact source repository, branch, and commit before fetching.
- Open Stash Manager to create a named stash from selected files or manage any stash visible to Git, including entries made outside the app. Repository Tools exposes tag creation, fetch, push, move, signing, pruning, and deletion; destructive tag and bulk-branch actions identify the exact refs first and retain recovery information.
- When switching away from a dirty branch, choose Leave my changes here to keep the current files in place and open Add worktree with the destination branch and a suggested worktree name already filled in. The current worktree is not stashed or checked out to the destination; creation happens only after the path review succeeds.
- In Changes, switch the file list to a directory tree, choose persisted diff context, compare CSV/TSV rows and cells, and preview TGA images. Editor actions understand the expanded editor catalog plus WSL paths. Settings and Repository Tools expose global-ignore editing, allowlisted custom Git presets, reviewed patch import/export, and network/WSL path diagnostics.
- Open GitHub Projects from Repository Tools for the current account-bound repository. A successful bounded load refreshes the local cache; while offline, the workspace labels and shows only the last-known-good snapshot.
The complete 30-item feature ledger links to behavior, recovery, security, and test details for every workflow.

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-shellWindows 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-shellClose 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 --shortOpen/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-ExpressionThe command loads the tracked installer script from this repository. The script then:
- queries
Ding-Ding-Projects/desktop-materialfor its newest non-draft, non-prerelease GitHub release; - requires exactly one installer matching the native Windows architecture and validates that its HTTPS URL belongs to this repository;
- checks the reported byte count and GitHub SHA-256 release-asset digest, then requires any Authenticode signature to be valid;
- runs the per-user Squirrel installer with its supported
--silentflag; - requires Squirrel to exit zero and verifies the installed postcondition; and
- 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 CurrentUserInstall 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.

目前原始碼開發版畫面已驗收:真 Electron/Squirrel 路徑用驗證器自有、已披露嘅無害 payload;呢個係 UI 路徑證明,唔係話個 payload 已經發佈。
The immutable frame below remains the separate installed legacy-to-shared-lane migration record.

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:
- A left icon navigation rail with entries for Changes (with a count badge), History, Branches, Settings, and your account avatar at the bottom.
- A floating pill toolbar across the top carrying repository, worktree, branch, and sync controls. When the measured width gets tight, Build & Run moves into the keyboard-accessible More toolbar actions surface first, then Commit & Push follows. Widening the window restores each action automatically before its label can clip. For GitHub-backed repositories, the branch control also shows a small colour-coded CI logo for the current commit; hover it for the result, such as CI checks: successful.
- Browser-like repository tabs (see Repository tabs) above the workspace.
- Floating, radius-24 workspace cards for Changes, the diff, History, and the empty/welcome states, with tri-state selection checkboxes, tonal status chips, token-based diff colors, and an inverse-surface undo banner.
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.


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.

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:
- a voice provided over the network will not speak while you are offline, and says so;
- a voice you chose that has since been uninstalled is reported as missing — your choice is kept in case it comes back, and the narrator falls back to the closest match meanwhile;
- if your computer has no voice at all for a language, the picker says that rather than claiming it will choose one automatically.
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
- In Settings → Accounts, choose Add account.
- Pick GitHub.com or GitHub Enterprise and complete the browser sign-in.
- 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:
- In Settings → Accounts, choose Add account → GitLab (self-hosted).
- Enter your instance endpoint (for example
https://gitlab.example.com). - Paste a personal access token with the scopes you
need (typically
api,read_repository,write_repository). - 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.

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.

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:
- In the system browser for the default, recommended behavior, especially when the app-hosted browser renders a blank page; or
- Inside Desktop Material to intentionally send browser-bound HTTP(S) links to the dedicated app-hosted window.
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:
- Check the endpoint health and Ollama version. Installed and running models load independently, so a partial response does not erase usable data.
- 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.
- 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.
- 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.

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
- Click + on the tab strip to open a new tab, then pick a repository, or
- Middle-click / use the context menu on a repository in the list to open it in a new tab, or
- Drag one or more local repository folders onto the app. An existing repository switches instantly; a new valid repository is added and opened as 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:
- Weight — bold on/off
- Italic and underline
- Size
- Text color — choose from the palette or use a custom color
- Background color — choose independently from the same palette or use a custom color
- Font family
- Alignment
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+Zif you ever need to inspect, undo, redo, or restore an earlier state.

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.

Close matching tabs safely
The tab-strip menu keeps both directions explicit:
- Close Tabs Containing… uses the existing regular-expression workflow to close matching tabs.
- Close all tabs except those containing… keeps tabs whose visible label, repository alias/name, or local path contains a case-insensitive literal query. Review the live kept, closed, and pinned counts plus the bounded preview before confirming. Empty input and a query with zero matches cannot confirm, so this action cannot accidentally become close-all.
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:
- drag a tab within its current pin group;
- use the named Move left, Move right, Move first, and Move last keyboard actions;
- apply one-shot A to Z, Z to A, Newest opened, Oldest opened, Needs attention first, Clean first, Favorites first, or Favorites last ordering.
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.


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.

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.

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:
- Start from the repository-mark, monogram, or repository-name preset and watch the live preview.
- Choose a rounded square, circle, square, or hexagon; use a solid or gradient fill; then tune colors, gradient angle, border, and shadow.
- Compose up to eight mark and text layers. Reorder or remove layers and edit their mark/text source, font, weight, letter spacing, color, position, scale, rotation, and opacity.
- Use Undo and Redo while experimenting. A repository override can return to Inherit profile logo without changing the profile design.
- Export JSON… for a portable design and bounded version-1 Import JSON….
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.


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.
- Select a timeline entry to lazily load its changed files and diff.
- Choose Undo last to reverse the latest logical change, or Redo to replay an undone change.
- Use an entry's restore action to confirm and restore the complete settings state at that point.
- Choose Load more when the timeline contains more entries than the first page.
- Undo, redo, and restore all append audit commits, so the history itself is never rewritten.

Non-modal dialogs
Desktop Material's dialogs are non-modal floating surfaces — the main window stays fully interactive while a dialog is open.
- Drag a dialog by its header to reposition it, and keep working in the app behind it.
- Click or focus a dialog to bring it to front; multiple open dialogs cascade so you can see them all.
- Wheel or trackpad-scroll anywhere over dialog content; nested lists and editors consume their own range first, then the outer dialog continues at the nested edge.
- OS-native pickers (file open/save) stay native.
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.


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.



Power workflows
Multi-clone
The multi-clone window clones many repositories in one pass.
- Open Clone → Multiple repositories (or the multi-clone entry in the repositories menu).
- 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.
- Use org filter chips to limit the list to a specific organization.
- Tick the checkboxes for the repositories you want.
- 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).
- 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.

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
- Export writes the selected repositories to a list file containing portable clone URLs only — no tokens, credentials, local paths, query strings, or fragments.
- Import loads such a list back into the checkbox selection, so you can re-clone the same set on another machine or share a curated set with a teammate.
- Import rejects local paths and
file:URLs before any clone starts; the list is a clone recipe, not a local filesystem instruction. - After each imported clone finishes, Desktop Material runs the same Cheap LFS large-file restoration used by the normal clone path when Repository settings → Cheap LFS → Download large files after cloning is enabled (the default). The transfer file does not export account bindings or a manifest-bound file selection; those remain local so a shared list cannot carry credentials or stale restore proofs. If the setting is off or no eligible provider account is available, pointer files remain intact for a later restore.
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:
- an empty destination can be retried;
- a clean, non-bare worktree with a valid
HEADand the exact matchingoriginis accepted as the completed clone; and - an occupied, incomplete, linked, bare, tracked-modified, or differently bound destination is left untouched and marked for review.
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.
- Search the complete current catalog of 1,206 REST operations by method, path, summary, or operation ID, narrow it by category, or choose the New operations scope to see exactly the 10 operations added since the prior pinned 2026-03-10 catalog.
- Select a catalog result to populate its method and repository-aware path, or switch between the REST and GraphQL request builders for an explicit request.
- Read requests can run directly. REST write methods and GraphQL mutations first show a Review GitHub API mutation step with the exact account and request preview; they run only after Run reviewed request is confirmed.
- The response view exposes status and allowlisted diagnostic headers, bounds and truncates the displayed body, and recursively redacts credential-shaped values before rendering them.
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.


Shallow clone
Open File → Clone repository… → URL when you need only recent history:
- Enter the repository URL and local path.
- Enable Shallow clone.
- Set Commit depth to the number of commits to fetch.
- 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.

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 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.

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:
- 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.
- 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.
- 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.


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.

Rebase the current branch

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.

Create a pull request
Open Branch → Create pull request while the head branch is checked out:
- Confirm the exact target repository and signed-in account.
- Choose the base branch; the current local branch is the fixed head.
- Enter a title and optional Markdown description, and choose whether to create a draft.
- Select Review pull request and verify repository, account, base/head, title, body, and draft state.
- 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.

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.

One-click commit & push

For quick, low-ceremony commits, use one-click commit & push:
- Stage the changes you want (or commit everything shown in Changes).
- Press the one-click commit & push action.
- Copilot writes the commit message from your staged diff.
- 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.

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.

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:
- Scroll to bottom jumps to the newest line once and does not change another setting.
- Auto-scroll output starts enabled and follows incoming lines. Scrolling upward to read older output pauses and saves this setting; enable it again to jump back to the tail and resume following.
- Truncate long lines starts disabled. Enabling it displays each long line on one row with an ellipsis. It does not delete output: disabling it restores normal wrapping, and Copy all output always retains the complete line.
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:
- Local is backed by its own git repo. An unread badge shows how many notifications you have not seen. Search titles and messages, filter by notification type, select every visible result, and apply Mark read, Mark unread, or Delete selected as one history-backed change. Clear all opens an inline confirmation and the notification history can restore the removed entries later.
- GitHub uses an explicit account selector with All, Unread, and Participating only controls. Refresh follows every available 50-item API page automatically, so older entries no longer stop at 50 or require Load more. Search the complete fetched titles, repositories, types, and reasons; select all visible matches; then mark them read or done in bulk. Clear all names the exact fetched count and requires confirmation before marking the complete selected GitHub inbox done with bounded concurrency. Any failed threads stay visible for retry. Changing account, source, filter, or search safely cancels stale work and resets the scoped selection. When no account is signed in, the complete No signed-in accounts state remains visible and tells you to sign in before refreshing the inbox.
The account, filters, and empty state are part of the guided inbox
workflow rather than a gh command or GitHub API search
screen.


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.

GitHub Actions panel
The Actions panel brings CI into the app:
- Workflow runs for the current repository, newest first. When more are available, choose Load more runs; already loaded pages remain visible across background polling and Refresh.
- Filters by workflow, status, branch, and event.
- Re-run a whole run or re-run only the failed jobs.
- For a run that is queued, running, waiting, or pending, choose Cancel run. The Material review names the workflow and run number plus its branch/ref, actor, and commit when GitHub supplied them. Desktop Material revalidates the selected repository/account/run and live cancellable state immediately before one normal cancel request, disables duplicate submission, and refreshes until GitHub reports cancelled or another terminal state. Authentication/SSO and stale/conflict errors keep specific recovery guidance visible; force-cancel is not the primary action.
- Drill into a run, choose the current or a historical attempt, and use Load more jobs to append the next bounded 50-job page. A failed later page keeps the jobs already loaded and offers the same named retry. Every loaded job retains its exact View logs and Re-run job actions.
- Open the in-app log viewer to read output without leaving Desktop Material. Search the loaded log to isolate a command, warning, or error; only the spatial log body may pan horizontally.
- Inspect pending deployment environments and prior review history. Select only environments for which the signed-in account is eligible, enter a required bounded comment, review the exact approve/reject intent, and confirm. Locked environments keep their explanation visible.
- When GitHub marks a first-time fork run as eligible, use the separate Approve fork run confirmation. It is never inferred from a deployment decision.
- Trigger manual workflows with the
workflow_dispatchdialog — pick the workflow, ref, and inputs, and dispatch. - CI Windows uses GitHub-hosted runners for pushes,
pull requests, and reusable calls. A protected-main manual dispatch can
explicitly select
cloudor the exact[self-hosted, Windows, X64, desktop-material-windows-local]pool. - The repository's Super Express Release emergency
lane runs the complete script-contract suite before the Windows x64
production build/package, asset verification, and release. It still
omits unit, TUI, lint, type, parity, smoke, and packaged E2E tests. A
direct Windows
workflow_dispatchpublishes an immutable Windows Release marked Latest after preserving and verifying its artifact; the direct Linux TUI action and reusable packaging calls remain artifact-only so the combined dispatcher publishes one complete cross-platform Release. The combined dispatcher is self-hosted-only: preparation and publication use the registered Linux x64 WSL runner, the Windows lane uses[self-hosted, Windows, X64], and the TUI lane uses[self-hosted, Linux, X64]. A direct Windows dispatch keeps both package build and publication on[self-hosted, Windows, X64, desktop-material-windows-local]. Missing or busy packaging or publication runners queue or fail their release rather than falling back to a hosted runner. Ordinary CI and tested Express remain the default gates. Automatic CI stays on clean GitHub-hosted machines; the tested Express Windows jobs use the project-labelled self-hosted pool. Their release work keeps unique non-cancelling run/attempt groups. Only Super Express retains local packaging placement and same-ref cancellation. A release pull request targets the Windows product'smaindefault branch. Windows self-hosted dependency setup restores an exact verified cache without a post-job archive hook, explicitly saves a verified miss, and can use an older cache only as a warm start while the current lockfiles still drive installation. A cold publisher installs pinned checksum-verified PortableGit, GitHub CLI, andjqbelowRUNNER_TOOL_CACHE, then reuses them on later runs. The TUI's isolated profile-history repository also enables Gitcore.longpathslocally, so Windows history writes do not depend on the checkout's separate Git configuration. - Ordinary Windows unit tests leave
NODE_OPTIONStoscript/test.mjs, which owns both the per-worker heap and memory-aware concurrency. Packaged E2E waits at most 300 seconds for the Squirrel installer process, terminates that tree on timeout, and repeatedly stops only newly launched same-session application processes while preserving any process that predated the test. Remote proof of this hosted workflow repair is still pending. - Automatic and Super Express installers share one monotonic
zpackage-version namespace. Automatic Express releases begin non-latest, while Super Express publishers request Latest after verified assets are present. The greatest release for freshly revalidated currentmainremains the only release allowed to own the Squirrel update feed, so an older overlapping job cannot move Latest backward. - Select a run artifact to review its name, size, creation/expiry, workflow source, and GitHub digest. Search the loaded artifact catalog by name or workflow context with fuzzy, substring, or safe regular-expression matching; substring and regex modes can also match the digest, and the regex builder is available from the filter control. Choose Load more artifacts to append the next bounded page; search immediately includes each appended page. A failed later page keeps the cards you already loaded and lets you retry that same page. Download archive opens the native save picker; after transfer, Desktop computes SHA-256 locally, reports whether it matches, and offers Show in folder.
- The cache manager can list, search, and delete Actions caches, but Download unavailable is intentional: GitHub does not provide a supported Actions-cache archive download API. Download supported workflow output from that run's Artifacts section instead.
- Check attestations reports whether an attestation record is present. Presence is not presented as cryptographic verification: signer, signature, timestamp, source identity, and policy still need a future verification function.



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.







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.

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.


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.
- Search the loaded catalog with fuzzy, substring, or regular-expression matching, optionally case-sensitive, and combine it with the Published, Pre-release, or Draft status filter. The result count always says how many loaded releases are shown; Load more releases expands the catalog before filtering when GitHub reports another bounded page. In the compact gate, Filters and selection is a localized native keyboard-operable disclosure. Clearing a selection after a filter produces zero results returns focus to an enabled fallback rather than disabled select-all.
- Select a release to inspect its status, author, tag, target branch
or commit, creation and publish times, notes, asset count, and total
downloads. Row timestamps use unambiguous 24-hour
HH:mmtime. Open the exact provider release page when GitHub supplies a validated repository URL. - Asset cards show file type, size, upload dates, download count, and digest when available. Existing guarded actions still create or edit drafts, publish or delete a reviewed release, upload or delete an asset, and download an asset through bounded transfer and integrity checks. Only after the verified download completes, Open file appears beside Show in folder.
- Initial loading, asset loading, no-releases, no-filter-match, invalid-regex, and provider-error states remain distinct. A failed release or asset request names the failed operation and retries that same scope without discarding already loaded data.

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.

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:
- Open the scaling slider and set anywhere from 50% to 200%.
- Or choose auto-fit to window, which picks a scale that fits the current window size.
- Auto-fit treats your slider value as the requested maximum. If the window is too small, it caps the effective scale instead of multiplying the requested scale again. At the supported minimum window size, 200% auto-fits below that maximum so the title bar, navigation rail, Appearance cards, value, and footer remain visible without horizontal clipping. The latest P0 gate measured 94%; the older screenshot below records a 96% viewport.
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.



Automation and merge-all

Open Settings → Automation to configure the two background schedules:
- Turn on Automatically commit and push and/or Automatically pull.
- Pick an interval for each enabled operation.
- Under Account overrides, let an identity inherit the global value or override its enabled state and interval.
- 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.



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 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.




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:
- Expand Stashes in Changes and select an entry by its label.
- Inspect that stash's file list and individual diffs before acting.
- Choose apply or pop for the exact entry, or review rename, create-branch, and delete actions.
- 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.

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.

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
Open the repositories side sheet and choose Pull all to fetch/pull every eligible repository; review the per-repository result instead of assuming the batch succeeded as one unit. If the normal pull ends in an HTTPS authentication or repository-not-found ambiguity, Desktop Material can try the remaining token-bearing signed-in accounts whose HTML origin exactly matches that remote. A repository-bound identity is preferred within the otherwise stable account order.
Pin important repositories from their context menu, hide the automatically maintained Recent group if desired, and use grouping to keep large repository lists manageable.
Use branch presets/default-branch controls when creating or switching branches; pin, hide, solo, and restore branch visibility from branch context and filtered-state controls.
Set a repository-specific external editor when the global editor is not appropriate.
Manage every named remote in Repository settings → Remote, and administer add/move/rename/ lock/repair/remove/prune worktree operations from the Worktrees view. Remote names and URLs wrap only when genuinely long; before a field/control column becomes unreadable, each row changes to a single-column layout with fetch/push controls and actions in keyboard order.
In the same Remote settings page, save a non-secret SSH working-copy definition and keep its password or key passphrase in the operating-system credential vault. Turn on Deploy Docker Compose after pushes to this source remote to deploy only after Desktop Material successfully pushes that named remote. The SSH checkout must already be on the pushed branch; the app fetches that exact branch, requires a fast-forward merge, and then runs
docker compose up --detach --build. A mismatched branch or non-fast-forward update stops without a reset, force operation, or automatic checkout. Deploy Docker now runs the same bounded, output-redacted SSH recipe on demand. Host-key, passphrase, password, generic Git credential, and GitHub sign-in prompts share one FIFO, so concurrent prompts appear and settle one at a time.Open the wider Repository settings → Submodules surface and choose Add submodule… to browse GitHub.com, Enterprise, GitLab, or Bitbucket with the appropriate exact account, or enter an HTTPS, SSH, or local Git URL. Review the repository-relative checkout path and optional branch; Desktop rechecks duplicate/occupied destinations immediately before Git, reports bounded clone progress, and lets you cancel the running operation before refreshing the managed list. The same tab shows a Back preview:
Shift+right-click it (or focus it and press the Context Menu key orShift+F10) to open that element's appearance editor beside it. Changes remain staged with the rest of Repository Settings until Save. The adjacent Subtrees tab embeds the full add, pull, push, and split manager. The same managed list opens as the Submodule Manager from the Tools tab's Nested repositories category, and clone-list rows show a submodule badge whose details dialog can clone any submodule as its own repository. New to submodules? The beginner-friendly Submodules page walks the whole workflow in plain words and pictures.On any initialized Submodule Manager row or changed/new submodule commit card, choose Open temporary viewer to inspect that checked-out child read-only in the current workspace without importing it. It does not enter the repository list, Recent, or the persisted last selection. The context bar's Close viewer action and Back control both return to the saved root and clear temporary state; repeated Open, Close, or Back activation cannot create another tab or repository entry.
Shift+right-click the actual Back control—or focus it and use the Context Menu key orShift+F10—to open the same anchored editor and save its profile-wide style or label immediately. Uninitialized, stale, invalid-Git, traversal, sibling-prefix, and symlink/junction escape targets fail without changing repository persistence, and the manager stays available for recovery.Use the
.gitignoremanager and one-click Build & Run for project-aware cleanup and execution. Build & Run discovers common nested projects across Node, Deno, Rust, Go, .NET, Python, JVM, PHP, Ruby, Swift, Dart/Flutter, Elixir, Scala, Haskell, Zig, Make, and CMake; choose a profile by its displayed project folder when several projects share a language or toolchain.Open Repository tools for the full set of named, reviewed Git functions. Diagnostics cover the status summary, repository health check, commit-signature audit, branch sync overview, contributor summary, nearest-tag version description, whitespace/conflict-marker audit, an ignored-files preview, and a commit-notes view. Inspect and search adds Line authorship, which shows the commit, author, and date behind every line of one picked tracked file; Search tracked content, a bounded literal-text search across tracked files with file and line references that can optionally be scoped to one branch, tag, HEAD, or commit ID — a matchless search completes cleanly rather than reporting an error; and Edit commit notes, which saves, replaces, or removes the free-form Git note on one commit only after a dedicated review step that shows the exact commit and note text. Maintenance covers the maintenance preview and run, a fully-merged-branch audit, unreachable-object prune preview, and a two-step untracked cleanup: Preview untracked cleanup lists exactly what would be deleted, and Remove untracked files deletes it only after its own destructive confirmation (tracked and ignored files are always preserved). Recovery covers the reflog view and an unreachable-commit finder for locating work lost to a deleted branch or reset. Every function runs a fixed, reviewed Git recipe — there is no shell and no editable command line; the only accepted inputs are a picked in-repository file and one bounded line of literal search text.
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.






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.

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.
- HTTP-capable MCP clients connect to the displayed
/mcpURL with anAuthorization: Bearer …header. - Stdio-only clients run
node script/agent/mcp-stdio-proxy.js. - Scripts can start with
node script/agent/desktop-agent.js infoand use the fallback command-line client for the same bounded command contract.
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.

Next: Automation · Submodules · Regex Guide · Developer Guide