@salesforcedevs/docs-components 1.17.11-hover-edit → 1.17.11-lwc-fix-alpha1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lwc.config.json +0 -2
- package/package.json +1 -1
- package/src/modules/doc/componentPlayground/componentPlayground.css +1 -1
- package/src/modules/doc/componentPlayground/componentPlayground.html +4 -4
- package/src/modules/doc/componentPlayground/componentPlayground.ts +13 -0
- package/src/modules/doc/lwcContentLayout/lwcContentLayout.ts +103 -15
- package/src/modules/doc/specificationContent/specificationContent.html +2 -6
- package/src/modules/doc/xmlContent/xmlContent.html +9 -1
- package/src/modules/doc/xmlContent/xmlContent.ts +18 -8
- package/src/modules/doc/markdownEditor/markdownEditor.css +0 -225
- package/src/modules/doc/markdownEditor/markdownEditor.html +0 -80
- package/src/modules/doc/markdownEditor/markdownEditor.ts +0 -148
- package/src/modules/doc/textSelectionSearch/README.md +0 -185
- package/src/modules/doc/textSelectionSearch/textSelectionSearch.css +0 -285
- package/src/modules/doc/textSelectionSearch/textSelectionSearch.html +0 -91
- package/src/modules/doc/textSelectionSearch/textSelectionSearch.js +0 -236
- package/src/modules/doc/textSelectionSearch/textSelectionSearch.ts +0 -446
|
@@ -1,236 +0,0 @@
|
|
|
1
|
-
import { LightningElement, api, track } from "lwc";
|
|
2
|
-
|
|
3
|
-
export default class TextSelectionSearch extends LightningElement {
|
|
4
|
-
@api searchApiUrl = "";
|
|
5
|
-
@api repoUrl = "";
|
|
6
|
-
@api placeholder = "Search for...";
|
|
7
|
-
@api popoverPosition = "top";
|
|
8
|
-
@api maxResults = 10;
|
|
9
|
-
|
|
10
|
-
@track isVisible = false;
|
|
11
|
-
@track isHidden = false;
|
|
12
|
-
@track searchQuery = "";
|
|
13
|
-
@track isLoading = false;
|
|
14
|
-
@track isSuccess = false;
|
|
15
|
-
@track errorMessage = "";
|
|
16
|
-
@track popoverStyle = "";
|
|
17
|
-
|
|
18
|
-
selectedText = "";
|
|
19
|
-
selectionStart = 0;
|
|
20
|
-
selectionEnd = 0;
|
|
21
|
-
initialPopoverStyle = "";
|
|
22
|
-
|
|
23
|
-
connectedCallback() {
|
|
24
|
-
this.setupTextSelectionListener();
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
disconnectedCallback() {
|
|
28
|
-
this.removeTextSelectionListener();
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
setupTextSelectionListener() {
|
|
32
|
-
console.log('TextSelectionSearch: Setting up text selection listeners');
|
|
33
|
-
document.addEventListener("mouseup", (event) => this.handleTextSelection(event));
|
|
34
|
-
document.addEventListener("keyup", (event) => this.handleTextSelection(event));
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
removeTextSelectionListener() {
|
|
38
|
-
console.log('TextSelectionSearch: Removing text selection listeners');
|
|
39
|
-
document.removeEventListener("mouseup", (event) => this.handleTextSelection(event));
|
|
40
|
-
document.removeEventListener("keyup", (event) => this.handleTextSelection(event));
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
handleTextSelection(event) {
|
|
44
|
-
if (this.isVisible) {
|
|
45
|
-
console.log('TextSelectionSearch: Popover is visible, ignoring text selection event');
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
console.log('TextSelectionSearch: Text selection event triggered');
|
|
50
|
-
|
|
51
|
-
if (event && event.target) {
|
|
52
|
-
const target = event.target;
|
|
53
|
-
const popover = this.template.querySelector('.text-selection-search-popover');
|
|
54
|
-
if (popover && popover.contains(target)) {
|
|
55
|
-
console.log('TextSelectionSearch: Click inside popover, ignoring selection event');
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const selection = window.getSelection();
|
|
61
|
-
|
|
62
|
-
if (!selection || selection.toString().trim() === "") {
|
|
63
|
-
if (this.isVisible) {
|
|
64
|
-
console.log('TextSelectionSearch: No text selected, but popover is visible - keeping it open');
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
console.log('TextSelectionSearch: No text selected, hiding popover');
|
|
68
|
-
this.hidePopover();
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
this.selectedText = selection.toString().trim();
|
|
73
|
-
console.log('TextSelectionSearch: Selected text:', this.selectedText);
|
|
74
|
-
|
|
75
|
-
if (this.selectedText.length > 0) {
|
|
76
|
-
this.showPopover();
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
showPopover() {
|
|
81
|
-
console.log('TextSelectionSearch: Showing popover');
|
|
82
|
-
const selection = window.getSelection();
|
|
83
|
-
if (!selection || selection.rangeCount === 0) {
|
|
84
|
-
console.log('TextSelectionSearch: No selection range found');
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const range = selection.getRangeAt(0);
|
|
89
|
-
const rect = range.getBoundingClientRect();
|
|
90
|
-
|
|
91
|
-
const popoverTop = this.popoverPosition === "top"
|
|
92
|
-
? Math.max(10, rect.top - 80)
|
|
93
|
-
: Math.min(window.innerHeight - 200, rect.bottom + 20);
|
|
94
|
-
|
|
95
|
-
const popoverLeft = Math.max(10, Math.min(window.innerWidth - 350, rect.left + (rect.width / 2) - 175));
|
|
96
|
-
|
|
97
|
-
this.popoverStyle = `position: fixed; top: ${popoverTop}px; left: ${popoverLeft}px; z-index: 9999; width: 350px;`;
|
|
98
|
-
this.initialPopoverStyle = this.popoverStyle;
|
|
99
|
-
console.log('TextSelectionSearch: Popover style:', this.popoverStyle);
|
|
100
|
-
|
|
101
|
-
this.isVisible = true;
|
|
102
|
-
this.isHidden = false;
|
|
103
|
-
this.searchQuery = this.selectedText;
|
|
104
|
-
this.errorMessage = "";
|
|
105
|
-
this.isSuccess = false;
|
|
106
|
-
|
|
107
|
-
console.log('TextSelectionSearch: Popover should now be visible');
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
hidePopover() {
|
|
111
|
-
console.log('TextSelectionSearch: Hiding popover');
|
|
112
|
-
this.isHidden = true;
|
|
113
|
-
|
|
114
|
-
setTimeout(() => {
|
|
115
|
-
this.isVisible = false;
|
|
116
|
-
this.searchQuery = "";
|
|
117
|
-
this.errorMessage = "";
|
|
118
|
-
this.isSuccess = false;
|
|
119
|
-
this.popoverStyle = "";
|
|
120
|
-
this.initialPopoverStyle = "";
|
|
121
|
-
|
|
122
|
-
const selection = window.getSelection();
|
|
123
|
-
if (selection) {
|
|
124
|
-
selection.removeAllRanges();
|
|
125
|
-
}
|
|
126
|
-
}, 100);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
handlePopoverClick(event) {
|
|
130
|
-
event.stopPropagation();
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
handleInputClick(event) {
|
|
134
|
-
event.stopPropagation();
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
handleSearchInputChange(event) {
|
|
138
|
-
const target = event.target;
|
|
139
|
-
this.searchQuery = target.value;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
async handleSearchSubmit(event) {
|
|
143
|
-
event.preventDefault();
|
|
144
|
-
|
|
145
|
-
if (!this.searchQuery.trim()) {
|
|
146
|
-
this.errorMessage = "Please enter edited text";
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
if (!this.repoUrl.trim()) {
|
|
151
|
-
this.errorMessage = "Repository URL not configured";
|
|
152
|
-
return;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
if (!this.searchApiUrl) {
|
|
156
|
-
this.errorMessage = "Search API URL not configured";
|
|
157
|
-
return;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
await this.performSearch();
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
async performSearch() {
|
|
164
|
-
this.isLoading = true;
|
|
165
|
-
this.errorMessage = "";
|
|
166
|
-
this.isSuccess = false;
|
|
167
|
-
|
|
168
|
-
try {
|
|
169
|
-
const response = await fetch(this.searchApiUrl, {
|
|
170
|
-
method: "POST",
|
|
171
|
-
headers: {
|
|
172
|
-
"Content-Type": "application/json"
|
|
173
|
-
},
|
|
174
|
-
body: JSON.stringify({
|
|
175
|
-
repoUrl: this.repoUrl,
|
|
176
|
-
selectedText: this.selectedText,
|
|
177
|
-
editedText: this.searchQuery
|
|
178
|
-
})
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
if (response.ok) {
|
|
182
|
-
this.isSuccess = true;
|
|
183
|
-
|
|
184
|
-
this.dispatchEvent(
|
|
185
|
-
new CustomEvent("textedited", {
|
|
186
|
-
detail: {
|
|
187
|
-
repoUrl: this.repoUrl,
|
|
188
|
-
selectedText: this.selectedText,
|
|
189
|
-
editedText: this.searchQuery,
|
|
190
|
-
success: true
|
|
191
|
-
}
|
|
192
|
-
})
|
|
193
|
-
);
|
|
194
|
-
|
|
195
|
-
setTimeout(() => {
|
|
196
|
-
this.hidePopover();
|
|
197
|
-
}, 2000);
|
|
198
|
-
} else {
|
|
199
|
-
throw new Error(`Edit failed: ${response.status}`);
|
|
200
|
-
}
|
|
201
|
-
} catch (error) {
|
|
202
|
-
console.error("Edit failed:", error);
|
|
203
|
-
this.errorMessage = "Edit failed. Please try again.";
|
|
204
|
-
} finally {
|
|
205
|
-
this.isLoading = false;
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
handleKeyDown(event) {
|
|
210
|
-
if (event.key === "Escape") {
|
|
211
|
-
this.hidePopover();
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
get hasError() {
|
|
216
|
-
return this.errorMessage.length > 0;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
get searchButtonDisabled() {
|
|
220
|
-
return this.isLoading || !this.searchQuery.trim();
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
renderedCallback() {
|
|
224
|
-
if (this.isVisible && this.initialPopoverStyle) {
|
|
225
|
-
const popover = this.template.querySelector('.text-selection-search-popover');
|
|
226
|
-
if (popover) {
|
|
227
|
-
popover.style.cssText = this.initialPopoverStyle;
|
|
228
|
-
}
|
|
229
|
-
} else if (!this.isVisible) {
|
|
230
|
-
const popover = this.template.querySelector('.text-selection-search-popover');
|
|
231
|
-
if (popover) {
|
|
232
|
-
popover.style.cssText = '';
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
}
|
|
@@ -1,446 +0,0 @@
|
|
|
1
|
-
import { LightningElement, api, track } from "lwc";
|
|
2
|
-
|
|
3
|
-
export default class TextSelectionSearch extends LightningElement {
|
|
4
|
-
@api searchApiUrl: string = "";
|
|
5
|
-
@api repoUrl: string = "";
|
|
6
|
-
@api placeholder: string = "Search for...";
|
|
7
|
-
@api popoverPosition: "top" | "bottom" = "top";
|
|
8
|
-
@api maxResults: number = 10;
|
|
9
|
-
|
|
10
|
-
@track isVisible: boolean = false;
|
|
11
|
-
@track isHidden: boolean = false;
|
|
12
|
-
@track searchQuery: string = "";
|
|
13
|
-
@track isLoading: boolean = false;
|
|
14
|
-
@track isSuccess: boolean = false;
|
|
15
|
-
@track errorMessage: string = "";
|
|
16
|
-
@track popoverStyle: string = "";
|
|
17
|
-
|
|
18
|
-
private selectedText: string = "";
|
|
19
|
-
private selectionStart: number = 0;
|
|
20
|
-
private selectionEnd: number = 0;
|
|
21
|
-
private initialPopoverStyle: string = ""; // Store initial position
|
|
22
|
-
private mutationObserver: MutationObserver | null = null;
|
|
23
|
-
private lastMouseX: number = 0;
|
|
24
|
-
private lastMouseY: number = 0;
|
|
25
|
-
|
|
26
|
-
connectedCallback() {
|
|
27
|
-
console.log('TextSelectionSearch: Component connected');
|
|
28
|
-
console.log('TextSelectionSearch: Document ready state:', document.readyState);
|
|
29
|
-
console.log('TextSelectionSearch: Current page URL:', window.location.href);
|
|
30
|
-
this.setupTextSelectionListener();
|
|
31
|
-
this.setupContentObserver();
|
|
32
|
-
|
|
33
|
-
// Additional initialization for spec-based references
|
|
34
|
-
if (this.isSpecBasedReferencePage()) {
|
|
35
|
-
console.log('TextSelectionSearch: Detected spec-based reference page, setting up additional listeners');
|
|
36
|
-
this.setupSpecBasedReferenceHandling();
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
disconnectedCallback() {
|
|
41
|
-
console.log('TextSelectionSearch: Component disconnected');
|
|
42
|
-
this.removeTextSelectionListener();
|
|
43
|
-
this.removeContentObserver();
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Setup text selection listener
|
|
47
|
-
setupTextSelectionListener() {
|
|
48
|
-
console.log('TextSelectionSearch: Setting up text selection listeners');
|
|
49
|
-
try {
|
|
50
|
-
// Use capture phase to ensure we get the events
|
|
51
|
-
document.addEventListener("mouseup", (event) => {
|
|
52
|
-
this.lastMouseX = event.clientX;
|
|
53
|
-
this.lastMouseY = event.clientY;
|
|
54
|
-
this.handleTextSelection(event);
|
|
55
|
-
}, true);
|
|
56
|
-
document.addEventListener("keyup", (event) => this.handleTextSelection(event), true);
|
|
57
|
-
console.log('TextSelectionSearch: Event listeners attached successfully');
|
|
58
|
-
} catch (error) {
|
|
59
|
-
console.error('TextSelectionSearch: Error setting up event listeners:', error);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
removeTextSelectionListener() {
|
|
64
|
-
console.log('TextSelectionSearch: Removing text selection listeners');
|
|
65
|
-
try {
|
|
66
|
-
document.removeEventListener("mouseup", (event) => this.handleTextSelection(event), true);
|
|
67
|
-
document.removeEventListener("keyup", (event) => this.handleTextSelection(event), true);
|
|
68
|
-
console.log('TextSelectionSearch: Event listeners removed successfully');
|
|
69
|
-
} catch (error) {
|
|
70
|
-
console.error('TextSelectionSearch: Error removing event listeners:', error);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Setup observer to detect when content is dynamically loaded
|
|
75
|
-
setupContentObserver() {
|
|
76
|
-
console.log('TextSelectionSearch: Setting up content observer');
|
|
77
|
-
try {
|
|
78
|
-
this.mutationObserver = new MutationObserver((mutations) => {
|
|
79
|
-
let shouldReinitialize = false;
|
|
80
|
-
|
|
81
|
-
for (const mutation of mutations) {
|
|
82
|
-
if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
|
|
83
|
-
// Check if any added nodes contain text content
|
|
84
|
-
for (const node of Array.from(mutation.addedNodes)) {
|
|
85
|
-
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
86
|
-
const element = node as Element;
|
|
87
|
-
|
|
88
|
-
// Check for spec-based reference content (.api-documentation)
|
|
89
|
-
if (element.classList && element.classList.contains('api-documentation')) {
|
|
90
|
-
console.log('TextSelectionSearch: Detected .api-documentation container');
|
|
91
|
-
shouldReinitialize = true;
|
|
92
|
-
break;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// Check for doc-amf-topic component
|
|
96
|
-
if (element.tagName && element.tagName.toLowerCase() === 'doc-amf-topic') {
|
|
97
|
-
console.log('TextSelectionSearch: Detected doc-amf-topic component');
|
|
98
|
-
shouldReinitialize = true;
|
|
99
|
-
break;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// Check for shadow DOM content in spec-based references
|
|
103
|
-
if (this.isSpecBasedReferencePage() && element.shadowRoot) {
|
|
104
|
-
console.log('TextSelectionSearch: Detected shadow DOM content');
|
|
105
|
-
shouldReinitialize = true;
|
|
106
|
-
break;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Check for common content containers
|
|
110
|
-
if (element.querySelector && (
|
|
111
|
-
element.querySelector('p, h1, h2, h3, h4, h5, h6, li, td, th, div') ||
|
|
112
|
-
element.matches('p, h1, h2, h3, h4, h5, h6, li, td, th, div')
|
|
113
|
-
)) {
|
|
114
|
-
shouldReinitialize = true;
|
|
115
|
-
break;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
if (shouldReinitialize) {
|
|
123
|
-
console.log('TextSelectionSearch: Content detected, reinitializing listeners');
|
|
124
|
-
// Small delay to ensure content is fully rendered
|
|
125
|
-
setTimeout(() => {
|
|
126
|
-
this.setupTextSelectionListener();
|
|
127
|
-
}, 200); // Increased delay for spec-based references
|
|
128
|
-
}
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
// Observe the entire document for content changes
|
|
132
|
-
this.mutationObserver.observe(document.body, {
|
|
133
|
-
childList: true,
|
|
134
|
-
subtree: true
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
console.log('TextSelectionSearch: Content observer set up successfully');
|
|
138
|
-
} catch (error) {
|
|
139
|
-
console.error('TextSelectionSearch: Error setting up content observer:', error);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
removeContentObserver() {
|
|
144
|
-
console.log('TextSelectionSearch: Removing content observer');
|
|
145
|
-
if (this.mutationObserver) {
|
|
146
|
-
this.mutationObserver.disconnect();
|
|
147
|
-
this.mutationObserver = null;
|
|
148
|
-
console.log('TextSelectionSearch: Content observer removed successfully');
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// Handle text selection
|
|
153
|
-
handleTextSelection(event?: Event) {
|
|
154
|
-
// If popover is visible, completely ignore all text selection events
|
|
155
|
-
if (this.isVisible) {
|
|
156
|
-
console.log('TextSelectionSearch: Popover is visible, ignoring text selection event');
|
|
157
|
-
return;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
console.log('TextSelectionSearch: Text selection event triggered');
|
|
161
|
-
console.log('TextSelectionSearch: Event type:', event?.type);
|
|
162
|
-
console.log('TextSelectionSearch: Event target:', event?.target);
|
|
163
|
-
|
|
164
|
-
// Check if the event target is inside our popover
|
|
165
|
-
if (event && event.target) {
|
|
166
|
-
const target = event.target as Element;
|
|
167
|
-
const popover = this.template.querySelector('.text-selection-search-popover');
|
|
168
|
-
if (popover && popover.contains(target)) {
|
|
169
|
-
console.log('TextSelectionSearch: Click inside popover, ignoring selection event');
|
|
170
|
-
return;
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
const selection = window.getSelection();
|
|
175
|
-
console.log('TextSelectionSearch: Selection object:', selection);
|
|
176
|
-
console.log('TextSelectionSearch: Selection string:', selection?.toString());
|
|
177
|
-
console.log('TextSelectionSearch: Selection range count:', selection?.rangeCount);
|
|
178
|
-
|
|
179
|
-
if (!selection || selection.toString().trim() === "") {
|
|
180
|
-
// Only hide popover if it's currently visible and we're not clicking inside it
|
|
181
|
-
if (this.isVisible) {
|
|
182
|
-
console.log('TextSelectionSearch: No text selected, but popover is visible - keeping it open');
|
|
183
|
-
return;
|
|
184
|
-
}
|
|
185
|
-
console.log('TextSelectionSearch: No text selected, hiding popover');
|
|
186
|
-
this.hidePopover();
|
|
187
|
-
return;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
this.selectedText = selection.toString().trim();
|
|
191
|
-
console.log('TextSelectionSearch: Selected text:', this.selectedText);
|
|
192
|
-
|
|
193
|
-
// For spec-based references, check if selection is within the API documentation container
|
|
194
|
-
if (this.isSpecBasedReferencePage()) {
|
|
195
|
-
const apiDocContainer = document.querySelector('.api-documentation');
|
|
196
|
-
if (apiDocContainer) {
|
|
197
|
-
const range = selection.getRangeAt(0);
|
|
198
|
-
const rect = range.getBoundingClientRect();
|
|
199
|
-
const containerRect = apiDocContainer.getBoundingClientRect();
|
|
200
|
-
|
|
201
|
-
// Check if the selection is within the API documentation container
|
|
202
|
-
const isWithinContainer = (
|
|
203
|
-
rect.top >= containerRect.top &&
|
|
204
|
-
rect.bottom <= containerRect.bottom &&
|
|
205
|
-
rect.left >= containerRect.left &&
|
|
206
|
-
rect.right <= containerRect.right
|
|
207
|
-
);
|
|
208
|
-
|
|
209
|
-
console.log('TextSelectionSearch: Selection within API container:', isWithinContainer);
|
|
210
|
-
|
|
211
|
-
if (!isWithinContainer) {
|
|
212
|
-
console.log('TextSelectionSearch: Selection outside API container, ignoring');
|
|
213
|
-
return;
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
if (this.selectedText.length > 0) {
|
|
219
|
-
this.showPopover();
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// Show popover at selection position
|
|
224
|
-
showPopover() {
|
|
225
|
-
console.log('TextSelectionSearch: Showing popover');
|
|
226
|
-
const selection = window.getSelection();
|
|
227
|
-
if (!selection || selection.rangeCount === 0) {
|
|
228
|
-
console.log('TextSelectionSearch: No selection range found');
|
|
229
|
-
return;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
// Use a simpler, more reliable positioning approach
|
|
233
|
-
const rect = selection.getRangeAt(0).getBoundingClientRect();
|
|
234
|
-
console.log('TextSelectionSearch: Selection rect:', rect);
|
|
235
|
-
|
|
236
|
-
// Check if we're on a spec-based reference page
|
|
237
|
-
const isSpecBased = this.isSpecBasedReferencePage();
|
|
238
|
-
console.log('TextSelectionSearch: Is spec-based reference:', isSpecBased);
|
|
239
|
-
|
|
240
|
-
// Use mouse position as fallback for better positioning
|
|
241
|
-
const mouseX = this.lastMouseX || rect.left + rect.width / 2;
|
|
242
|
-
const mouseY = this.lastMouseY || rect.top;
|
|
243
|
-
|
|
244
|
-
console.log('TextSelectionSearch: Using mouse position:', mouseX, mouseY);
|
|
245
|
-
|
|
246
|
-
// Calculate popover position
|
|
247
|
-
const popoverLeft = Math.max(10, Math.min(window.innerWidth - 360, mouseX - 175)); // Center on mouse
|
|
248
|
-
let popoverTop: number;
|
|
249
|
-
|
|
250
|
-
if (this.popoverPosition === "top") {
|
|
251
|
-
popoverTop = Math.max(10, mouseY - 220); // Above mouse cursor
|
|
252
|
-
} else {
|
|
253
|
-
popoverTop = Math.min(window.innerHeight - 250, mouseY + 20); // Below mouse cursor
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
// Ensure the popover doesn't go off-screen
|
|
257
|
-
if (popoverTop < 10) {
|
|
258
|
-
popoverTop = 10;
|
|
259
|
-
}
|
|
260
|
-
if (popoverTop > window.innerHeight - 250) {
|
|
261
|
-
popoverTop = window.innerHeight - 250;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
this.setPopoverPosition(popoverTop, popoverLeft);
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
// Set the popover position and show it
|
|
268
|
-
setPopoverPosition(top: number, left: number) {
|
|
269
|
-
this.popoverStyle = `position: fixed; top: ${top}px; left: ${left}px; z-index: 9999; width: 350px;`;
|
|
270
|
-
this.initialPopoverStyle = this.popoverStyle;
|
|
271
|
-
console.log('TextSelectionSearch: Final popover style:', this.popoverStyle);
|
|
272
|
-
console.log('TextSelectionSearch: Final position - top:', top, 'left:', left);
|
|
273
|
-
|
|
274
|
-
this.isVisible = true;
|
|
275
|
-
this.isHidden = false;
|
|
276
|
-
this.searchQuery = this.selectedText;
|
|
277
|
-
this.errorMessage = "";
|
|
278
|
-
this.isSuccess = false;
|
|
279
|
-
|
|
280
|
-
console.log('TextSelectionSearch: Popover should now be visible');
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
// Hide popover
|
|
284
|
-
hidePopover() {
|
|
285
|
-
console.log('TextSelectionSearch: Hiding popover');
|
|
286
|
-
this.isHidden = true;
|
|
287
|
-
|
|
288
|
-
// Use setTimeout to ensure the hidden class is applied before hiding
|
|
289
|
-
setTimeout(() => {
|
|
290
|
-
this.isVisible = false;
|
|
291
|
-
this.searchQuery = "";
|
|
292
|
-
this.errorMessage = "";
|
|
293
|
-
this.isSuccess = false;
|
|
294
|
-
this.popoverStyle = ""; // Reset the positioning style
|
|
295
|
-
this.initialPopoverStyle = ""; // Reset the initial position
|
|
296
|
-
|
|
297
|
-
// Clear any text selection to prevent immediate re-showing
|
|
298
|
-
const selection = window.getSelection();
|
|
299
|
-
if (selection) {
|
|
300
|
-
selection.removeAllRanges();
|
|
301
|
-
}
|
|
302
|
-
}, 100);
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
// Handle popover click to prevent closing
|
|
306
|
-
handlePopoverClick(event: Event) {
|
|
307
|
-
event.stopPropagation();
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
// Handle input click to prevent closing
|
|
311
|
-
handleInputClick(event: Event) {
|
|
312
|
-
event.stopPropagation();
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
// Handle search input change
|
|
316
|
-
handleSearchInputChange(event: Event) {
|
|
317
|
-
const target = event.target as HTMLInputElement;
|
|
318
|
-
this.searchQuery = target.value;
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
// Handle search submission
|
|
322
|
-
async handleSearchSubmit(event: Event) {
|
|
323
|
-
event.preventDefault();
|
|
324
|
-
|
|
325
|
-
if (!this.searchQuery.trim()) {
|
|
326
|
-
this.errorMessage = "Please enter edited text";
|
|
327
|
-
return;
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
if (!this.repoUrl.trim()) {
|
|
331
|
-
this.errorMessage = "Repository URL not configured";
|
|
332
|
-
return;
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
if (!this.searchApiUrl) {
|
|
336
|
-
this.errorMessage = "Search API URL not configured";
|
|
337
|
-
return;
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
await this.performSearch();
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
// Perform search API call
|
|
344
|
-
async performSearch() {
|
|
345
|
-
this.isLoading = true;
|
|
346
|
-
this.errorMessage = "";
|
|
347
|
-
this.isSuccess = false;
|
|
348
|
-
|
|
349
|
-
try {
|
|
350
|
-
const response = await fetch(this.searchApiUrl, {
|
|
351
|
-
method: "POST",
|
|
352
|
-
headers: {
|
|
353
|
-
"Content-Type": "application/json"
|
|
354
|
-
},
|
|
355
|
-
body: JSON.stringify({
|
|
356
|
-
repoUrl: this.repoUrl,
|
|
357
|
-
selectedText: this.selectedText,
|
|
358
|
-
editedText: this.searchQuery
|
|
359
|
-
})
|
|
360
|
-
});
|
|
361
|
-
|
|
362
|
-
if (response.ok) {
|
|
363
|
-
this.isSuccess = true;
|
|
364
|
-
|
|
365
|
-
// Dispatch success event
|
|
366
|
-
this.dispatchEvent(
|
|
367
|
-
new CustomEvent("textedited", {
|
|
368
|
-
detail: {
|
|
369
|
-
repoUrl: this.repoUrl,
|
|
370
|
-
selectedText: this.selectedText,
|
|
371
|
-
editedText: this.searchQuery,
|
|
372
|
-
success: true
|
|
373
|
-
}
|
|
374
|
-
})
|
|
375
|
-
);
|
|
376
|
-
|
|
377
|
-
// Auto-hide popover after success
|
|
378
|
-
setTimeout(() => {
|
|
379
|
-
this.hidePopover();
|
|
380
|
-
}, 2000);
|
|
381
|
-
} else {
|
|
382
|
-
throw new Error(`Edit failed: ${response.status}`);
|
|
383
|
-
}
|
|
384
|
-
} catch (error) {
|
|
385
|
-
console.error("Edit failed:", error);
|
|
386
|
-
this.errorMessage = "Edit failed. Please try again.";
|
|
387
|
-
} finally {
|
|
388
|
-
this.isLoading = false;
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
// Handle escape key
|
|
393
|
-
handleKeyDown(event: KeyboardEvent) {
|
|
394
|
-
if (event.key === "Escape") {
|
|
395
|
-
this.hidePopover();
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
// Getters for UI state
|
|
400
|
-
get hasError() {
|
|
401
|
-
return this.errorMessage.length > 0;
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
get searchButtonDisabled() {
|
|
405
|
-
return this.isLoading || !this.searchQuery.trim();
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
// Check if current page is a spec-based reference
|
|
409
|
-
isSpecBasedReferencePage(): boolean {
|
|
410
|
-
const url = window.location.href;
|
|
411
|
-
const hasMetaParam = url.includes('?meta=');
|
|
412
|
-
const isReferencePage = url.includes('/references/');
|
|
413
|
-
console.log('TextSelectionSearch: URL analysis - hasMetaParam:', hasMetaParam, 'isReferencePage:', isReferencePage);
|
|
414
|
-
return hasMetaParam && isReferencePage;
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
// Additional setup for spec-based references
|
|
418
|
-
setupSpecBasedReferenceHandling() {
|
|
419
|
-
// Wait for the page to be fully loaded
|
|
420
|
-
if (document.readyState === 'loading') {
|
|
421
|
-
document.addEventListener('DOMContentLoaded', () => {
|
|
422
|
-
this.initializeForSpecBasedReference();
|
|
423
|
-
});
|
|
424
|
-
} else {
|
|
425
|
-
this.initializeForSpecBasedReference();
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
initializeForSpecBasedReference() {
|
|
430
|
-
console.log('TextSelectionSearch: Initializing for spec-based reference');
|
|
431
|
-
|
|
432
|
-
// Wait for the .api-documentation container to appear
|
|
433
|
-
const checkForApiDocumentation = () => {
|
|
434
|
-
const apiDocContainer = document.querySelector('.api-documentation');
|
|
435
|
-
if (apiDocContainer) {
|
|
436
|
-
console.log('TextSelectionSearch: Found .api-documentation container, setting up listeners');
|
|
437
|
-
this.setupTextSelectionListener();
|
|
438
|
-
} else {
|
|
439
|
-
console.log('TextSelectionSearch: .api-documentation container not found yet, retrying...');
|
|
440
|
-
setTimeout(checkForApiDocumentation, 500);
|
|
441
|
-
}
|
|
442
|
-
};
|
|
443
|
-
|
|
444
|
-
checkForApiDocumentation();
|
|
445
|
-
}
|
|
446
|
-
}
|