fix: ctrl-k behavior on macOS (#2052)

* Improve cmd-k / ctrl-k behavior

Use cmd-k on macOS/iOS for search and keep ctrl-k everywhere else.

Fixes a bug where ctrl-k on macOS, which cuts to the end of the line,
was also triggering the search prompt.

* comment submit: cmd-enter (mac) / ctrl-enter (win/linux)
This commit is contained in:
Peter Tripp
2026-04-30 19:36:40 -04:00
committed by GitHub
parent 24be90b95f
commit b16f1e5a55
5 changed files with 19 additions and 7 deletions
@@ -10,6 +10,7 @@ import { useTranslation } from "react-i18next";
import EmojiCommand from "@/features/editor/extensions/emoji-command"; import EmojiCommand from "@/features/editor/extensions/emoji-command";
import mentionRenderItems from "@/features/editor/components/mention/mention-suggestion"; import mentionRenderItems from "@/features/editor/components/mention/mention-suggestion";
import MentionView from "@/features/editor/components/mention/mention-view"; import MentionView from "@/features/editor/components/mention/mention-view";
import { platformModifierKey } from "@/lib";
interface CommentEditorProps { interface CommentEditorProps {
defaultContent?: any; defaultContent?: any;
@@ -83,7 +84,7 @@ const CommentEditor = forwardRef(
} }
} }
if ((event.ctrlKey || event.metaKey) && event.key === "Enter") { if (platformModifierKey(event) && event.code === "Enter") {
event.preventDefault(); event.preventDefault();
if (onSave) onSave(); if (onSave) onSave();
@@ -62,7 +62,7 @@ import { useIdle } from "@/hooks/use-idle.ts";
import { queryClient } from "@/main.tsx"; import { queryClient } from "@/main.tsx";
import { IPage } from "@/features/page/types/page.types.ts"; import { IPage } from "@/features/page/types/page.types.ts";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import { extractPageSlugId } from "@/lib"; import { extractPageSlugId, platformModifierKey } from "@/lib";
import { FIVE_MINUTES } from "@/lib/constants.ts"; import { FIVE_MINUTES } from "@/lib/constants.ts";
import { PageEditMode } from "@/features/user/types/user.types.ts"; import { PageEditMode } from "@/features/user/types/user.types.ts";
import { jwtDecode } from "jwt-decode"; import { jwtDecode } from "jwt-decode";
@@ -232,11 +232,11 @@ export default function PageEditor({
scrollMargin: 80, scrollMargin: 80,
handleDOMEvents: { handleDOMEvents: {
keydown: (_view, event) => { keydown: (_view, event) => {
if ((event.ctrlKey || event.metaKey) && event.code === "KeyS") { if (platformModifierKey(event) && event.code === "KeyS") {
event.preventDefault(); event.preventDefault();
return true; return true;
} }
if ((event.ctrlKey || event.metaKey) && event.code === "KeyK") { if (platformModifierKey(event) && event.code === "KeyK") {
searchSpotlight.open(); searchSpotlight.open();
return true; return true;
} }
@@ -27,6 +27,7 @@ import localEmitter from "@/lib/local-emitter.ts";
import { currentUserAtom } from "@/features/user/atoms/current-user-atom.ts"; import { currentUserAtom } from "@/features/user/atoms/current-user-atom.ts";
import { PageEditMode } from "@/features/user/types/user.types.ts"; import { PageEditMode } from "@/features/user/types/user.types.ts";
import { searchSpotlight } from "@/features/search/constants.ts"; import { searchSpotlight } from "@/features/search/constants.ts";
import { platformModifierKey } from "@/lib";
export interface TitleEditorProps { export interface TitleEditorProps {
pageId: string; pageId: string;
@@ -90,11 +91,11 @@ export function TitleEditor({
editorProps: { editorProps: {
handleDOMEvents: { handleDOMEvents: {
keydown: (_view, event) => { keydown: (_view, event) => {
if ((event.ctrlKey || event.metaKey) && event.code === "KeyS") { if (platformModifierKey(event) && event.code === "KeyS") {
event.preventDefault(); event.preventDefault();
return true; return true;
} }
if ((event.ctrlKey || event.metaKey) && event.code === "KeyK") { if (platformModifierKey(event) && event.code === "KeyK") {
searchSpotlight.open(); searchSpotlight.open();
return true; return true;
} }
@@ -13,6 +13,7 @@ import {
import classes from "./search-control.module.css"; import classes from "./search-control.module.css";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { platformModifierLabel } from "@/lib";
interface SearchControlProps extends BoxProps, ElementProps<"button"> {} interface SearchControlProps extends BoxProps, ElementProps<"button"> {}
@@ -27,7 +28,7 @@ export function SearchControl({ className, ...others }: SearchControlProps) {
{t("Search")} {t("Search")}
</Text> </Text>
<Text fw={700} className={classes.shortcut}> <Text fw={700} className={classes.shortcut}>
Ctrl + K {platformModifierLabel} + K
</Text> </Text>
</Group> </Group>
</UnstyledButton> </UnstyledButton>
+9
View File
@@ -100,6 +100,15 @@ export const normalizeUrl = (url: string): string => {
return `https://${url}`; return `https://${url}`;
}; };
const _isApple = /mac|iphone|ipad|ipod/i.test(navigator.platform ?? "");
/// Cmd key on Apple devices, Ctrl key everywhere else
export function platformModifierKey(event: KeyboardEvent): boolean {
return _isApple ? event.metaKey : event.ctrlKey;
}
export const platformModifierLabel = _isApple ? "⌘" : "Ctrl";
export function castToBoolean(value: unknown): boolean { export function castToBoolean(value: unknown): boolean {
if (value == null) { if (value == null) {
return false; return false;