Skip to content

fix(ui): follow the UI scale and the selected language in canvas chrome, Settings and plugin surfaces - #149

Closed
BIackFIame wants to merge 4 commits into
howdeploy:mainfrom
BIackFIame:pr/ui-scale-and-ru
Closed

BIackFIame wants to merge 4 commits into
howdeploy:mainfrom
BIackFIame:pr/ui-scale-and-ru

Conversation

@BIackFIame

@BIackFIame BIackFIame commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Branch pr/ui-scale-and-ru (head b8c543bf, 4 commits), based on main ba427487.

Why

Your reviews kept coming back to the same three UI points: chrome must follow root --ui-scale, the text must be in the selected language, and it must be readable on Linux. While taking the Linux screenshots for the other PRs, we found places on main itself that miss these points. This PR fixes them, along with GitHub device sign-in, which could not be cancelled and showed raw English IPC errors in the Russian UI. It adds no new surfaces and no settings.

What changes for users

  • UI scale: Settings → Appearance → UI scale (0.85–1.25) now also reaches these parts:

    • the terminal card's options menu;
    • the canvas shortcut-hints panel;
    • the whole plugin card in Settings → Plugins (title, author, permissions, contribution rows, buttons). Before, only its description scaled.

    At 1.0 nothing changes.

  • Russian text: the canvas hints, the wheel/pinch settings and the canvas navigation description are fully Russian. Long labels in Settings → Keyboard shortcuts wrap instead of being cut off.

  • Key caps: the hints panel shows them like the shortcut reference ("Alt+↑", not "Alt+ArrowUp"). This also fixes the reference's ",nd+Comma" on macOS.

  • GitHub sign-in:

    • a started device sign-in has a Cancel button;
    • denied, expired and failed outcomes are reported with a localized line in the Settings error style;
    • transient errors back off, and five in a row end the flow;
    • raw IPC errors go to the log only.
  • Plugins:

    • plugin pages, HOME widgets and canvas apps receive appearance.uiScale, live;
    • a manifest may localize the plugin name, contribution titles and launch field labels (name.ru/title.ru/label.ru and .en), shown in the app's language;
    • the "may allow" help line in Settings → Agents uses the normal help style instead of monospace.

Commits

  1. 0496428c fix(ui): scale the card menu, hints panel and plugin settings with the UI scale
  2. 894af84b fix(i18n): finish the Russian canvas hints and format their key caps like the reference
  3. 48d7fb92 fix(github): cancel device sign-in and report denied, expired and failed outcomes
  4. b8c543bf feat(plugins): send the UI scale to plugin pages and localize plugin names and titles

The four commits are independent of each other, so you can drop any one of them.

Verification

  • macOS: this exact tree, with a fake HOME. Results: helpers, catalog (no diff), typecheck, full suite 2189 / 2185 / 0 / 4, build and audit:secrets pass; startup bundle +4,422 bytes over main (1,693,545).

  • Linux (Ubuntu 24.04 VM, xvfb): checked in ru/en, sage/night, at UI scale 0.85, 1.0 and 1.25: the hints panel (396×248 px at 0.85, 573×348 px at 1.25), wheel settings, the shortcut reference, the GitHub failure line, the card options menu and a plugin card at 1.25.

    hints 0.85 hints 1.25
    wheel settings ru 0.85 GitHub sign-in failure line

  • Not verified: Windows; a real GitHub sign-in (the flow is covered by tests with a stubbed GitHub API); a real third-party plugin page at 1.25; the lilac palette.

Risks

  • At 1.25 the hints panel and plugin cards are larger, so a corner panel covers a little more canvas. The title bar does not scale, because its height is tied to the native window chrome.
  • On a slow network, sign-in ends after five transient failures in a row (about 2 minutes of back-off) instead of retrying until the code expires.
  • Older cores reject manifests that use the new localized keys, as they reject any unknown field.

Depends on

Nothing. It conflicts only in text with #136 (docs/UI_CONTRACT.md: keep #136's bullets and add this PR's clause about the card menu and hints panel).

…names and titles

- Plugin pages, HOME widgets and canvas apps get appearance.uiScale (the
  root --ui-scale) with the locale and palette, again whenever it changes;
  separate plugin windows read it from host.getContext.
- A manifest may add name.ru/name.en, title.ru/title.en on contributions
  (canvas apps, the settings page, HOME widgets, windows) and
  label.ru/label.en on launch fields, each up to 80 characters like the
  text it translates. The app shows its language's text, else the plain one.
- Settings -> Agents: the "may allow" help line uses the normal help text
  style instead of the monospace metadata line.
- Schema, plugin-api.d.ts and docs/plugins (en/ru/zh-CN) document both.
…led outcomes

