Skip to content

Conversation

@rszwajko
Copy link
Member

@rszwajko rszwajko commented Feb 25, 2025

Depends on: #3209

📝 Description

General steps:

  1. get text from clipboard
  2. for each Unicode code point get the keystrokes required to trigger
    it in the given keyboard layout
  3. send the keystrokes to the server using QEMUExtendedKeyEvent

Mapping code point to keystrokes:

  1. use ucs2keysym() method to convert Unicode code to X11 keysym code.
    Functionality is part of xorg-server-1.12.2/hw/xquartz/keysym2ucs.c
    that was ported to TypeScript.
  2. map keysym code to mnemonic name using keysym_to_name mapping
    based on XKB mappings
  3. check if there is a mapping for that name in the keymap for
    the chosen keyboard layout. The keymaps are in Qemu format.
    In Qemu they are used to enforce a keyboard layout (-k switch).
  4. resolved mapping consist of Qemu keycode coressponding to
    physical key and a list of modifieres i.e. shift, altgr, numlock.

Example:

  1. assume single char ":"
  2. Unicode code point is 0x03a
  3. X11 keysym code is 0x03a (Latin-1 set has 1:1 mapping)
  4. resolved mnemonic name is "colon"
  5. in the keymap en-us name "colon" has following mapping:
    ['colon', 0x27, 'shift']
  6. based on this mapping we need to:
    a) press shift key
    b) press and release key with scancode 0x27
    c) release shift key

Navigation in the UI:

  1. default keymap is en-us
  2. if favorite keymaps are defined then the first keymap is used as the
    default keymap
  3. favorite keymaps are stored in the local storage
  4. currently selected keymap is stored in the top level component state
    which allows user to change to Serial Console and back without losing
    the selection.

Reference-Url: https://github.com/xkbcommon/libxkbcommon/blob/b21a58d0cb00c117a4821ac528b586c6d7222f0b/src/keysym.c#L68
Reference-Url: https://github.com/xkbcommon/libxkbcommon/blob/b21a58d0cb00c117a4821ac528b586c6d7222f0b/tools/how-to-type.c#L25
Reference-Url: https://github.com/qemu/qemu/blob/b69801dd6b1eb4d107f7c2f643adf0a4e3ec9124/ui/vnc_keysym.h
Reference-Url: https://github.com/qemu/qemu/tree/b69801dd6b1eb4d107f7c2f643adf0a4e3ec9124/pc-bios/keymaps
Reference-Url: https://www.x.org/releases/X11R7.7/src/xserver/xorg-server-1.12.2.tar.gz
Reference-Url: https://danielhb.github.io/article/2019/05/06/noVNC-QEMU-RFB.html
Signed-off-by: Radoslaw Szwajkowski [email protected]

🎥 Demo

Demo with US, French and German layouts

Steps in demo:

  1. start with US keyboard layout
  2. insert our test phrase zaqwerty using en-us keymap
  3. change keyboard to French using sudo loadkeys fr
  4. insert the same phrase using en-us keymap - note that the text is malformed
  5. repeat action with correct keymap
  6. change keyboard to German and re-test
Screencast.From.2025-10-02.15-53-24.mp4

Information about unsupported char mapping

Screenshot From 2025-10-02 15-57-31

Navigation in the UI (favorites and maintaining selection)

Screencast.From.2025-10-02.16-01-06.mp4

Summary by CodeRabbit

  • New Features

    • Added keyboard layout selection for VNC consoles with support for saving favorite layouts.
    • Added detection and user notification for unsupported characters when pasting content into VNC consoles.
  • Chores

    • Updated Node.js version to 22.
    • Updated npm package configuration for enhanced dependency management.
  • Documentation

    • Added multi-language support for keyboard layout and clipboard messaging.

✏️ Tip: You can customize this high-level summary in your review settings.

@openshift-ci-robot
Copy link
Collaborator

openshift-ci-robot commented Feb 25, 2025

@rszwajko: This pull request references CNV-40684 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the bug to target the "4.19.0" version, but no target version was set.

In response to this:

📝 Description

🎥 Demo

Screencast.from.2025-02-25.18-20-57.mp4

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@rszwajko rszwajko requested a review from metalice February 25, 2025 17:26
@openshift-ci
Copy link
Contributor

openshift-ci bot commented Feb 25, 2025

Skipping CI for Draft Pull Request.
If you want CI signal for your change, please convert it to an actual PR.
You can still manually trigger a test run with /test all

@openshift-ci
Copy link
Contributor

openshift-ci bot commented Feb 27, 2025

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: rszwajko

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-ci openshift-ci bot added the approved This issue is something we want to fix label Feb 27, 2025
@openshift-ci-robot
Copy link
Collaborator

openshift-ci-robot commented Feb 28, 2025

@rszwajko: This pull request references CNV-40684 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the bug to target the "4.19.0" version, but no target version was set.

In response to this:

📝 Description

General steps:

  1. get text from clipboard
  2. for each Unicode code point get the keystrokes required to trigger
    it in the given keyboard layout
  3. send the keystrokes to the server using QEMUExtendedKeyEvent

Mapping code point to keystrokes:

  1. use ucs2keysym() method to convert Unicode code to X11 keysym code.
    Functionality is part of xorg-server-1.12.2/hw/xquartz/keysym2ucs.c
    that was ported to TypeScript.
  2. map keysym code to mnemonic name using Qemu vnc_keysym.h
  3. check if there is a mapping for that name in the keymap for
    the chosen keyboard layout. The keymaps originate from Qemu project
    where they are used to enforce a keyboard layout (-k switch)
  4. resolved mapping consist of the scancode coressponding to physical
    key and a list of modifieres i.e. shift, altgr, numlock.

Example:

  1. assume single char ":"
  2. Unicode code point is 0x03a
  3. X11 keysym code is 0x03a (Latin-1 set has 1:1 mapping)
  4. resolved mnemonic name is "colon"
  5. in the keymap en-us name "colon" has following mapping:
    ['colon', 0x27, 'shift']
  6. based on this mapping we need to:
    a) press shift key
    b) press and release key with scancode 0x27
    c) release shift key

Reference-Url: https://github.com/qemu/qemu/blob/b69801dd6b1eb4d107f7c2f643adf0a4e3ec9124/ui/vnc_keysym.h
Reference-Url: https://github.com/qemu/qemu/tree/b69801dd6b1eb4d107f7c2f643adf0a4e3ec9124/pc-bios/keymaps
Reference-Url: https://www.x.org/releases/X11R7.7/src/xserver/xorg-server-1.12.2.tar.gz

🎥 Demo

Demo with US, French and German layouts

Steps in demo:

  1. ensure US keyboard layout sudo loadkeys us
  2. insert our test phrase ZaQwErTy-= using en-us keymap
  3. change keyboard to French
  4. insert the same phrase using en-us keymap - observer malformed text
  5. repeat action with correct keymap
  6. change keyboard to German and re-test
Screencast.from.2025-02-28.13-04-03.mp4

Information about unsupported char mapping

Screenshot from 2025-02-27 14-32-08

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@openshift-ci-robot
Copy link
Collaborator

openshift-ci-robot commented Feb 28, 2025

@rszwajko: This pull request references CNV-40684 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the bug to target the "4.19.0" version, but no target version was set.

In response to this:

📝 Description

General steps:

  1. get text from clipboard
  2. for each Unicode code point get the keystrokes required to trigger
    it in the given keyboard layout
  3. send the keystrokes to the server using QEMUExtendedKeyEvent

Mapping code point to keystrokes:

  1. use ucs2keysym() method to convert Unicode code to X11 keysym code.
    Functionality is part of xorg-server-1.12.2/hw/xquartz/keysym2ucs.c
    that was ported to TypeScript.
  2. map keysym code to mnemonic name using Qemu vnc_keysym.h
  3. check if there is a mapping for that name in the keymap for
    the chosen keyboard layout. The keymaps originate from Qemu project
    where they are used to enforce a keyboard layout (-k switch)
  4. resolved mapping consist of the scancode coressponding to physical
    key and a list of modifieres i.e. shift, altgr, numlock.

Example:

  1. assume single char ":"
  2. Unicode code point is 0x03a
  3. X11 keysym code is 0x03a (Latin-1 set has 1:1 mapping)
  4. resolved mnemonic name is "colon"
  5. in the keymap en-us name "colon" has following mapping:
    ['colon', 0x27, 'shift']
  6. based on this mapping we need to:
    a) press shift key
    b) press and release key with scancode 0x27
    c) release shift key

Reference-Url: https://github.com/qemu/qemu/blob/b69801dd6b1eb4d107f7c2f643adf0a4e3ec9124/ui/vnc_keysym.h
Reference-Url: https://github.com/qemu/qemu/tree/b69801dd6b1eb4d107f7c2f643adf0a4e3ec9124/pc-bios/keymaps
Reference-Url: https://www.x.org/releases/X11R7.7/src/xserver/xorg-server-1.12.2.tar.gz
Reference-Url: #2471

🎥 Demo

Demo with US, French and German layouts

Steps in demo:

  1. ensure US keyboard layout sudo loadkeys us
  2. insert our test phrase ZaQwErTy-= using en-us keymap
  3. change keyboard to French
  4. insert the same phrase using en-us keymap - observer malformed text
  5. repeat action with correct keymap
  6. change keyboard to German and re-test
Screencast.from.2025-02-28.13-04-03.mp4

Information about unsupported char mapping

Screenshot from 2025-02-27 14-32-08

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@rszwajko rszwajko marked this pull request as ready for review February 28, 2025 12:24
@openshift-ci openshift-ci bot requested a review from vojtechszocs February 28, 2025 12:24
rszwajko added a commit to rszwajko/vnc-keymaps that referenced this pull request Jun 5, 2025
Create project utilities:
1. GitHub actions - based on GH provide templates
2. simple build based on tsup
3. test via jest
4. lintings with eslint

Reference-Url: kubevirt-ui/kubevirt-plugin#2486
Signed-off-by: Radoslaw Szwajkowski <[email protected]>
@rszwajko rszwajko marked this pull request as draft June 5, 2025 11:45
rszwajko added a commit to rszwajko/vnc-keymaps that referenced this pull request Jun 5, 2025
Create project utilities:
1. GitHub actions - based on GH provide templates
2. simple build based on tsup
3. test via jest
4. lintings with eslint

Reference-Url: kubevirt-ui/kubevirt-plugin#2486
Reference-Url: https://github.com/qemu/qemu/blob/b69801dd6b1eb4d107f7c2f643adf0a4e3ec9124/ui/vnc_keysym.h
Reference-Url: https://github.com/qemu/qemu/tree/b69801dd6b1eb4d107f7c2f643adf0a4e3ec9124/pc-bios/keymaps
Reference-Url: https://www.x.org/releases/X11R7.7/src/xserver/xorg-server-1.12.2.tar.gz
Reference-Url: https://danielhb.github.io/article/2019/05/06/noVNC-QEMU-RFB.html
Signed-off-by: Radoslaw Szwajkowski <[email protected]>
rszwajko added a commit to rszwajko/vnc-keymaps that referenced this pull request Jun 9, 2025
Create project utilities:
1. GitHub actions - based on GH provide templates
2. simple build based on tsup
3. test via jest
4. lintings with eslint

Reference-Url: kubevirt-ui/kubevirt-plugin#2486
Reference-Url: https://github.com/qemu/qemu/blob/b69801dd6b1eb4d107f7c2f643adf0a4e3ec9124/ui/vnc_keysym.h
Reference-Url: https://github.com/qemu/qemu/tree/b69801dd6b1eb4d107f7c2f643adf0a4e3ec9124/pc-bios/keymaps
Reference-Url: https://www.x.org/releases/X11R7.7/src/xserver/xorg-server-1.12.2.tar.gz
Reference-Url: https://danielhb.github.io/article/2019/05/06/noVNC-QEMU-RFB.html
Signed-off-by: Radoslaw Szwajkowski <[email protected]>
rszwajko added a commit to rszwajko/vnc-keymaps that referenced this pull request Jun 9, 2025
Create project utilities:
1. GitHub actions - based on GH provide templates
2. dual publishing ESM and CJS build based on tsup
3. test via jest
4. lintings with eslint

Reference-Url: kubevirt-ui/kubevirt-plugin#2486
Reference-Url: https://github.com/qemu/qemu/blob/b69801dd6b1eb4d107f7c2f643adf0a4e3ec9124/ui/vnc_keysym.h
Reference-Url: https://github.com/qemu/qemu/tree/b69801dd6b1eb4d107f7c2f643adf0a4e3ec9124/pc-bios/keymaps
Reference-Url: https://www.x.org/releases/X11R7.7/src/xserver/xorg-server-1.12.2.tar.gz
Reference-Url: https://danielhb.github.io/article/2019/05/06/noVNC-QEMU-RFB.html
Signed-off-by: Radoslaw Szwajkowski <[email protected]>

import UnsupportedCharModal from './UnsupportedCharModal';

const canExecuteCommnands = (rfb) => rfb._rfbConnectionState === 'connected' && !rfb._viewOnly;
Copy link
Member

Choose a reason for hiding this comment

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

We can use ConsoleState.connected

Copy link
Member Author

Choose a reason for hiding this comment

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

We cannot because we are checking against internal RFB state - the names are the same but using our enum would suggest it's the same state.

@openshift-ci-robot
Copy link
Collaborator

openshift-ci-robot commented Oct 2, 2025

@rszwajko: This pull request references CNV-40684 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the bug to target the "4.21.0" version, but no target version was set.

In response to this:

📝 Description

General steps:

  1. get text from clipboard
  2. for each Unicode code point get the keystrokes required to trigger
    it in the given keyboard layout
  3. send the keystrokes to the server using QEMUExtendedKeyEvent

Mapping code point to keystrokes:

  1. use ucs2keysym() method to convert Unicode code to X11 keysym code.
    Functionality is part of xorg-server-1.12.2/hw/xquartz/keysym2ucs.c
    that was ported to TypeScript.
  2. map keysym code to mnemonic name using keysym_to_name mapping
    based on XKB mappings
  3. check if there is a mapping for that name in the keymap for
    the chosen keyboard layout. The keymaps are in Qemu format.
    In Qemu they are used to enforce a keyboard layout (-k switch).
  4. resolved mapping consist of Qemu keycode coressponding to
    physical key and a list of modifieres i.e. shift, altgr, numlock.

Example:

  1. assume single char ":"
  2. Unicode code point is 0x03a
  3. X11 keysym code is 0x03a (Latin-1 set has 1:1 mapping)
  4. resolved mnemonic name is "colon"
  5. in the keymap en-us name "colon" has following mapping:
    ['colon', 0x27, 'shift']
  6. based on this mapping we need to:
    a) press shift key
    b) press and release key with scancode 0x27
    c) release shift key

Navigation in the UI:

  1. default keymap is en-us
  2. if favorite keymaps are defined then the first keymap is used as the
    default keymap
  3. favorite keymaps are stored in the local storage
  4. currently selected keymap is stored in the top level component state
    which allows user to change to Serial Console and back without losing
    the selection.

Reference-Url: https://github.com/xkbcommon/libxkbcommon/blob/b21a58d0cb00c117a4821ac528b586c6d7222f0b/src/keysym.c#L68
Reference-Url: https://github.com/xkbcommon/libxkbcommon/blob/b21a58d0cb00c117a4821ac528b586c6d7222f0b/tools/how-to-type.c#L25
Reference-Url: https://github.com/qemu/qemu/blob/b69801dd6b1eb4d107f7c2f643adf0a4e3ec9124/ui/vnc_keysym.h
Reference-Url: https://github.com/qemu/qemu/tree/b69801dd6b1eb4d107f7c2f643adf0a4e3ec9124/pc-bios/keymaps
Reference-Url: https://www.x.org/releases/X11R7.7/src/xserver/xorg-server-1.12.2.tar.gz
Reference-Url: https://danielhb.github.io/article/2019/05/06/noVNC-QEMU-RFB.html
Signed-off-by: Radoslaw Szwajkowski [email protected]

🎥 Demo

Demo with US, French and German layouts

Steps in demo:

  1. start with US keyboard layout
  2. insert our test phrase zaqwerty using en-us keymap
  3. change keyboard to French using sudo loadkeys fr
  4. insert the same phrase using en-us keymap - note that the text is malformed
  5. repeat action with correct keymap
  6. change keyboard to German and re-test
Screencast.From.2025-10-02.15-53-24.mp4

Information about unsupported char mapping

Screenshot From 2025-10-02 15-57-31

Navigation in the UI (favorites and maintaining selection)

Screencast.From.2025-10-02.16-01-06.mp4

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@coderabbitai
Copy link

coderabbitai bot commented Nov 13, 2025

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

This PR introduces VNC keyboard layout selection with favorite keymaps persistence, replaces static paste functionality with keymap-aware clipboard handling, adds modal-based validation for unsupported characters, updates CI/workflow to Node.js 22 and GitHub npm registry authentication, and adds new translation keys for clipboard/keyboard error messaging.

Changes

Cohort / File(s) Summary
CI/Workflow Configuration
.github/workflows/ci_checks.yml
Adds global GITHUB_READ_PACKAGES_TOKEN env variable, permission packages: read to i18n job, upgrades Node.js from v18 to v22 in all Yarn setup steps, and injects auth token env blocks into yarn install steps.
NPM Registry Configuration
.npmrc
Configures @kubevirt-ui scope registry to npm.pkg.github.com with authentication token placeholder GITHUB_READ_PACKAGES_TOKEN.
Localization Additions
locales/en/plugin__kubevirt-plugin.json, locales/es/plugin__kubevirt-plugin.json, locales/fr/plugin__kubevirt-plugin.json, locales/ja/plugin__kubevirt-plugin.json, locales/ko/plugin__kubevirt-plugin.json, locales/zh/plugin__kubevirt-plugin.json
Adds two new translation keys across all locales: {{unsupportedCharCount}} characters are not supported by the keyboard layout mapping: and Unsupported content in the clipboard.
Package Dependencies
package.json
Adds new dependency @kubevirt-ui/vnc-keymaps at version 0.0.1.
Console Modal Integration
src/utils/components/Consoles/ConsoleStandAlone.tsx
Wraps Consoles component in ModalProvider, importing ModalProvider and useModalValue hooks.
VNC Keyboard Layout Support
src/utils/components/Consoles/components/AccessConsoles/AccessConsoles.tsx, src/utils/components/Consoles/components/AccessConsoles/VncKeymapDropdown.tsx
Replaces static Paste button with conditional rendering: VNC console types render VncKeymapDropdown for keymap selection; others render Paste button with error handling. New VncKeymapDropdown component offers dropdown selection with favorite keymaps management.
AccessConsoles Utilities
src/utils/components/Consoles/components/AccessConsoles/utils/accessConsoles.tsx
Updates sendPaste signature to accept PasteParams object, introduces PasteParams type with createModal, selectedKeyboard, and shouldFocusOnConsole fields, exports keyboard layout constants (EN_US, VNC_FAVORITE_KEYMAPS_KEY), and implements useFavoriteKeymaps hook for local storage persistence.
Serial Console Paste Handling
src/utils/components/Consoles/components/SerialConsole/SerialConsole.tsx
Updates sendPaste method signature to accept PasteParams object parameter and derives shouldFocusOnConsole default.
Unsupported Character Modal
src/utils/components/Consoles/components/vnc-console/UnsupportedCharModal.tsx
New component that renders a modal displaying up to 10 unsupported clipboard characters with hex code points, including count message.
VNC Console Export Changes
src/utils/components/Consoles/components/vnc-console/VncConsole.tsx
Removes named export of VncConsole component; default export remains unchanged.
VNC Console Actions Refactor
src/utils/components/Consoles/components/vnc-console/actions.ts, src/utils/components/Consoles/components/vnc-console/actions.tsx
Deletes entire actions.ts module and replaces with new actions.tsx file implementing: canExecuteCommands() gate logic, sendCtrlAltPlusKey() for key sequences, sendPasteCMD(params) with clipboard reading, keyboard layout mapping, unsupported character detection and modal triggering, and sendF1–sendF12 key senders.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant VncKeymapDropdown as VncKeymapDropdown
    participant AccessConsoles as AccessConsoles
    participant sendPasteCMD as sendPasteCMD
    participant Clipboard as Clipboard API
    participant KeyMap as KeyMap Lookup
    participant UnsupportedCharModal as UnsupportedCharModal
    participant VNC as VNC Console

    User->>VncKeymapDropdown: Open dropdown / Select keymap
    VncKeymapDropdown->>VncKeymapDropdown: Update selectedKeyboard
    VncKeymapDropdown->>VncKeymapDropdown: Manage favorites (localStorage)
    
    User->>VncKeymapDropdown: Click Paste action
    VncKeymapDropdown->>sendPasteCMD: Call with PasteParams<br/>(selectedKeyboard, createModal)
    
    sendPasteCMD->>Clipboard: Read clipboard text
    alt Valid String
        Clipboard-->>sendPasteCMD: clipboard text
        sendPasteCMD->>KeyMap: Map each char using<br/>selectedKeyboard layout
        
        alt Unsupported chars found
            rect rgb(255, 200, 200)
            sendPasteCMD->>UnsupportedCharModal: Trigger modal<br/>via createModal()
            User->>UnsupportedCharModal: View unsupported chars
            User->>UnsupportedCharModal: Close
            end
        else All chars supported
            rect rgb(200, 255, 200)
            sendPasteCMD->>VNC: Send mapped keys<br/>with delays
            VNC-->>sendPasteCMD: Keys emitted
            end
        end
    else Invalid/Non-string
        Clipboard-->>sendPasteCMD: null/non-string
        sendPasteCMD->>sendPasteCMD: Abort
    end
    
    sendPasteCMD->>VNC: Focus (if requested)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Key areas requiring attention:
    • The new sendPasteCMD() logic in actions.tsx: keyboard layout mapping, unsupported character detection, QEMUExtendedKeyEvent emission with proper delays, and modal integration for error feedback.
    • VncKeymapDropdown component: dropdown state management, favorite keymaps persistence, and filtering/sorting logic.
    • PasteParams signature change across SerialConsole.tsx and AccessConsoles.tsx: ensure all callers are updated consistently.
    • Removal of named export from VncConsole.tsx: verify no external code depends on the named export (only default export should be used).
    • useFavoriteKeymaps hook: local storage read/write logic, default fallback behavior, and array manipulation consistency.

Possibly related PRs

Suggested labels

lgtm

Suggested reviewers

  • vojtechszocs
  • pcbailey
  • metalice
  • galkremer1
  • upalatucci

Poem

🐰 A keymap carousel spins with grace,
Favorites tucked in the right place,
Clipboard chars now checked with care,
Modal whispers which ones won't fare—
VNC typing, smooth as a hare! 🎹

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Linked Issues check ❓ Inconclusive The PR title includes ticket reference 'CNV-40684' and the description provides comprehensive technical context. However, the PR objectives note that the JIRA issue has an invalid or missing target version. Verify that the JIRA issue CNV-40684 has a valid target version set for the destination branch (main) and that all linked issues are properly configured.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding keyboard keymap support for typing into the console. It directly relates to the substantial refactoring and new feature across multiple components.
Out of Scope Changes check ✅ Passed The PR includes multiple coordinated changes spanning console components, utilities, localization, build configuration, and a new dependency. All changes directly support the keymap feature and remain within scope.
Description check ✅ Passed The pull request description is comprehensive and includes required sections: detailed technical description of the implementation approach, demo videos/screenshots, and references.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@rszwajko
Copy link
Member Author

@CodeRabbit review

@coderabbitai
Copy link

coderabbitai bot commented Nov 20, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@openshift-ci-robot
Copy link
Collaborator

openshift-ci-robot commented Nov 20, 2025

@rszwajko: This pull request references CNV-40684 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the bug to target the "4.21.0" version, but no target version was set.

In response to this:

📝 Description

General steps:

  1. get text from clipboard
  2. for each Unicode code point get the keystrokes required to trigger
    it in the given keyboard layout
  3. send the keystrokes to the server using QEMUExtendedKeyEvent

Mapping code point to keystrokes:

  1. use ucs2keysym() method to convert Unicode code to X11 keysym code.
    Functionality is part of xorg-server-1.12.2/hw/xquartz/keysym2ucs.c
    that was ported to TypeScript.
  2. map keysym code to mnemonic name using keysym_to_name mapping
    based on XKB mappings
  3. check if there is a mapping for that name in the keymap for
    the chosen keyboard layout. The keymaps are in Qemu format.
    In Qemu they are used to enforce a keyboard layout (-k switch).
  4. resolved mapping consist of Qemu keycode coressponding to
    physical key and a list of modifieres i.e. shift, altgr, numlock.

Example:

  1. assume single char ":"
  2. Unicode code point is 0x03a
  3. X11 keysym code is 0x03a (Latin-1 set has 1:1 mapping)
  4. resolved mnemonic name is "colon"
  5. in the keymap en-us name "colon" has following mapping:
    ['colon', 0x27, 'shift']
  6. based on this mapping we need to:
    a) press shift key
    b) press and release key with scancode 0x27
    c) release shift key

Navigation in the UI:

  1. default keymap is en-us
  2. if favorite keymaps are defined then the first keymap is used as the
    default keymap
  3. favorite keymaps are stored in the local storage
  4. currently selected keymap is stored in the top level component state
    which allows user to change to Serial Console and back without losing
    the selection.

Reference-Url: https://github.com/xkbcommon/libxkbcommon/blob/b21a58d0cb00c117a4821ac528b586c6d7222f0b/src/keysym.c#L68
Reference-Url: https://github.com/xkbcommon/libxkbcommon/blob/b21a58d0cb00c117a4821ac528b586c6d7222f0b/tools/how-to-type.c#L25
Reference-Url: https://github.com/qemu/qemu/blob/b69801dd6b1eb4d107f7c2f643adf0a4e3ec9124/ui/vnc_keysym.h
Reference-Url: https://github.com/qemu/qemu/tree/b69801dd6b1eb4d107f7c2f643adf0a4e3ec9124/pc-bios/keymaps
Reference-Url: https://www.x.org/releases/X11R7.7/src/xserver/xorg-server-1.12.2.tar.gz
Reference-Url: https://danielhb.github.io/article/2019/05/06/noVNC-QEMU-RFB.html
Signed-off-by: Radoslaw Szwajkowski [email protected]

🎥 Demo

Demo with US, French and German layouts

Steps in demo:

  1. start with US keyboard layout
  2. insert our test phrase zaqwerty using en-us keymap
  3. change keyboard to French using sudo loadkeys fr
  4. insert the same phrase using en-us keymap - note that the text is malformed
  5. repeat action with correct keymap
  6. change keyboard to German and re-test
Screencast.From.2025-10-02.15-53-24.mp4

Information about unsupported char mapping

Screenshot From 2025-10-02 15-57-31

Navigation in the UI (favorites and maintaining selection)

Screencast.From.2025-10-02.16-01-06.mp4

Summary by CodeRabbit

  • New Features

  • Added keyboard layout selection for VNC consoles with support for saving favorite layouts.

  • Added detection and user notification for unsupported characters when pasting content into VNC consoles.

  • Chores

  • Updated Node.js version to 22.

  • Updated npm package configuration for enhanced dependency management.

  • Documentation

  • Added multi-language support for keyboard layout and clipboard messaging.

✏️ Tip: You can customize this high-level summary in your review settings.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (6)
.npmrc (1)

1-2: Scoped registry looks good; ensure local dev guidance and consider setup-node auth.

  • Keep this, but document how contributors set GITHUB_READ_PACKAGES_TOKEN locally (PAT with read:packages) or installs will 401.
  • Optional: rely on actions/setup-node to inject auth and remove step‑level env duplication by setting with:
    registry-url: https://npm.pkg.github.com and scope: '@kubevirt-ui'.
locales/en/plugin__kubevirt-plugin.json (1)

1636-1636: Minor copy nit (optional).

“Unsupported content in the clipboard” → “Unsupported clipboard content” (shorter, title-friendly). Optional.

-  "Unsupported content in the clipboard": "Unsupported content in the clipboard",
+  "Unsupported clipboard content": "Unsupported clipboard content",
src/utils/components/Consoles/components/vnc-console/actions.tsx (1)

18-129: VNC paste flow looks sound; consider small robustness & perf tweaks

The overall approach (connection checks, keymap lookup, unsupported-char modal, and QEMU extended key events with delays) looks solid. A few non-blocking improvements to consider:

  • Reuse canExecuteCommands in createSendKeyFunction for a single source of truth on connection/view-only checks.
  • Add a defensive guard for an unexpected/missing keyMaps[selectedKeyboard] (e.g. fallback to a default layout or early-return with a console error) to avoid a hard runtime error if an invalid layout ever slips through.
  • The fixed 50ms sleeps per key event can make large pastes quite slow (hundreds of ms per character). If testing shows it’s safe, you might reduce the delay or make it configurable to better balance reliability vs. throughput.

These are optional cleanups; the current implementation is functionally correct given the surrounding usage.

src/utils/components/Consoles/components/AccessConsoles/VncKeymapDropdown.tsx (2)

40-43: Validate value type before calling updateFavorite.

The onActionClick callback receives value typed as number | string (from PatternFly's API), but updateFavorite expects a KeyboardLayout. Consider adding type validation similar to the onSelect handler.

Apply this diff:

       onActionClick={(event, value) => {
         event.stopPropagation();
-        updateFavorite(value);
+        isKeyboardLayout(value) && updateFavorite(value);
       }}

92-99: Consider defensive null check for keyMaps access.

While favoriteKeymaps is pre-filtered in useFavoriteKeymaps to contain only known keymaps, adding a defensive check here would improve robustness against edge cases (e.g., stale localStorage data combined with library version mismatches).

             <DropdownGroup>
               {favoriteKeymaps
                 .map((value): [KeyboardLayout, KeyMapDef] => [value, keyMaps[value]])
+                .filter(([, def]) => def !== undefined)
                 .map(([value, def]) => (
                   <DropdownItem description={value} isFavorited key={value} value={value}>
                     {def.description}
                   </DropdownItem>
                 ))}
             </DropdownGroup>
src/utils/components/Consoles/components/AccessConsoles/utils/accessConsoles.tsx (1)

63-65: Strengthen type validation for localStorage data.

The array elements from savedFavoriteKeymaps aren't explicitly validated as strings before filtering. While the knownKeymaps.has(entry) check will fail gracefully for non-string types, adding explicit type validation would improve type safety.

   const filteredFavoriteKeymaps: KeyboardLayout[] = (
     Array.isArray(savedFavoriteKeymaps) ? savedFavoriteKeymaps : []
-  ).filter((entry) => knownKeymaps.has(entry));
+  ).filter((entry): entry is KeyboardLayout => 
+    typeof entry === 'string' && knownKeymaps.has(entry)
+  );
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 67121de and 66e05ba.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (18)
  • .github/workflows/ci_checks.yml (4 hunks)
  • .npmrc (1 hunks)
  • locales/en/plugin__kubevirt-plugin.json (2 hunks)
  • locales/es/plugin__kubevirt-plugin.json (2 hunks)
  • locales/fr/plugin__kubevirt-plugin.json (2 hunks)
  • locales/ja/plugin__kubevirt-plugin.json (2 hunks)
  • locales/ko/plugin__kubevirt-plugin.json (2 hunks)
  • locales/zh/plugin__kubevirt-plugin.json (2 hunks)
  • package.json (1 hunks)
  • src/utils/components/Consoles/ConsoleStandAlone.tsx (3 hunks)
  • src/utils/components/Consoles/components/AccessConsoles/AccessConsoles.tsx (4 hunks)
  • src/utils/components/Consoles/components/AccessConsoles/VncKeymapDropdown.tsx (1 hunks)
  • src/utils/components/Consoles/components/AccessConsoles/utils/accessConsoles.tsx (3 hunks)
  • src/utils/components/Consoles/components/SerialConsole/SerialConsole.tsx (2 hunks)
  • src/utils/components/Consoles/components/vnc-console/UnsupportedCharModal.tsx (1 hunks)
  • src/utils/components/Consoles/components/vnc-console/VncConsole.tsx (1 hunks)
  • src/utils/components/Consoles/components/vnc-console/actions.ts (0 hunks)
  • src/utils/components/Consoles/components/vnc-console/actions.tsx (1 hunks)
💤 Files with no reviewable changes (1)
  • src/utils/components/Consoles/components/vnc-console/actions.ts
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-11-06T13:11:11.791Z
Learnt from: rszwajko
Repo: kubevirt-ui/kubevirt-plugin PR: 3089
File: locales/es/plugin__kubevirt-plugin.json:102-102
Timestamp: 2025-11-06T13:11:11.791Z
Learning: For the kubevirt-ui/kubevirt-plugin repository: Do not flag or comment on missing translations (English values) in locale files for languages other than English (e.g., locales/es/, locales/fr/, locales/ja/, locales/ko/, locales/zh/). Only review English locale files (locales/en/) for translation keys.

Applied to files:

  • locales/es/plugin__kubevirt-plugin.json
  • locales/zh/plugin__kubevirt-plugin.json
  • locales/ja/plugin__kubevirt-plugin.json
  • locales/en/plugin__kubevirt-plugin.json
  • locales/fr/plugin__kubevirt-plugin.json
  • locales/ko/plugin__kubevirt-plugin.json
🔇 Additional comments (10)
.github/workflows/ci_checks.yml (1)

25-25: Node 22 bump is toolchain-compatible — verified.

Verification confirms compatibility: engines.node constraint >=18.20.5 allows Node 22, no native build dependencies present, and standard yarn lockfile v1 has no known issues. The bump is safe to proceed.

locales/ko/plugin__kubevirt-plugin.json (1)

60-60: Skipping non-English locale review per repo policy.

Per repository guidance, we only review English locale files; non‑English entries are not evaluated for translation quality. Based on learnings.

Also applies to: 1636-1636

locales/es/plugin__kubevirt-plugin.json (1)

65-65: Non-English locale diff acknowledged (no translation review)

Per repo guidance, I’m not reviewing or flagging translation content in non-English locale files; the new keys structurally align with the rest of the file.

Also applies to: 644-644

locales/zh/plugin__kubevirt-plugin.json (1)

60-60: Non-English locale diff acknowledged (no translation review)

Following project guidance, I’m not reviewing translation quality/content in non-English locale files; the newly added keys are structurally consistent.

Also applies to: 1636-1636

src/utils/components/Consoles/ConsoleStandAlone.tsx (1)

11-21: ModalProvider integration around stand-alone consoles looks correct

Wrapping <Consoles> in <ModalProvider value={value}> with value from useModalValue() is a clean way to enable the new modal flow (e.g., unsupported-char modal) in the stand-alone console; hooks are used correctly and existing behavior is preserved.

Also applies to: 34-46

package.json (1)

2-4: Dependency properly integrated; registry availability requires manual CI verification

The @kubevirt-ui/[email protected] package is correctly imported and used across four console components (vnc-console/actions.tsx, VncKeymapDropdown.tsx, AccessConsoles.tsx, and accessConsoles.tsx utilities), matching the keyboard layout mapping logic as described.

However, registry availability cannot be verified in this environment since the package is published to GitHub Packages (which requires authentication). Ensure your CI/build environment has proper GitHub token configuration (via GITHUB_TOKEN env var or .npmrc credentials) to resolve this private scope dependency during builds.

src/utils/components/Consoles/components/AccessConsoles/AccessConsoles.tsx (1)

4-5: Verified: sendPaste implementations are consistently async with proper Promise handling

The type definition in accessConsoles.tsx:34 explicitly requires sendPaste?: (params?: PasteParams) => Promise<void>. Both implementations—SerialConsole's inline async function (line 117) and VNC's sendPasteCMD (vnc-console/actions.tsx:40)—are async and thus return Promise. Both call sites (VncKeymapDropdown.tsx:57 and AccessConsoles.tsx:109) correctly chain .catch() for error handling. Your concern has already been addressed by the type system and implementation.

src/utils/components/Consoles/components/vnc-console/VncConsole.tsx (1)

41-49: Change is safe – no breaking named imports remain

Verification confirms VncConsole is exported only as default (line 151: export default memo(VncConsole)), and no named imports of VncConsole exist anywhere in the codebase. The removal of the named export poses no breaking change risk.

src/utils/components/Consoles/components/SerialConsole/SerialConsole.tsx (1)

117-126: LGTM! Clean API migration.

The update to accept PasteParams instead of a boolean maintains backward-compatible default behavior (shouldFocusOnConsole = true). While SerialConsole doesn't use all fields in PasteParams (e.g., createModal, selectedKeyboard), this unified API design is appropriate for consistency across console types.

src/utils/components/Consoles/components/AccessConsoles/utils/accessConsoles.tsx (1)

34-34: All sendPaste call sites have been successfully migrated to the new signature.

Verification across the codebase confirms that all invocations of sendPaste are using the new object parameter pattern ({ shouldFocusOnConsole: true } or similar), with no remaining calls using the old boolean argument style. The migration is complete.

permissions:
packages: read
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

build and unit-test lack packages: read; installs from GH Packages will fail.

Grant packages: read to all jobs (or at workflow level). Example (preferred, top-level):

 name: CI
+permissions:
+  packages: read
 on:

Or per job:

 i18n:
   name: i18n
   permissions:
     packages: read
+
+build:
+  permissions:
+    packages: read
+
+unit-test:
+  permissions:
+    packages: read

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
.github/workflows/ci_checks.yml lines 15-16: the workflow only sets permissions:
packages: read for a single scope which is missing at job level so installs from
GitHub Packages will fail for build and unit-test jobs; update the workflow to
grant packages: read at the workflow-level permissions block (or add packages:
read to each job) so GH Packages pulls succeed, ensuring the permissions block
is at the top-level of the YAML and not nested under a specific job.

"{{time}} seconds": "{{time}} seconds",
"{{timestampPluralized}} ago": "{{timestampPluralized}} ago",
"{{tolerations}} Toleration rules": "{{tolerations}} Toleration rules",
"{{unsupportedCharCount}} characters are not supported by the keyboard layout mapping:": "{{unsupportedCharCount}} characters are not supported by the keyboard layout mapping:",
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Clarify wording; consider pluralization.

“keyboard layout mapping” is a bit clunky for users. Prefer concise phrasing and (optionally) plural handling.

Apply this small copy tweak:

-  "{{unsupportedCharCount}} characters are not supported by the keyboard layout mapping:": "{{unsupportedCharCount}} characters are not supported by the keyboard layout mapping:",
+  "{{unsupportedCharCount}} characters are not supported by the selected keyboard layout:": "{{unsupportedCharCount}} characters are not supported by the selected keyboard layout:",

Optional (if your i18n setup supports plural forms like _one/_other used elsewhere in this file), you could split for proper grammar:

+  "{{unsupportedCharCount}} characters are not supported by the selected keyboard layout:_one": "{{unsupportedCharCount}} character is not supported by the selected keyboard layout:",
+  "{{unsupportedCharCount}} characters are not supported by the selected keyboard layout:_other": "{{unsupportedCharCount}} characters are not supported by the selected keyboard layout:",

And call with t(key, { count: unsupportedCharCount }).

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"{{unsupportedCharCount}} characters are not supported by the keyboard layout mapping:": "{{unsupportedCharCount}} characters are not supported by the keyboard layout mapping:",
"{{unsupportedCharCount}} characters are not supported by the selected keyboard layout:": "{{unsupportedCharCount}} characters are not supported by the selected keyboard layout:",
🤖 Prompt for AI Agents
locales/en/plugin__kubevirt-plugin.json around line 60: the string uses clunky
phrasing and doesn't handle pluralization; change the key/value to clearer
wording (e.g., "Unsupported characters for keyboard layout:" or "Characters not
supported by the keyboard layout:") and if your i18n supports plural forms, add
two entries like key_one and key_other (or the _one/_other pattern used in this
file) where the _one form uses singular wording and _other uses plural, then
ensure callers invoke t(key, { count: unsupportedCharCount }) so the correct
form is used.

Comment on lines +20 to +37
const UnsupportedCharModal: FC<UnsupportedCharModal> = ({ isOpen, onClose, unsupportedChars }) => {
const { t } = useKubevirtTranslation();
const maxChars = 10;
const unsupportedCharCount = unsupportedChars.length;
return (
<Modal isOpen={isOpen} onClose={onClose} position="top" variant={'small'}>
<ModalHeader title={t('Unsupported content in the clipboard')} />
<ModalBody>
{t(
'{{unsupportedCharCount}} characters are not supported by the keyboard layout mapping:',
{
unsupportedCharCount,
},
)}
<List>
{unsupportedChars.splice(0, maxChars).map((char) => (
<ListItem key={char}>{`'${char}' (0x${char.codePointAt(0).toString(16)})`}</ListItem>
))}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Avoid mutating props when truncating the unsupported chars list

unsupportedChars.splice(0, maxChars) mutates the unsupportedChars prop during render, which is a React anti‑pattern and can lead to surprising behavior if the same array is reused.

Use a non‑mutating slice instead:

-        <List>
-          {unsupportedChars.splice(0, maxChars).map((char) => (
+        <List>
+          {unsupportedChars.slice(0, maxChars).map((char) => (
             <ListItem key={char}>{`'${char}' (0x${char.codePointAt(0).toString(16)})`}</ListItem>
           ))}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const UnsupportedCharModal: FC<UnsupportedCharModal> = ({ isOpen, onClose, unsupportedChars }) => {
const { t } = useKubevirtTranslation();
const maxChars = 10;
const unsupportedCharCount = unsupportedChars.length;
return (
<Modal isOpen={isOpen} onClose={onClose} position="top" variant={'small'}>
<ModalHeader title={t('Unsupported content in the clipboard')} />
<ModalBody>
{t(
'{{unsupportedCharCount}} characters are not supported by the keyboard layout mapping:',
{
unsupportedCharCount,
},
)}
<List>
{unsupportedChars.splice(0, maxChars).map((char) => (
<ListItem key={char}>{`'${char}' (0x${char.codePointAt(0).toString(16)})`}</ListItem>
))}
const UnsupportedCharModal: FC<UnsupportedCharModal> = ({ isOpen, onClose, unsupportedChars }) => {
const { t } = useKubevirtTranslation();
const maxChars = 10;
const unsupportedCharCount = unsupportedChars.length;
return (
<Modal isOpen={isOpen} onClose={onClose} position="top" variant={'small'}>
<ModalHeader title={t('Unsupported content in the clipboard')} />
<ModalBody>
{t(
'{{unsupportedCharCount}} characters are not supported by the keyboard layout mapping:',
{
unsupportedCharCount,
},
)}
<List>
{unsupportedChars.slice(0, maxChars).map((char) => (
<ListItem key={char}>{`'${char}' (0x${char.codePointAt(0).toString(16)})`}</ListItem>
))}
🤖 Prompt for AI Agents
In src/utils/components/Consoles/components/vnc-console/UnsupportedCharModal.tsx
around lines 20 to 37, the render calls unsupportedChars.splice(0, maxChars)
which mutates the prop array; replace that with a non‑mutating slice (e.g.,
const displayed = unsupportedChars.slice(0, maxChars)) and map over displayed so
the original unsupportedChars is not modified during render.

@rszwajko rszwajko force-pushed the keymapPaste branch 2 times, most recently from 0e48122 to 5bf7b36 Compare November 21, 2025 12:23
@openshift-ci-robot
Copy link
Collaborator

openshift-ci-robot commented Nov 21, 2025

@rszwajko: This pull request references CNV-40684 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the bug to target the "4.21.0" version, but no target version was set.

In response to this:

Depends on: #3209

📝 Description

General steps:

  1. get text from clipboard
  2. for each Unicode code point get the keystrokes required to trigger
    it in the given keyboard layout
  3. send the keystrokes to the server using QEMUExtendedKeyEvent

Mapping code point to keystrokes:

  1. use ucs2keysym() method to convert Unicode code to X11 keysym code.
    Functionality is part of xorg-server-1.12.2/hw/xquartz/keysym2ucs.c
    that was ported to TypeScript.
  2. map keysym code to mnemonic name using keysym_to_name mapping
    based on XKB mappings
  3. check if there is a mapping for that name in the keymap for
    the chosen keyboard layout. The keymaps are in Qemu format.
    In Qemu they are used to enforce a keyboard layout (-k switch).
  4. resolved mapping consist of Qemu keycode coressponding to
    physical key and a list of modifieres i.e. shift, altgr, numlock.

Example:

  1. assume single char ":"
  2. Unicode code point is 0x03a
  3. X11 keysym code is 0x03a (Latin-1 set has 1:1 mapping)
  4. resolved mnemonic name is "colon"
  5. in the keymap en-us name "colon" has following mapping:
    ['colon', 0x27, 'shift']
  6. based on this mapping we need to:
    a) press shift key
    b) press and release key with scancode 0x27
    c) release shift key

Navigation in the UI:

  1. default keymap is en-us
  2. if favorite keymaps are defined then the first keymap is used as the
    default keymap
  3. favorite keymaps are stored in the local storage
  4. currently selected keymap is stored in the top level component state
    which allows user to change to Serial Console and back without losing
    the selection.

Reference-Url: https://github.com/xkbcommon/libxkbcommon/blob/b21a58d0cb00c117a4821ac528b586c6d7222f0b/src/keysym.c#L68
Reference-Url: https://github.com/xkbcommon/libxkbcommon/blob/b21a58d0cb00c117a4821ac528b586c6d7222f0b/tools/how-to-type.c#L25
Reference-Url: https://github.com/qemu/qemu/blob/b69801dd6b1eb4d107f7c2f643adf0a4e3ec9124/ui/vnc_keysym.h
Reference-Url: https://github.com/qemu/qemu/tree/b69801dd6b1eb4d107f7c2f643adf0a4e3ec9124/pc-bios/keymaps
Reference-Url: https://www.x.org/releases/X11R7.7/src/xserver/xorg-server-1.12.2.tar.gz
Reference-Url: https://danielhb.github.io/article/2019/05/06/noVNC-QEMU-RFB.html
Signed-off-by: Radoslaw Szwajkowski [email protected]

🎥 Demo

Demo with US, French and German layouts

Steps in demo:

  1. start with US keyboard layout
  2. insert our test phrase zaqwerty using en-us keymap
  3. change keyboard to French using sudo loadkeys fr
  4. insert the same phrase using en-us keymap - note that the text is malformed
  5. repeat action with correct keymap
  6. change keyboard to German and re-test
Screencast.From.2025-10-02.15-53-24.mp4

Information about unsupported char mapping

Screenshot From 2025-10-02 15-57-31

Navigation in the UI (favorites and maintaining selection)

Screencast.From.2025-10-02.16-01-06.mp4

Summary by CodeRabbit

  • New Features

  • Added keyboard layout selection for VNC consoles with support for saving favorite layouts.

  • Added detection and user notification for unsupported characters when pasting content into VNC consoles.

  • Chores

  • Updated Node.js version to 22.

  • Updated npm package configuration for enhanced dependency management.

  • Documentation

  • Added multi-language support for keyboard layout and clipboard messaging.

✏️ Tip: You can customize this high-level summary in your review settings.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

Usage in the CI:
1. GITHUB_TOKEN with permissions reduced to packages:read
2. create .npmrc file with actions/setup-node
3. inject the token as NODE_AUTH_TOKEN env var

Local development:
The packages under @kubevirt-ui will be public but GitHub Packages
requires authentication with personal access token to install.
The minimum required scope is packages:read.
One way to access the token is to use env var and amend the global
.npmrc with the following 2 lines:
@kubevirt-ui:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${GITHUB_READ_PACKAGES_TOKEN}

Updates:
1. the Node version from v18 to v22 (the currently active LTS version)
2. actions/setup-node from v4 to v6
3. unify actions/checkout to v5

Reference-Url: https://docs.github.com/en/packages/learn-github-packages/about-permissions-for-github-packages#about-scopes-and-permissions-for-package-registries
Signed-off-by: Radoslaw Szwajkowski <[email protected]>
General steps:
1. get text from clipboard
2. for each Unicode code point get the keystrokes required to trigger
   it in the given keyboard layout
3. send the keystrokes to the server using QEMUExtendedKeyEvent

Mapping code point to keystrokes:
1. use ucs2keysym() method to convert Unicode code to X11 keysym code.
   Functionality is part of xorg-server-1.12.2/hw/xquartz/keysym2ucs.c
   that was ported to TypeScript.
2. map keysym code to mnemonic name using keysym_to_name mapping
   based on XKB mappings
3. check if there is a mapping for that name in the keymap for
   the chosen keyboard layout. The keymaps are in Qemu format.
   In Qemu they are used to enforce a keyboard layout (-k switch).
4. resolved mapping consist of Qemu keycode coressponding to
   physical key and a list of modifieres i.e. shift, altgr, numlock.

Example:
1. assume single char ":"
2. Unicode code point is 0x03a
3. X11 keysym code is 0x03a (Latin-1 set has 1:1 mapping)
4. resolved mnemonic name is "colon"
5. in the keymap en-us name "colon" has following mapping:
   ['colon', 0x27, 'shift']
6. based on this mapping we need to:
  a) press shift key
  b) press and release key with scancode 0x27
  c) release shift key

Navigation in the UI:
1. default keymap is en-us
2. if favorite keymaps are defined then the first keymap is used as the
   default keymap
3. favorite keymaps are stored in the local storage
4. currently selected keymap is stored in the top level component state
   which allows user to change to Serial Console and back without losing
   the selection.

Reference-Url: https://github.com/xkbcommon/libxkbcommon/blob/b21a58d0cb00c117a4821ac528b586c6d7222f0b/src/keysym.c#L68
Reference-Url: https://github.com/xkbcommon/libxkbcommon/blob/b21a58d0cb00c117a4821ac528b586c6d7222f0b/tools/how-to-type.c#L25
Reference-Url: https://github.com/qemu/qemu/blob/b69801dd6b1eb4d107f7c2f643adf0a4e3ec9124/ui/vnc_keysym.h
Reference-Url: https://github.com/qemu/qemu/tree/b69801dd6b1eb4d107f7c2f643adf0a4e3ec9124/pc-bios/keymaps
Reference-Url: https://www.x.org/releases/X11R7.7/src/xserver/xorg-server-1.12.2.tar.gz
Reference-Url: https://danielhb.github.io/article/2019/05/06/noVNC-QEMU-RFB.html
Signed-off-by: Radoslaw Szwajkowski <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants