Skip to content

Commit f17b0d2

Browse files
committed
Refactor PromptField event handlers into a modular directory structure
- from `packages/ui/src/components/editor/panel/prompts/PromptField/hooks/use-handlers/use-handlers.ts` move indivual handle functions to a separate file for each
1 parent c2e7561 commit f17b0d2

12 files changed

Lines changed: 1285 additions & 1015 deletions

File tree

packages/ui/src/components/editor/panel/prompts/PromptField/hooks/use-handlers.ts

Lines changed: 0 additions & 1015 deletions
This file was deleted.
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { map_display_pos_to_raw_pos } from '../../../shared/symbols'
2+
import { HandlerContext } from './types'
3+
4+
export const create_handle_copy =
5+
({ props, params }: HandlerContext) =>
6+
(e: React.ClipboardEvent<HTMLDivElement>) => {
7+
const selection = window.getSelection()
8+
if (!selection || selection.rangeCount == 0 || selection.isCollapsed) return
9+
10+
const range = selection.getRangeAt(0)
11+
const input_element = params.input_ref.current
12+
if (!input_element || !input_element.contains(range.startContainer)) return
13+
14+
e.preventDefault()
15+
16+
const pre_selection_range = document.createRange()
17+
pre_selection_range.selectNodeContents(input_element)
18+
pre_selection_range.setEnd(range.startContainer, range.startOffset)
19+
const display_start = pre_selection_range.toString().length
20+
21+
pre_selection_range.setEnd(range.endContainer, range.endOffset)
22+
const display_end = pre_selection_range.toString().length
23+
24+
const raw_start = map_display_pos_to_raw_pos({
25+
display_pos: display_start,
26+
raw_text: props.value,
27+
context_file_paths: props.context_file_paths ?? []
28+
})
29+
const raw_end = map_display_pos_to_raw_pos({
30+
display_pos: display_end,
31+
raw_text: props.value,
32+
context_file_paths: props.context_file_paths ?? []
33+
})
34+
35+
const raw_text_slice = props.value.substring(raw_start, raw_end)
36+
e.clipboardData.setData('text/plain', raw_text_slice)
37+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { map_display_pos_to_raw_pos } from '../../../shared/symbols'
2+
import { HandlerContext } from './types'
3+
4+
export const create_handle_cut =
5+
({ props, params, refs, utils }: HandlerContext) =>
6+
(e: React.ClipboardEvent<HTMLDivElement>) => {
7+
const selection = window.getSelection()
8+
if (!selection || selection.rangeCount == 0 || selection.isCollapsed) return
9+
10+
const range = selection.getRangeAt(0)
11+
const input_element = params.input_ref.current
12+
if (!input_element || !input_element.contains(range.startContainer)) return
13+
14+
e.preventDefault()
15+
16+
const pre_selection_range = document.createRange()
17+
pre_selection_range.selectNodeContents(input_element)
18+
pre_selection_range.setEnd(range.startContainer, range.startOffset)
19+
const display_start = pre_selection_range.toString().length
20+
21+
pre_selection_range.setEnd(range.endContainer, range.endOffset)
22+
const display_end = pre_selection_range.toString().length
23+
24+
const raw_start = map_display_pos_to_raw_pos({
25+
display_pos: display_start,
26+
raw_text: props.value,
27+
context_file_paths: props.context_file_paths ?? []
28+
})
29+
const raw_end = map_display_pos_to_raw_pos({
30+
display_pos: display_end,
31+
raw_text: props.value,
32+
context_file_paths: props.context_file_paths ?? []
33+
})
34+
35+
const raw_text_slice = props.value.substring(raw_start, raw_end)
36+
e.clipboardData.setData('text/plain', raw_text_slice)
37+
38+
const new_value =
39+
props.value.substring(0, raw_start) + props.value.substring(raw_end)
40+
41+
refs.has_modified_current_entry_ref.current = true
42+
utils.update_value(new_value, raw_start)
43+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import {
2+
reconstruct_raw_value_from_node,
3+
get_caret_position_from_div
4+
} from '../../../shared/symbols'
5+
import { HandlerContext } from './types'
6+
7+
export const create_handle_input_change =
8+
({ props, refs, state, utils }: HandlerContext) =>
9+
(e: React.FormEvent<HTMLDivElement>) => {
10+
const currentTarget = e.currentTarget
11+
const new_raw_value = reconstruct_raw_value_from_node(currentTarget)
12+
13+
if (new_raw_value === props.value) {
14+
return
15+
}
16+
17+
const new_display_value = currentTarget.textContent ?? ''
18+
const caret_position = get_caret_position_from_div(currentTarget)
19+
const char_before_caret = new_display_value.charAt(caret_position - 1)
20+
21+
refs.has_modified_current_entry_ref.current = new_raw_value != ''
22+
utils.update_value(new_raw_value)
23+
24+
const native_event = e.nativeEvent as unknown as { inputType?: string }
25+
if (native_event.inputType?.startsWith('delete')) {
26+
state.set_history_index(-1)
27+
return
28+
}
29+
30+
if (char_before_caret == '@') {
31+
let should_trigger = true
32+
if (caret_position > 1) {
33+
const char_before_at = new_display_value.charAt(caret_position - 2)
34+
if (char_before_at == '@') {
35+
should_trigger = false
36+
}
37+
}
38+
39+
if (should_trigger) {
40+
props.on_at_sign_click()
41+
}
42+
} else if (char_before_caret == '#') {
43+
const is_at_start = caret_position == 1
44+
let is_after_whitespace = false
45+
if (caret_position > 1) {
46+
const char_before_hash = new_display_value.charAt(caret_position - 2)
47+
is_after_whitespace = /\s/.test(char_before_hash)
48+
}
49+
50+
if (is_at_start || is_after_whitespace) {
51+
props.on_hash_sign_click()
52+
}
53+
}
54+
55+
state.set_history_index(-1)
56+
}
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
import { HandlerContext } from './types'
2+
3+
export const create_handle_input_click = ({
4+
props,
5+
params,
6+
refs,
7+
state,
8+
utils
9+
}: HandlerContext) => {
10+
const handle_clear = () => {
11+
refs.has_modified_current_entry_ref.current = false
12+
utils.update_value('')
13+
state.set_history_index(-1)
14+
}
15+
16+
return (e: React.MouseEvent<HTMLDivElement>) => {
17+
const target = e.target as HTMLElement
18+
const icon_element = target.closest('[data-role="symbol-icon"]')
19+
const text_element = target.closest('[data-role="symbol-text"]')
20+
const clear_button = target.closest('[data-role="clear-button"]')
21+
const tab_item = target.closest('[data-role="tab-item"]')
22+
const tab_new = target.closest('[data-role="tab-new"]')
23+
24+
if (icon_element) {
25+
e.preventDefault()
26+
e.stopPropagation()
27+
const symbol_element = (icon_element as HTMLElement).closest<HTMLElement>(
28+
'[data-type]'
29+
)
30+
if (symbol_element) {
31+
utils.handle_symbol_deletion_by_click(symbol_element)
32+
}
33+
} else if (text_element) {
34+
e.preventDefault()
35+
e.stopPropagation()
36+
37+
const file_symbol_element = text_element.closest<HTMLElement>(
38+
'[data-type="file-symbol"]'
39+
)
40+
if (file_symbol_element) {
41+
const file_path = file_symbol_element.getAttribute('title')
42+
if (file_path) {
43+
props.on_go_to_file(file_path)
44+
}
45+
}
46+
47+
const pasted_lines_symbol_element = text_element.closest<HTMLElement>(
48+
'[data-type="pasted-lines-symbol"]'
49+
)
50+
if (pasted_lines_symbol_element) {
51+
const path = pasted_lines_symbol_element.dataset.path
52+
const start = pasted_lines_symbol_element.dataset.start
53+
const end = pasted_lines_symbol_element.dataset.end
54+
if (path) {
55+
props.on_pasted_lines_click(path, start, end)
56+
}
57+
}
58+
59+
const skill_symbol_element = text_element.closest<HTMLElement>(
60+
'[data-type="skill-symbol"]'
61+
)
62+
if (skill_symbol_element) {
63+
const repo = skill_symbol_element.dataset.repo
64+
const skill_name = skill_symbol_element.dataset.skillName
65+
66+
if (repo && repo != 'local' && skill_name) {
67+
const parts = repo.split(':')
68+
if (parts.length == 2) {
69+
const [user, repo_name] = parts
70+
const url = `https://skills.sh/${user}/${repo_name}/${skill_name}`
71+
props.on_open_url(url)
72+
}
73+
}
74+
}
75+
76+
const image_symbol_element = text_element.closest<HTMLElement>(
77+
'[data-type="image-symbol"]'
78+
)
79+
if (image_symbol_element) {
80+
const hash = image_symbol_element.dataset.hash
81+
if (hash) {
82+
props.on_open_image(hash)
83+
}
84+
}
85+
86+
const pasted_text_symbol_element = text_element.closest<HTMLElement>(
87+
'[data-type="pasted-text-symbol"]'
88+
)
89+
if (pasted_text_symbol_element) {
90+
const hash = pasted_text_symbol_element.dataset.hash
91+
if (hash) {
92+
props.on_open_pasted_text(hash)
93+
}
94+
}
95+
96+
const website_symbol_element = text_element.closest<HTMLElement>(
97+
'[data-type="website-symbol"]'
98+
)
99+
if (website_symbol_element) {
100+
const url = website_symbol_element.dataset.url
101+
if (url) {
102+
props.on_open_website(url)
103+
}
104+
}
105+
106+
if (params.input_ref.current) {
107+
params.input_ref.current.focus()
108+
}
109+
} else if (clear_button) {
110+
e.preventDefault()
111+
e.stopPropagation()
112+
if (props.value) {
113+
handle_clear()
114+
} else if (props.tabs_count > 1) {
115+
props.on_tab_delete(props.active_tab_index)
116+
}
117+
if (params.input_ref.current) {
118+
params.input_ref.current.focus()
119+
}
120+
} else if (tab_item) {
121+
e.preventDefault()
122+
e.stopPropagation()
123+
const index = parseInt((tab_item as HTMLElement).dataset.index || '0')
124+
if (index !== props.active_tab_index) {
125+
props.on_tab_change?.(index)
126+
}
127+
} else if (tab_new) {
128+
e.preventDefault()
129+
e.stopPropagation()
130+
props.on_new_tab?.()
131+
}
132+
}
133+
}

0 commit comments

Comments
 (0)