Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
C
canifa_note
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Vũ Hoàng Anh
canifa_note
Commits
4de8712c
Unverified
Commit
4de8712c
authored
Nov 17, 2025
by
Neo
Committed by
GitHub
Nov 17, 2025
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix: keyboard shortcuts (#5250)
parent
35711880
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
114 additions
and
3 deletions
+114
-3
index.tsx
web/src/components/MemoEditor/index.tsx
+6
-0
MemoView.tsx
web/src/components/MemoView.tsx
+93
-2
SearchBar.tsx
web/src/components/SearchBar.tsx
+15
-1
No files found.
web/src/components/MemoEditor/index.tsx
View file @
4de8712c
...
...
@@ -197,6 +197,12 @@ const MemoEditor = observer((props: Props) => {
if
(
isMetaKey
)
{
if
(
event
.
key
===
"Enter"
)
{
event
.
preventDefault
();
handleSaveBtnClick
();
return
;
}
if
(
event
.
key
.
toLowerCase
()
===
"s"
)
{
event
.
preventDefault
();
handleSaveBtnClick
();
return
;
}
...
...
web/src/components/MemoView.tsx
View file @
4de8712c
import
{
BookmarkIcon
,
EyeOffIcon
,
MessageCircleMoreIcon
}
from
"lucide-react"
;
import
{
observer
}
from
"mobx-react-lite"
;
import
{
memo
,
useCallback
,
useState
}
from
"react"
;
import
{
memo
,
useCallback
,
useEffect
,
useRef
,
useState
}
from
"react"
;
import
toast
from
"react-hot-toast"
;
import
{
Link
,
useLocation
}
from
"react-router-dom"
;
import
{
Tooltip
,
TooltipContent
,
TooltipProvider
,
TooltipTrigger
}
from
"@/components/ui/tooltip"
;
import
useAsyncEffect
from
"@/hooks/useAsyncEffect"
;
...
...
@@ -50,6 +51,8 @@ const MemoView: React.FC<Props> = observer((props: Props) => {
urls
:
[],
index
:
0
,
});
const
[
shortcutActive
,
setShortcutActive
]
=
useState
(
false
);
const
cardRef
=
useRef
<
HTMLDivElement
>
(
null
);
const
instanceMemoRelatedSetting
=
instanceStore
.
state
.
memoRelatedSetting
;
const
referencedMemos
=
memo
.
relations
.
filter
((
relation
)
=>
relation
.
type
===
MemoRelation_Type
.
REFERENCE
);
const
commentAmount
=
memo
.
relations
.
filter
(
...
...
@@ -124,6 +127,89 @@ const MemoView: React.FC<Props> = observer((props: Props) => {
}
};
const
archiveMemo
=
useCallback
(
async
()
=>
{
if
(
isArchived
)
{
return
;
}
try
{
await
memoStore
.
updateMemo
(
{
name
:
memo
.
name
,
state
:
State
.
ARCHIVED
,
},
[
"state"
],
);
toast
.
success
(
t
(
"message.archived-successfully"
));
userStore
.
setStatsStateId
();
}
catch
(
error
:
any
)
{
console
.
error
(
error
);
toast
.
error
(
error
?.
details
);
}
},
[
isArchived
,
memo
.
name
,
t
,
memoStore
,
userStore
]);
useEffect
(()
=>
{
if
(
!
shortcutActive
||
readonly
||
showEditor
||
!
cardRef
.
current
)
{
return
;
}
const
cardEl
=
cardRef
.
current
;
const
isTextInputElement
=
(
element
:
HTMLElement
|
null
)
=>
{
if
(
!
element
)
{
return
false
;
}
if
(
element
.
isContentEditable
)
{
return
true
;
}
if
(
element
instanceof
HTMLTextAreaElement
)
{
return
true
;
}
if
(
element
instanceof
HTMLInputElement
)
{
const
textTypes
=
[
"text"
,
"search"
,
"email"
,
"password"
,
"url"
,
"tel"
,
"number"
];
return
textTypes
.
includes
(
element
.
type
||
"text"
);
}
return
false
;
};
const
handleKeyDown
=
(
event
:
KeyboardEvent
)
=>
{
const
target
=
event
.
target
as
HTMLElement
|
null
;
if
(
!
cardEl
.
contains
(
target
)
||
isTextInputElement
(
target
))
{
return
;
}
if
(
event
.
metaKey
||
event
.
ctrlKey
||
event
.
altKey
)
{
return
;
}
const
key
=
event
.
key
.
toLowerCase
();
if
(
key
===
"e"
)
{
event
.
preventDefault
();
setShowEditor
(
true
);
}
else
if
(
key
===
"a"
&&
!
isArchived
)
{
event
.
preventDefault
();
archiveMemo
();
}
};
cardEl
.
addEventListener
(
"keydown"
,
handleKeyDown
);
return
()
=>
cardEl
.
removeEventListener
(
"keydown"
,
handleKeyDown
);
},
[
shortcutActive
,
readonly
,
showEditor
,
isArchived
,
archiveMemo
]);
useEffect
(()
=>
{
if
(
showEditor
||
readonly
)
{
setShortcutActive
(
false
);
}
},
[
showEditor
,
readonly
]);
const
handleShortcutActivation
=
(
active
:
boolean
)
=>
{
if
(
readonly
)
{
return
;
}
setShortcutActive
(
active
);
};
const
displayTime
=
isArchived
?
(
memo
.
displayTime
?.
toLocaleString
()
)
:
(
...
...
@@ -142,9 +228,14 @@ const MemoView: React.FC<Props> = observer((props: Props) => {
)
:
(
<
div
className=
{
cn
(
"relative group flex flex-col justify-start items-start bg-card w-full px-4 py-3 mb-2 gap-2 text-card-foreground rounded-lg border border-border transition-colors"
,
"relative group flex flex-col justify-start items-start bg-card w-full px-4 py-3 mb-2 gap-2 text-card-foreground rounded-lg border border-border transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
,
shortcutActive
&&
!
showEditor
&&
"border-ring ring-2 ring-ring bg-accent/10"
,
className
,
)
}
ref=
{
cardRef
}
tabIndex=
{
readonly
?
-
1
:
0
}
onFocus=
{
()
=>
handleShortcutActivation
(
true
)
}
onBlur=
{
()
=>
handleShortcutActivation
(
false
)
}
>
<
div
className=
"w-full flex flex-row justify-between items-center gap-2"
>
<
div
className=
"w-auto max-w-[calc(100%-8rem)] grow flex flex-row justify-start items-center"
>
...
...
web/src/components/SearchBar.tsx
View file @
4de8712c
import
{
SearchIcon
}
from
"lucide-react"
;
import
{
observer
}
from
"mobx-react-lite"
;
import
{
useState
}
from
"react"
;
import
{
use
Effect
,
useRef
,
use
State
}
from
"react"
;
import
{
cn
}
from
"@/lib/utils"
;
import
{
memoFilterStore
}
from
"@/store"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
...
...
@@ -9,6 +9,19 @@ import MemoDisplaySettingMenu from "./MemoDisplaySettingMenu";
const
SearchBar
=
observer
(()
=>
{
const
t
=
useTranslate
();
const
[
queryText
,
setQueryText
]
=
useState
(
""
);
const
inputRef
=
useRef
<
HTMLInputElement
>
(
null
);
useEffect
(()
=>
{
const
handleGlobalShortcut
=
(
event
:
KeyboardEvent
)
=>
{
if
((
event
.
metaKey
||
event
.
ctrlKey
)
&&
event
.
key
.
toLowerCase
()
===
"k"
)
{
event
.
preventDefault
();
inputRef
.
current
?.
focus
();
}
};
window
.
addEventListener
(
"keydown"
,
handleGlobalShortcut
);
return
()
=>
window
.
removeEventListener
(
"keydown"
,
handleGlobalShortcut
);
},
[]);
const
onTextChange
=
(
event
:
React
.
FormEvent
<
HTMLInputElement
>
)
=>
{
setQueryText
(
event
.
currentTarget
.
value
);
...
...
@@ -40,6 +53,7 @@ const SearchBar = observer(() => {
value=
{
queryText
}
onChange=
{
onTextChange
}
onKeyDown=
{
onKeyDown
}
ref=
{
inputRef
}
/>
<
MemoDisplaySettingMenu
className=
"absolute right-2 top-2 text-sidebar-foreground"
/>
</
div
>
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment