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
ef8e3cfb
Commit
ef8e3cfb
authored
Jan 02, 2026
by
Johnny
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
chore: remove unused keyboard shortcuts
parent
02f39c2a
Changes
10
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
16 additions
and
129 deletions
+16
-129
FocusModeOverlay.tsx
...src/components/MemoEditor/components/FocusModeOverlay.tsx
+1
-9
constants.ts
web/src/components/MemoEditor/constants.ts
+0
-3
useKeyboard.ts
web/src/components/MemoEditor/hooks/useKeyboard.ts
+0
-14
index.tsx
web/src/components/MemoEditor/index.tsx
+1
-2
MemoView.tsx
web/src/components/MemoView/MemoView.tsx
+2
-31
constants.ts
web/src/components/MemoView/constants.ts
+0
-4
index.ts
web/src/components/MemoView/hooks/index.ts
+0
-1
useKeyboardShortcuts.ts
web/src/components/MemoView/hooks/useKeyboardShortcuts.ts
+0
-48
SearchBar.tsx
web/src/components/SearchBar.tsx
+1
-13
LocationPicker.tsx
web/src/components/map/LocationPicker.tsx
+11
-4
No files found.
web/src/components/MemoEditor/components/FocusModeOverlay.tsx
View file @
ef8e3cfb
...
...
@@ -6,15 +6,7 @@ import type { FocusModeExitButtonProps, FocusModeOverlayProps } from "../types";
export
function
FocusModeOverlay
({
isActive
,
onToggle
}:
FocusModeOverlayProps
)
{
if
(
!
isActive
)
return
null
;
return
(
<
button
type=
"button"
className=
{
FOCUS_MODE_STYLES
.
backdrop
}
onClick=
{
onToggle
}
onKeyDown=
{
(
e
)
=>
e
.
key
===
"Escape"
&&
onToggle
()
}
aria
-
label=
"Exit focus mode"
/>
);
return
<
button
type=
"button"
className=
{
FOCUS_MODE_STYLES
.
backdrop
}
onClick=
{
onToggle
}
aria
-
label=
"Exit focus mode"
/>;
}
export
function
FocusModeExitButton
({
isActive
,
onToggle
,
title
}:
FocusModeExitButtonProps
)
{
...
...
web/src/components/MemoEditor/constants.ts
View file @
ef8e3cfb
...
...
@@ -10,9 +10,6 @@ export const FOCUS_MODE_STYLES = {
exitButton
:
"absolute top-2 right-2 z-10 opacity-60 hover:opacity-100"
,
}
as
const
;
export
const
FOCUS_MODE_TOGGLE_KEY
=
"f"
;
export
const
FOCUS_MODE_EXIT_KEY
=
"Escape"
;
export
const
EDITOR_HEIGHT
=
{
// Max height for normal mode - focus mode uses flex-1 to grow dynamically
normal
:
"max-h-[50vh]"
,
...
...
web/src/components/MemoEditor/hooks/useKeyboard.ts
View file @
ef8e3cfb
import
{
useEffect
}
from
"react"
;
import
{
FOCUS_MODE_TOGGLE_KEY
}
from
"../constants"
;
import
type
{
EditorRefActions
}
from
"../Editor"
;
interface
UseKeyboardOptions
{
onSave
:
()
=>
void
;
onToggleFocusMode
?:
()
=>
void
;
}
export
const
useKeyboard
=
(
_editorRef
:
React
.
RefObject
<
EditorRefActions
|
null
>
,
options
:
UseKeyboardOptions
)
=>
{
useEffect
(()
=>
{
const
handleKeyDown
=
(
event
:
KeyboardEvent
)
=>
{
// Cmd/Ctrl + Enter to save
if
((
event
.
metaKey
||
event
.
ctrlKey
)
&&
event
.
key
===
"Enter"
)
{
event
.
preventDefault
();
options
.
onSave
();
return
;
}
// 'f' key to toggle focus mode (only if no modifiers and not in input)
if
(
event
.
key
===
FOCUS_MODE_TOGGLE_KEY
&&
!
event
.
metaKey
&&
!
event
.
ctrlKey
&&
!
event
.
altKey
&&
!
event
.
shiftKey
)
{
const
target
=
event
.
target
as
HTMLElement
;
// Don't trigger if user is typing in an input/textarea
if
(
target
.
tagName
!==
"INPUT"
&&
target
.
tagName
!==
"TEXTAREA"
&&
!
target
.
isContentEditable
)
{
event
.
preventDefault
();
options
.
onToggleFocusMode
?.();
}
}
};
...
...
web/src/components/MemoEditor/index.tsx
View file @
ef8e3cfb
...
...
@@ -62,8 +62,7 @@ const MemoEditorImpl: React.FC<MemoEditorProps> = ({
dispatch
(
actions
.
toggleFocusMode
());
};
// Keyboard shortcuts
useKeyboard
(
editorRef
,
{
onSave
:
handleSave
,
onToggleFocusMode
:
handleToggleFocusMode
});
useKeyboard
(
editorRef
,
{
onSave
:
handleSave
});
async
function
handleSave
()
{
// Validate before saving
...
...
web/src/components/MemoView/MemoView.tsx
View file @
ef8e3cfb
...
...
@@ -5,31 +5,10 @@ import MemoEditor from "../MemoEditor";
import
PreviewImageDialog
from
"../PreviewImageDialog"
;
import
{
MemoBody
,
MemoHeader
}
from
"./components"
;
import
{
MEMO_CARD_BASE_CLASSES
}
from
"./constants"
;
import
{
useImagePreview
,
use
KeyboardShortcuts
,
use
MemoActions
,
useMemoHandlers
,
useMemoViewDerivedState
,
useNsfwContent
}
from
"./hooks"
;
import
{
useImagePreview
,
useMemoActions
,
useMemoHandlers
,
useMemoViewDerivedState
,
useNsfwContent
}
from
"./hooks"
;
import
{
MemoViewContext
}
from
"./MemoViewContext"
;
import
type
{
MemoViewProps
}
from
"./types"
;
/**
* MemoView component displays a memo card with all its content, metadata, and interactive elements.
*
* Features:
* - Displays memo content with markdown rendering
* - Shows creator information and timestamps
* - Supports inline editing with keyboard shortcuts (e = edit, a = archive)
* - Handles NSFW content blurring
* - Image preview on click
* - Comments, reactions, and relations
*
* @example
* ```tsx
* <MemoView
* memo={memoData}
* showCreator
* showVisibility
* compact={false}
* />
* ```
*/
const
MemoView
:
React
.
FC
<
MemoViewProps
>
=
(
props
:
MemoViewProps
)
=>
{
const
{
memo
:
memoData
,
className
}
=
props
;
const
cardRef
=
useRef
<
HTMLDivElement
>
(
null
);
...
...
@@ -40,7 +19,7 @@ const MemoView: React.FC<MemoViewProps> = (props: MemoViewProps) => {
const
{
isArchived
,
readonly
,
parentPage
}
=
useMemoViewDerivedState
(
memoData
,
props
.
parentPage
);
const
{
nsfw
,
showNSFWContent
,
toggleNsfwVisibility
}
=
useNsfwContent
(
memoData
,
props
.
showNsfwContent
);
const
{
previewState
,
openPreview
,
setPreviewOpen
}
=
useImagePreview
();
const
{
archiveMemo
,
unpinMemo
}
=
useMemoActions
(
memoData
,
isArchived
);
const
{
unpinMemo
}
=
useMemoActions
(
memoData
,
isArchived
);
const
handleEditorConfirm
=
()
=>
setShowEditor
(
false
);
const
handleEditorCancel
=
()
=>
setShowEditor
(
false
);
...
...
@@ -53,14 +32,6 @@ const MemoView: React.FC<MemoViewProps> = (props: MemoViewProps) => {
openEditor
,
openPreview
,
});
useKeyboardShortcuts
(
cardRef
,
{
enabled
:
true
,
readonly
,
showEditor
,
isArchived
,
onEdit
:
openEditor
,
onArchive
:
archiveMemo
,
});
const
contextValue
=
useMemo
(
()
=>
({
...
...
web/src/components/MemoView/constants.ts
View file @
ef8e3cfb
export
const
MEMO_CARD_BASE_CLASSES
=
"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"
;
export
const
KEYBOARD_SHORTCUTS
=
{
EDIT
:
"e"
,
ARCHIVE
:
"a"
}
as
const
;
export
const
TEXT_INPUT_TYPES
=
[
"text"
,
"search"
,
"email"
,
"password"
,
"url"
,
"tel"
,
"number"
]
as
const
;
export
const
RELATIVE_TIME_THRESHOLD_MS
=
1000
*
60
*
60
*
24
;
web/src/components/MemoView/hooks/index.ts
View file @
ef8e3cfb
export
{
useImagePreview
}
from
"./useImagePreview"
;
export
{
useKeyboardShortcuts
}
from
"./useKeyboardShortcuts"
;
export
{
useMemoActions
}
from
"./useMemoActions"
;
export
{
useMemoHandlers
}
from
"./useMemoHandlers"
;
export
{
useMemoViewDerivedState
}
from
"./useMemoViewDerivedState"
;
...
...
web/src/components/MemoView/hooks/useKeyboardShortcuts.ts
deleted
100644 → 0
View file @
02f39c2a
import
{
useEffect
}
from
"react"
;
import
{
KEYBOARD_SHORTCUTS
,
TEXT_INPUT_TYPES
}
from
"../constants"
;
export
interface
UseKeyboardShortcutsOptions
{
enabled
:
boolean
;
readonly
:
boolean
;
showEditor
:
boolean
;
isArchived
:
boolean
;
onEdit
:
()
=>
void
;
onArchive
:
()
=>
Promise
<
void
>
;
}
const
isTextInputElement
=
(
element
:
HTMLElement
|
null
):
boolean
=>
{
if
(
!
element
)
return
false
;
if
(
element
.
isContentEditable
)
return
true
;
if
(
element
instanceof
HTMLTextAreaElement
)
return
true
;
if
(
element
instanceof
HTMLInputElement
)
{
return
TEXT_INPUT_TYPES
.
includes
(
element
.
type
as
(
typeof
TEXT_INPUT_TYPES
)[
number
]);
}
return
false
;
};
export
const
useKeyboardShortcuts
=
(
cardRef
:
React
.
RefObject
<
HTMLDivElement
|
null
>
,
options
:
UseKeyboardShortcutsOptions
)
=>
{
const
{
enabled
,
readonly
,
showEditor
,
isArchived
,
onEdit
,
onArchive
}
=
options
;
useEffect
(()
=>
{
if
(
!
enabled
||
readonly
||
showEditor
||
!
cardRef
.
current
)
return
;
const
cardEl
=
cardRef
.
current
;
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
===
KEYBOARD_SHORTCUTS
.
EDIT
)
{
event
.
preventDefault
();
onEdit
();
}
else
if
(
key
===
KEYBOARD_SHORTCUTS
.
ARCHIVE
&&
!
isArchived
)
{
event
.
preventDefault
();
onArchive
();
}
};
cardEl
.
addEventListener
(
"keydown"
,
handleKeyDown
);
return
()
=>
cardEl
.
removeEventListener
(
"keydown"
,
handleKeyDown
);
},
[
enabled
,
readonly
,
showEditor
,
isArchived
,
onEdit
,
onArchive
,
cardRef
]);
};
web/src/components/SearchBar.tsx
View file @
ef8e3cfb
import
{
SearchIcon
}
from
"lucide-react"
;
import
{
use
Effect
,
use
Ref
,
useState
}
from
"react"
;
import
{
useRef
,
useState
}
from
"react"
;
import
{
useMemoFilterContext
}
from
"@/contexts/MemoFilterContext"
;
import
{
cn
}
from
"@/lib/utils"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
...
...
@@ -11,18 +11,6 @@ const SearchBar = () => {
const
[
queryText
,
setQueryText
]
=
useState
(
""
);
const
inputRef
=
useRef
<
HTMLInputElement
>
(
null
);
useEffect
(()
=>
{
const
handleGlobalShortcut
=
(
event
:
KeyboardEvent
)
=>
{
if
((
event
.
metaKey
||
event
.
ctrlKey
)
&&
event
.
key
===
"/"
)
{
event
.
preventDefault
();
inputRef
.
current
?.
focus
();
}
};
window
.
addEventListener
(
"keydown"
,
handleGlobalShortcut
);
return
()
=>
window
.
removeEventListener
(
"keydown"
,
handleGlobalShortcut
);
},
[]);
const
onTextChange
=
(
event
:
React
.
FormEvent
<
HTMLInputElement
>
)
=>
{
setQueryText
(
event
.
currentTarget
.
value
);
};
...
...
web/src/components/map/LocationPicker.tsx
View file @
ef8e3cfb
...
...
@@ -27,7 +27,7 @@ const LocationMarker = (props: MarkerProps) => {
// Call the parent onChange function.
props
.
onChange
(
e
.
latlng
);
},
locationfound
()
{
},
locationfound
()
{},
});
useEffect
(()
=>
{
...
...
@@ -224,12 +224,19 @@ const LeafletMap = (props: MapProps) => {
const
position
=
props
.
latlng
||
DEFAULT_CENTER_LAT_LNG
;
return
(
<
MapContainer
className=
"w-full h-72"
center=
{
position
}
zoom=
{
13
}
scrollWheelZoom=
{
false
}
zoomControl=
{
false
}
attributionControl=
{
false
}
>
<
MapContainer
className=
"w-full h-72"
center=
{
position
}
zoom=
{
13
}
scrollWheelZoom=
{
false
}
zoomControl=
{
false
}
attributionControl=
{
false
}
>
<
ThemedTileLayer
/>
<
LocationMarker
position=
{
position
}
readonly=
{
props
.
readonly
}
onChange=
{
props
.
onChange
?
props
.
onChange
:
()
=>
{
}
}
/>
<
LocationMarker
position=
{
position
}
readonly=
{
props
.
readonly
}
onChange=
{
props
.
onChange
?
props
.
onChange
:
()
=>
{}
}
/>
<
MapControls
position=
{
props
.
latlng
}
/>
<
MapCleanup
/>
</
MapContainer
>
</
MapContainer
>
);
};
...
...
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