diff --git a/docs/research/carbon-ui.md b/docs/research/carbon-ui.md new file mode 100644 index 0000000..3322539 --- /dev/null +++ b/docs/research/carbon-ui.md @@ -0,0 +1,278 @@ +# IBM Carbon desktop UI strategy for VoidKontrol + +Research date: 2026-09-21. Decision input for +[Establish a faithful IBM Carbon desktop UI strategy for Void Linux](https://git.bongbetic.com/xavierk/voidkontrol/issues/4). +This report recommends a direction and records evidence; it does not select the +final stack, establish hardware support, or provide a prototype. + +## Recommendation + +Evaluate **official Carbon React in an unprivileged Tauri desktop application** +first. It combines Carbon's maintained component implementation with Void's +packaged GTK3/WebKitGTK stack and leaves the device service responsible for all +hardware control. Gate selection on a small native-Wayland, accessibility, and +packaging proof on the actual supported Void targets. This is an architectural +recommendation, not a completed compatibility test. [C1][T1][T2][V1] + +Electron remains a viable Carbon rendering alternative if the project accepts +owning a maintained Electron distribution/update route. Do not base that choice +on the mere existence of Void's `electron35` source template: the inspected +template is explicitly marked broken. A QML main application can implement Carbon, +but it would own a new Carbon implementation instead of consuming IBM's maintained +React or Web Components libraries. Reserve that extra cost for a demonstrated +requirement that the webview approach cannot satisfy. [C1][E1][E2][V2][Q1] + +A Noctalia plugin and the full editor have different responsibilities. The full +editor should use Carbon; the shell plugin should use Noctalia's supported API and +components and expose a small entry point/status surface. They can share the +device service without sharing a rendering toolkit. Exact Noctalia API choices +belong to the separate desktop-integration research. + +## Existing behavior and boundaries + +The supplied [control specification](../../KREO-SWARM75-LINUX-CONTROL-SPEC.md) +requires an unprivileged UI, a single service owner for discovery/authentication/ +serialization/readback, a complete backup before the first write, and comparison +of readback after every update. It prohibits grabbing input devices, detaching +the kernel driver, and automatic writes on startup/reconnect/resume. Protocol +payloads and persistence semantics still require authenticated hardware capture. + +The user has chosen verification of the observed USB presentation first and has +kept firmware flashing and the factory-reset UI outside v1. A GUI library cannot +close any of those protocol evidence gaps. The display should initially use the +specification's `Kreo Swarm 75 (signature match)` identification until successful +authentication; the observed VID/PID does not establish physical layout or +connection-mode compatibility. + +The repository contains a specification and agent documentation, with no existing +UI implementation or architecture decision to extend at this research checkout. +Consequently, the comparisons below do not propose replacing a working stack. + +## Maintained Carbon support and obligations + +1. Carbon's official developer guide lists **React and Web Components** as + officially supported. Other web frameworks have community implementations. + The maintained monorepo includes component styles, design tokens, icons, grid, + spacing, motion, and typography packages. It does not list an official Qt/QML + implementation. This is evidence about the supported offerings, not a claim + that no third-party QML project exists. [C1][C2] +2. `@carbon/react` supplies components, styles, and icons and requires a frontend + build pipeline. A static bundled frontend is sufficient for this desktop + editor; a web server framework is not required by Carbon. Official Web + Components are a legitimate alternative if the architecture decision finds a + concrete reason to avoid React; they still require a web rendering engine. + [C1][C2] +3. Carbon's themes are `white`, `g10`, `g90`, and `g100`. Use semantic theme, + spacing, typography, focus, and layer tokens instead of approximate custom + colors. Offer a supported light/dark choice; use Carbon's layering mechanism + for nested surfaces. Shell theme synchronization can select a Carbon theme, + but arbitrary wallpaper colors should not silently replace its contrast and + semantic color relationships. The latter is a product recommendation. [C3] +4. Carbon uses IBM Plex. Its current guide recommends per-family packages such + as `@ibm/plex-sans` and `@ibm/plex-mono`; the legacy monolithic `@ibm/plex` + package is no longer updated. Bundle the required fonts locally so installed + operation does not depend on a font CDN. Carbon is Apache-2.0; Plex is SIL + OFL-1.1 with reserved name `Plex`. Include the applicable notices and license + texts in the distribution. [C2][C4][C5] +5. Carbon components follow IBM's accessibility checklist, based on WCAG AA, + Section 508, and European standards. Carbon explicitly says accessible + components are only part of an accessible product. Test the assembled product + in the selected Linux renderer, including screen-reader integration, rather + than treating component adoption as application certification. [C6] + +## Desktop choices + +| Choice | Carbon fidelity and ownership | Linux/Void evidence | Decision cost or gate | +| --- | --- | --- | --- | +| Carbon React + Tauri | Reuses official components; small native IPC bridge can call the service. | Tauri uses WebKitGTK on Linux; its windowing stack uses GTK3. Wry documents the GTK route for Wayland. Void packages the required GTK3/libsoup3 WebKit 4.1 variant with Wayland enabled. [T1][T2][V1] | Build/runtime dependencies and WebKit behavior depend on the distribution. Test Carbon rendering, AT-SPI/screen reader operation, dialogs, scaling, and window activation on niri. | +| Carbon React + Electron | Reuses the same official components in bundled Chromium. | Upstream Linux binaries are built on Ubuntu; Electron documents native Wayland selection from version 38 when `XDG_SESSION_TYPE=wayland`. Void's inspected `electron35` template has musl build paths but is marked broken. [E1][E3][V2] | Ships Chromium/Node/Electron updates with the application. Establish a supported runtime distribution, musl route if needed, and package maintenance; the distro template is insufficient proof. | +| Qt Quick/QML with a custom Carbon style | Uses Qt controls and explicitly recreates Carbon appearance, layout, states, and interactions. | Qt provides native controls, accessibility metadata, and custom styling. Its listed styles do not include Carbon. [Q1][Q2] | Own the custom Carbon component set, visual parity, accessibility behavior, token mapping, and upstream design changes. Similarity to Noctalia's toolkit does not remove this work. | + +A smaller Tauri application bundle would **not** prove a smaller total installed +system or better memory/startup performance: GTK and WebKit dependencies still +exist. No benchmark was run. Conversely, Electron's bundled renderer can reduce +renderer-version variability but creates direct runtime update responsibility; +Electron supports the latest three stable major releases. [T1][E2] + +### glibc and musl + +- Upstream Tauri now includes Alpine/musl prerequisites, warning that some static + dependencies may need source builds. Therefore, a blanket claim that Tauri + cannot run on musl would be incorrect. Those Alpine instructions are **not a + Void compatibility result**. [T3] +- Void's `libwebkit2gtk41` template at + `954278b83979958bcfdff05f22cf699c9dc20346` is version `2.50.4_1`, enables + Wayland, defaults to a bubblewrap sandbox, and depends on `xdg-dbus-proxy` when + that option is enabled. It includes musl-compatible build paths rather than a + blanket musl prohibition. `libwebkitgtk60` is the GTK4 subpackage; it is not a + drop-in substitute for Tauri's GTK3 WebKit 4.1 requirement. Package source + availability alone does not prove a successful application build. [V1][T2] +- Void's `electron35` template at the same revision is `35.7.2_1`, has + `x86_64* aarch64*` architecture patterns and `musl-legacy-compat` paths, but is + marked `broken` because its configure tooling uses `FancyURLopener` removed in + Python 3.14. This disproves neither all Electron-on-musl possibilities nor the + possible existence of an older binary package; it does mean that this template + cannot establish a reproducible, maintained current runtime. [V2] +- Treat glibc and musl as separate build and runtime targets. A glibc binary or + AppImage must not be presented as proof of native musl support. Tauri's own + Linux distribution guidance warns that the build system's libc baseline affects + compatibility. Which libc and architectures v1 promises remains an architecture + decision informed by the Void research. [T4] + +### Security and process boundary + +Both wrappers can preserve the service boundary in the supplied specification. +Keep packaged UI assets local, render imported names as text, and expose only +typed domain operations through the desktop bridge. Neither JavaScript nor a QML +plugin should accept arbitrary HID frames, open `/dev/hidraw*`, run arbitrary shell +commands, or gain service credentials. The service must independently authorize +and validate requests; hiding a control is not authorization. + +For Tauri, capability/permission configuration constrains webview-to-core calls, +while core/plugin code itself has the application's OS privileges. Command +implementations remain responsible for enforcing scopes. This is a separate +boundary from the service's own peer authentication and device policy. [T5] + +For Electron, retain renderer sandboxing and context isolation, disable Node +integration in the renderer, validate IPC sender and arguments, constrain +navigation, and use a restrictive content-security policy. Its security guidance +also explicitly assigns responsibility for updating Electron, Chromium, Node, and +dependencies to the application maintainer. [E4] + +## Proposed interaction requirements + +These are design recommendations derived from the supplied protocol invariants, +not additional asserted keyboard capabilities. The service remains authoritative +for advertised features, verified ranges, physical key identifiers, raw-value +translation, and transaction state. + +### Main organization and keyboard accessibility + +Use a compact Carbon application shell with Device, Lighting, Key mapping, +Macros, Typing and power, and Backups as ordinary navigation destinations. Use +standard forms for editors and a restrained amount of custom rendering for the +physical keyboard preview. This is information architecture for later review, +not a locked wireframe. + +The layout preview should use the authenticated physical layout map and identify +each key by its position, legend, selected layer, and current assignment. A +keyboard-accessible list/form view must offer the same selection and editing +operations. The preview cannot be the only means of choosing a key, and color +cannot be the only indication of selection, assignment, or errors. A labelled +hex/RGB input or equivalent text entry should accompany any color picker. + +For layers, Carbon Tabs can expose Base, Fn, Fn1, and Fn2. Carbon supplies arrow-key +navigation and distinguishes automatic versus manual activation. Choose manual +activation if changing tabs causes a perceptible read delay; switching the editor +layer must not itself write settings or claim to change the keyboard's active +hardware layer. [C7] + +All functionality must be reachable without dragging or mouse-only gestures: +visible focus, logical tab order, standard activation keys, Escape for transient +surfaces, and focus restoration after dialogs. Preserve a path out of every custom +control. Provide move-up/down controls if macro actions are reorderable. Avoid +stealing ordinary typing or compositor shortcuts just to select physical keys. +Carbon's accessibility guidance explicitly requires full keyboard access and +testing beyond automated checks. [C6][C8] + +### Readback, changes, and failures + +Keep three concepts separate: the last configuration read from the device, the +user's unapplied edits, and the service's current transaction status. Recommended +visible states are: + +| Condition | UI behavior | +| --- | --- | +| No device, unsupported signature, unavailable service, or permission failure | Show a persistent, specific status with the relevant recovery action. Do not present cached state as live device state. | +| Authenticating or reading | Identify the operation and withhold hardware writes until the service declares them safe. | +| Edits pending | Clearly show unapplied changes and an explicit Apply action. Updating a form or selecting an effect changes local intent only. | +| Backing up, writing, or verifying | Show those actual stages. Prevent duplicate submission; do not call a sent command “Saved.” | +| Readback matches | Show “Applied and verified.” Claim persistence across power cycles only if the protocol/hardware evidence establishes it. | +| Timeout, disconnect, malformed reply, or mismatch | Preserve useful edit context, mark device state uncertain, and show a persistent error. Reconnection/readback precedes an explicit retry; no blind automatic replay. | + +Carbon InlineLoading supplies active/finished/error states and recommends +disabling associated actions during submission. Inline notifications persist, +and error messages should state the user action that resolves the problem. Use +these patterns for operation status; a fleeting toast is insufficient for a failed +hardware update. [C9][C10] + +### Feature editors + +| Area | v1 editor implications from the supplied specification | +| --- | --- | +| Lighting | Only capability-backed effects; five brightness/speed levels; monochrome/colourful; LED enable; one custom per-key frame. Use protocol-owned mappings for self-defined mode and colour-mode raw IDs. Do not invent direction controls or multiple onboard lighting profiles. | +| Key mapping | Four layers; physical keys only; keyboard/modifier/combination/media/mouse/disabled/macro choices where verified. Show current and proposed assignments; flag unknown readback without silently replacing it. | +| Macros | List the 32 slots; edit ordered press/release/delay actions with visible bounds and slot usage. Limit to 112 actions, 0–65,535 ms delays, and 127 encoded name bytes. Confirm the name encoding before implementing byte counting; “127 characters” is not equivalent. Offer only verified execution modes and validated parameter ranges. | +| Typing | Debounce choices 0/10/20/30/40 ms. Tap-delay UI must await its verified range/units. Do not expose unsupported NKRO, report-rate, OS-mode, Win-lock, or Hall-effect controls. | +| Power | Display battery/charging only when valid data is available; unknown is not zero. Sleep uses the stated discrete choices, not a free-form unbounded duration. | +| Backups and restore | Export a versioned, protocol-independent document; show source identity, timestamp, compatibility checks, and a change preview before explicit restore. Validation and a fresh pre-write backup belong to the service. Do not promise atomic restore unless evidence establishes it; identify partial/interrupted outcomes. | + +A macro action form is sufficient for complete authoring. If a later decision adds +recording, it must remain an explicit focused interaction with an obvious stop +path and must obey the specification's ban on input-device grabs. Global keyboard +recording is not required by the supplied capabilities and should not be added as +an implicit interpretation of “complete control.” Never execute a macro merely +because a user previews its steps. Layer reset, if included, needs separate +verified protocol behavior and explicit user intent; factory reset and firmware +flashing remain excluded. + +## Smallest sufficient proof before choosing the stack + +The later architecture/prototype decision should evaluate one packaged Carbon +form, layer tabs, a modal, a representative long macro list, the labelled keyboard +selector, and simulated service states. It does not need hardware writes. + +1. Build/install/uninstall on the intended Void libc/architecture targets with + declared runtime dependencies. Confirm local fonts/assets work without network + access and record exact dependency versions. +2. Launch as a native Wayland window on niri; verify app identity, keyboard focus, + normal resizing, fractional scaling, dialogs/file selection, and behavior when + launched from the shell integration. A successful XWayland fallback does not + satisfy a native-Wayland claim. +3. Operate every representative control by keyboard; test meaningful reading and + actions with the target Linux accessibility stack, including modal focus and + operation-status announcements. Carbon's upstream tabs screen-reader evidence + names JAWS, VoiceOver, and NVDA, so it is not a substitute for a Linux/Orca test. + [C7] +4. Exercise disconnected, unauthenticated, pending, verifying, and failed states + against a fake service; prove no simulated write occurs on launch, navigation, + refresh, or reconnect. Confirm duplicate Apply is blocked and stale drafts + cannot overwrite a newly read configuration without review. + +Unresolved product decisions are the supported libc/architecture matrix, wrapper +selection, theme synchronization policy, and the preferred editor interaction. +Unresolved hardware facts include the layout, name encoding, tap-delay parameters, +transaction persistence, and individual capability read/write validation. These +are explicit decision/proof obligations, not missing Carbon documentation. + +## Sources and retrieval + +Context7 was used first for Carbon, Tauri, Electron, and Qt (`library` then +`docs`, no more than three commands for each question). Findings were checked +against the following primary documentation/source. No application was built, +no packages were installed, no host configuration was changed, and no device I/O +was performed for this report. + +- [C1] [Carbon developer introduction](https://carbondesignsystem.com/developing/get-started/) and [React guide](https://carbondesignsystem.com/developing/frameworks/react/). +- [C2] [Carbon official repository, packages and license](https://github.com/carbon-design-system/carbon/blob/main/README.md). +- [C3] [Carbon themes](https://github.com/carbon-design-system/carbon/blob/main/packages/themes/README.md), [Theme component](https://github.com/carbon-design-system/carbon/blob/main/packages/react/src/components/Theme/Theme.mdx), and [Layer component](https://github.com/carbon-design-system/carbon/blob/main/packages/react/src/components/Layer/Layer.mdx). +- [C4] [Carbon IBM Plex guide](https://github.com/carbon-design-system/carbon/blob/main/docs/guides/ibm-plex.md). +- [C5] [IBM Plex license](https://github.com/IBM/plex/blob/master/LICENSE.txt). +- [C6] [Carbon accessibility overview](https://carbondesignsystem.com/guidelines/accessibility/overview/). +- [C7] [Carbon Tabs accessibility](https://carbondesignsystem.com/components/tabs/accessibility/). +- [C8] [Carbon accessibility verification guide](https://github.com/carbon-design-system/carbon/blob/main/docs/guides/accessibility.md). Its old IBM-internal DAP setup instructions are not adopted as a project tool requirement. +- [C9] [Carbon InlineLoading usage](https://carbondesignsystem.com/components/inline-loading/usage/). +- [C10] [Carbon notification usage](https://carbondesignsystem.com/components/notification/usage/) and [Form usage](https://carbondesignsystem.com/components/form/usage/). +- [T1] [Tauri webview versions](https://v2.tauri.app/reference/webview-versions/). +- [T2] [Wry Wayland/GTK integration](https://github.com/tauri-apps/wry/blob/dev/README.md) and [Tao Linux GTK3 dependency](https://github.com/tauri-apps/tao/blob/dev/README.md). +- [T3] [Tauri prerequisites, including Alpine](https://github.com/tauri-apps/tauri-docs/blob/v2/src/content/docs/start/prerequisites.mdx). +- [T4] [Tauri Linux distribution/libc baseline guidance](https://github.com/tauri-apps/tauri-docs/blob/v2/src/content/docs/distribute/debian.mdx). +- [T5] [Tauri security boundaries](https://github.com/tauri-apps/tauri-docs/blob/v2/src/content/docs/security/index.mdx) and [capabilities](https://github.com/tauri-apps/tauri-docs/blob/v2/src/content/docs/security/capabilities.mdx). +- [E1] [Electron platform support](https://github.com/electron/electron/blob/main/README.md). +- [E2] [Electron version support policy and cadence](https://github.com/electron/electron/blob/main/docs/tutorial/electron-timelines.md). +- [E3] [Electron breaking changes, v38 Wayland default](https://github.com/electron/electron/blob/main/docs/breaking-changes.md). +- [E4] [Electron security guidance](https://github.com/electron/electron/blob/main/docs/tutorial/security.md). +- [V1] [Void WebKitGTK package source at inspected revision](https://github.com/void-linux/void-packages/blob/954278b83979958bcfdff05f22cf699c9dc20346/srcpkgs/libwebkit2gtk41/template). +- [V2] [Void Electron package source at inspected revision](https://github.com/void-linux/void-packages/blob/954278b83979958bcfdff05f22cf699c9dc20346/srcpkgs/electron35/template). +- [Q1] [Qt Quick Controls available styles](https://doc.qt.io/qt-6/qtquickcontrols-styles.html). +- [Q2] [Qt Quick accessibility](https://doc.qt.io/qt-6/accessible-qtquick.html).