A started GitHub device sign-in could not be cancelled, retried poll
failures until the code expired, and ended silently when GitHub answered
access_denied or expired_token. A sign-in that could not start showed
the raw IPC error ("Error invoking remote method 'github-auth:start':
...") at the top of the Plugins page, in English in the Russian UI.

- GithubAuthService.cancelDeviceFlow() is public and exposed through the
  existing github-auth IPC (main-renderer only) and preload.
- status() reports deviceFlowState: idle, pending, denied, expired,
  failed or cancelled.
- access_denied, expired_token, unknown provider errors and non-transient
  HTTP errors stop polling with that outcome; OAuth error bodies on HTTP
  400 are read as well.
- Network errors, timeouts, HTTP 408/429/5xx and unreadable bodies back
  off and retry, but five failures in a row end the flow as failed.
- slow_down keeps adding five seconds to every later poll.
- The host clock ends the flow as expired even if GitHub keeps answering
  authorization_pending.
- Settings shows a Cancel button next to the pending code and a localized
  status line (en, ru) for the outcome, using existing classes: denied,
  expired and failed use the Settings error style, cancelled is a plain
  status line. A start or cancel failure shows a localized line ("not
  configured" for a build without an OAuth client); the details go to
  the log.
…e UI scale

Some UI chrome kept fixed pixel sizes, so Settings -> Appearance -> UI
scale (0.85-1.25) did not reach it:

- The terminal card's options menu (Don't restore this window, plugin
  card actions) had a 12px font and pixel paddings while the card header
  around it scaled.
- The canvas shortcut-hints panel looked the same at 0.85 and 1.25.
- In Settings -> Plugins the plugin description scaled with the settings
  panel, but the permission list, the contribution rows ("App on
  canvas"), the plugin title, author, version, buttons, module and
  native-hook lists kept fixed 9-17px sizes, so at 1.25 one card mixed
  large and small text.

The menu and the hints panel now take their font size from
calc(Npx * var(--ui-scale, 1)) and size their paddings, gaps, radii and
minimum widths in em; the hints icon is sized in em too. The plugin
settings font sizes use the same calc. At scale 1.0 every size is the
same as before. UI_CONTRACT names the menu and the hints panel among the
chrome that follows --ui-scale.

The title bar is left as is: its height is shared with the native
window chrome (macOS traffic-light position, the startup page) and with
every viewport-bound overlay through --titlebar-height.
…like the reference

The Russian canvas hints and Settings still showed English: the
wheel/pinch hint, the hard-coded "Shift + drag" key cap in the hints
panel and the shortcut reference, the canvas navigation override
description ("wheel, pinch и drag ... Drag колёсиком") and the
Settings > Canvas wheel capture strings and Off/On/Key labels.

- Translate them, reusing the existing on/off keys and adding
  shortcutShiftDrag and canvasWheelCaptureKey; the hints use one word for
  the drag gesture ("перетаскивание"). The group-selection label no
  longer repeats its key cap (ru/en). A test checks that these ru strings
  hold no Latin words besides key names.
- The wheel/pinch and navigation override rows in Settings > Keyboard
  shortcuts wrap instead of cutting their longer Russian labels off.
- The hints panel showed raw bindings ("Alt+ArrowUp") while the shortcut
  reference showed "Alt+↑". Both use one formatter now. It maps key by
  key, so on macOS "Meta+Comma" reads "Command+," instead of the
  reference's ",nd+Comma" (the Comma key was replaced inside "Command").
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-14T08:07:35.966866Z b8c543b PR opened
ℹ️ About Codex in GitHub

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

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

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b8c543bf9d

ℹ️ About Codex in GitHub

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

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

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

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

void this.pollDeviceCode(deviceCode, interval, flowLifetimeMs, generation, controller.signal)
.catch((error) => {
if (!isAbortError(error)) console.warn("CanvasTTY GitHub OAuth device poll failed.", error);
if (controller.signal.aborted || isAbortError(error)) return;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Treat only the outer abort as cancellation

When GitHub has returned an access token but the subsequent /user request exceeds the 15-second request timeout, request() aborts its private controller and fetchLogin() rethrows an AbortError even though this flow's controller.signal is still active. This guard then mistakes that timeout for a user cancellation, and the finally clears deviceFlow without changing deviceFlowState, leaving status permanently pending and making the new Cancel action a no-op; only suppress the error when the outer signal is actually aborted, and report an internal timeout as failed.

Useful? React with 👍 / 👎.

Comment on lines +295 to +296
if (providerError === null && (!response.ok || !isRecord(payload))) {
if (isTransientStatus(response.status) || response.ok) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Retry JSON-formatted transient OAuth responses

When the token endpoint returns a transient 429/5xx with an OAuth-shaped JSON body such as {"error":"temporarily_unavailable"}, providerError is non-null, so this condition skips isTransientStatus() and the response falls through to the unknown-provider-error path at lines 343-345, ending sign-in after the first transient response. The previous implementation retried every non-OK response, and the new retry policy also explicitly classifies 429/5xx as transient, so status-based retry handling needs to run even when the body contains an error field.

Useful? React with 👍 / 👎.

Comment on lines +1631 to +1632
...(nameRu ? { "name.ru": nameRu } : {}),
...(nameEn ? { "name.en": nameEn } : {}),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Propagate localized names to card-action UI

For a localized plugin with a trusted cards:decorate service, these fields survive validation but src/main/services/PluginManager.ts:730 still collapses the manifest to name: manifest.name; src/main/services/PluginCards.ts:120 forwards that value and src/renderer/src/features/terminal/TerminalCard.tsx:1321 renders it as the action tooltip (with the same fallback name used in failure toasts at PluginCards.ts:153). As a result, this user-facing plugin surface stays in the manifest's fallback language while the rest of the newly localized UI follows the selected locale; carry the localized variants through the action entry or resolve the name from pluginId in the renderer.

Useful? React with 👍 / 👎.

@BIackFIame

Copy link
Copy Markdown
Contributor Author

Closing this one: its fixes are now in #159 (the UI scale, Russian text and GitHub sign-in fixes, with their review follow-ups). The plugin-page part (appearance.uiScale for plugin pages, and localized plugin names and titles in the manifest) will come as a separate PR. Thank you for the review.

@BIackFIame BIackFIame closed this Oct 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant