astro-tractstack 2.0.0-rc.46 → 2.0.0-rc.47
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/package.json
CHANGED
|
@@ -189,7 +189,7 @@ export default function SearchModal({
|
|
|
189
189
|
{selectedTerms.map((term, index) => (
|
|
190
190
|
<div
|
|
191
191
|
key={index}
|
|
192
|
-
className="flex items-center gap-1 rounded-full border border-blue-200 bg-blue-100 px-3 py-1 text-sm font-
|
|
192
|
+
className="flex items-center gap-1 rounded-full border border-blue-200 bg-blue-100 px-3 py-1 text-sm font-bold text-blue-800"
|
|
193
193
|
>
|
|
194
194
|
<span>{term}</span>
|
|
195
195
|
<button
|
|
@@ -208,7 +208,10 @@ export default function SearchModal({
|
|
|
208
208
|
<div className="relative w-full px-6 py-2">
|
|
209
209
|
{showCompletion && (
|
|
210
210
|
<div className="pointer-events-none absolute left-0 top-0 flex h-full w-full items-center px-6 py-2 text-xl text-gray-400">
|
|
211
|
-
{
|
|
211
|
+
<span style={{ visibility: 'hidden' }}>{query}</span>
|
|
212
|
+
{bestCompletion
|
|
213
|
+
.slice(query.length)
|
|
214
|
+
.replace(/ /g, '\u00A0')}
|
|
212
215
|
</div>
|
|
213
216
|
)}
|
|
214
217
|
<input
|
|
@@ -282,7 +285,7 @@ export default function SearchModal({
|
|
|
282
285
|
{/* Suggestion Pills */}
|
|
283
286
|
{showSuggestions && (
|
|
284
287
|
<div className="w-full p-6">
|
|
285
|
-
<p className="text-mydarkgrey mb-4 text-sm font-
|
|
288
|
+
<p className="text-mydarkgrey mb-4 text-sm font-bold">
|
|
286
289
|
Suggestions ({suggestions.length})
|
|
287
290
|
</p>
|
|
288
291
|
<div className="flex flex-wrap gap-2">
|
|
@@ -290,7 +293,7 @@ export default function SearchModal({
|
|
|
290
293
|
<button
|
|
291
294
|
key={index}
|
|
292
295
|
onClick={() => handleSuggestionClick(suggestion)}
|
|
293
|
-
className={`inline-flex items-center rounded-full border px-3 py-1.5 text-sm font-
|
|
296
|
+
className={`inline-flex items-center rounded-full border px-3 py-1.5 text-sm font-bold transition-all hover:shadow-md ${getTypeColor(suggestion.type)}`}
|
|
294
297
|
>
|
|
295
298
|
<span>{suggestion.term}</span>
|
|
296
299
|
</button>
|
|
@@ -23,7 +23,7 @@ interface UseSearchReturn {
|
|
|
23
23
|
clearAll: () => void;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
const DEBOUNCE_MS =
|
|
26
|
+
const DEBOUNCE_MS = 150;
|
|
27
27
|
const BACKEND_THROTTLE_MS = 1200;
|
|
28
28
|
|
|
29
29
|
export function useSearch(): UseSearchReturn {
|
|
@@ -39,11 +39,10 @@ export function useSearch(): UseSearchReturn {
|
|
|
39
39
|
const [isRetrieving, setIsRetrieving] = useState(false);
|
|
40
40
|
const [retrieveError, setRetrieveError] = useState<string | null>(null);
|
|
41
41
|
|
|
42
|
-
//
|
|
43
|
-
const
|
|
44
|
-
const
|
|
42
|
+
// --- REVISED STATE FOR SEARCH LOGIC ---
|
|
43
|
+
const searchTimerRef = useRef<NodeJS.Timeout>();
|
|
44
|
+
const lastExecutionTimeRef = useRef<number>(0);
|
|
45
45
|
const pendingQueryRef = useRef<string | null>(null);
|
|
46
|
-
const throttleTimeoutRef = useRef<NodeJS.Timeout>();
|
|
47
46
|
const inflightQueryRef = useRef<string | null>(null);
|
|
48
47
|
const api = useMemo(() => new TractStackAPI(), []);
|
|
49
48
|
|
|
@@ -54,21 +53,17 @@ export function useSearch(): UseSearchReturn {
|
|
|
54
53
|
return;
|
|
55
54
|
}
|
|
56
55
|
|
|
57
|
-
// Check if this exact query is already inflight
|
|
58
56
|
if (inflightQueryRef.current === query.trim()) {
|
|
59
57
|
return;
|
|
60
58
|
}
|
|
61
59
|
|
|
62
|
-
// Mark this query as inflight
|
|
63
60
|
inflightQueryRef.current = query.trim();
|
|
64
|
-
|
|
65
61
|
setIsDiscovering(true);
|
|
66
62
|
setDiscoverError(null);
|
|
67
63
|
|
|
68
64
|
try {
|
|
69
65
|
const response = await api.discover(query.trim());
|
|
70
66
|
|
|
71
|
-
// Only process response if this is still the current inflight query
|
|
72
67
|
if (inflightQueryRef.current === query.trim()) {
|
|
73
68
|
if (response.success && response.data) {
|
|
74
69
|
setSuggestions(response.data.suggestions);
|
|
@@ -78,7 +73,6 @@ export function useSearch(): UseSearchReturn {
|
|
|
78
73
|
}
|
|
79
74
|
}
|
|
80
75
|
} catch (err) {
|
|
81
|
-
// Only process error if this is still the current inflight query
|
|
82
76
|
if (inflightQueryRef.current === query.trim()) {
|
|
83
77
|
setDiscoverError(
|
|
84
78
|
err instanceof Error ? err.message : 'Discovery failed'
|
|
@@ -86,7 +80,6 @@ export function useSearch(): UseSearchReturn {
|
|
|
86
80
|
setSuggestions([]);
|
|
87
81
|
}
|
|
88
82
|
} finally {
|
|
89
|
-
// Clear inflight tracking only if this is still the current query
|
|
90
83
|
if (inflightQueryRef.current === query.trim()) {
|
|
91
84
|
inflightQueryRef.current = null;
|
|
92
85
|
setIsDiscovering(false);
|
|
@@ -122,28 +115,11 @@ export function useSearch(): UseSearchReturn {
|
|
|
122
115
|
[api]
|
|
123
116
|
);
|
|
124
117
|
|
|
125
|
-
const executePendingSearch = useCallback(() => {
|
|
126
|
-
if (pendingQueryRef.current) {
|
|
127
|
-
const queryToExecute = pendingQueryRef.current;
|
|
128
|
-
pendingQueryRef.current = null;
|
|
129
|
-
|
|
130
|
-
// Update timestamp immediately when scheduling request, not when executing
|
|
131
|
-
lastSearchTimeRef.current = Date.now();
|
|
132
|
-
|
|
133
|
-
performDiscovery(queryToExecute);
|
|
134
|
-
}
|
|
135
|
-
}, [performDiscovery]);
|
|
136
|
-
|
|
137
118
|
const discoverTerms = useCallback(
|
|
138
119
|
(query: string) => {
|
|
139
|
-
// Clear existing
|
|
140
|
-
if (
|
|
141
|
-
clearTimeout(
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// Clear existing throttle timer
|
|
145
|
-
if (throttleTimeoutRef.current) {
|
|
146
|
-
clearTimeout(throttleTimeoutRef.current);
|
|
120
|
+
// Clear any existing timer.
|
|
121
|
+
if (searchTimerRef.current) {
|
|
122
|
+
clearTimeout(searchTimerRef.current);
|
|
147
123
|
}
|
|
148
124
|
|
|
149
125
|
// Clear results when starting new discovery
|
|
@@ -160,28 +136,33 @@ export function useSearch(): UseSearchReturn {
|
|
|
160
136
|
return;
|
|
161
137
|
}
|
|
162
138
|
|
|
163
|
-
// Always store the latest query
|
|
139
|
+
// Always store the latest query for the next execution
|
|
164
140
|
pendingQueryRef.current = query;
|
|
165
141
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
const now = Date.now();
|
|
169
|
-
const timeSinceLastSearch = now - lastSearchTimeRef.current;
|
|
142
|
+
const now = Date.now();
|
|
143
|
+
const timeSinceLastSearch = now - lastExecutionTimeRef.current;
|
|
170
144
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
145
|
+
// Start with the basic debounce delay
|
|
146
|
+
let delay = DEBOUNCE_MS;
|
|
147
|
+
|
|
148
|
+
// If we are inside the throttle window, we must wait longer
|
|
149
|
+
if (timeSinceLastSearch < BACKEND_THROTTLE_MS) {
|
|
150
|
+
const remainingThrottle = BACKEND_THROTTLE_MS - timeSinceLastSearch;
|
|
151
|
+
delay = Math.max(delay, remainingThrottle);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
searchTimerRef.current = setTimeout(() => {
|
|
155
|
+
// Double check there's a query to run
|
|
156
|
+
if (pendingQueryRef.current !== null) {
|
|
157
|
+
const queryToExecute = pendingQueryRef.current;
|
|
158
|
+
|
|
159
|
+
// Update execution time as soon as the search is initiated
|
|
160
|
+
lastExecutionTimeRef.current = Date.now();
|
|
161
|
+
performDiscovery(queryToExecute);
|
|
181
162
|
}
|
|
182
|
-
},
|
|
163
|
+
}, delay);
|
|
183
164
|
},
|
|
184
|
-
[
|
|
165
|
+
[performDiscovery]
|
|
185
166
|
);
|
|
186
167
|
|
|
187
168
|
const selectSuggestion = useCallback(
|
|
@@ -215,15 +196,12 @@ export function useSearch(): UseSearchReturn {
|
|
|
215
196
|
);
|
|
216
197
|
|
|
217
198
|
const clearAll = useCallback(() => {
|
|
218
|
-
// Clear
|
|
219
|
-
if (
|
|
220
|
-
clearTimeout(
|
|
221
|
-
}
|
|
222
|
-
if (throttleTimeoutRef.current) {
|
|
223
|
-
clearTimeout(throttleTimeoutRef.current);
|
|
199
|
+
// Clear the main search timer
|
|
200
|
+
if (searchTimerRef.current) {
|
|
201
|
+
clearTimeout(searchTimerRef.current);
|
|
224
202
|
}
|
|
225
203
|
|
|
226
|
-
// Reset all state
|
|
204
|
+
// Reset all state
|
|
227
205
|
setSuggestions([]);
|
|
228
206
|
setIsDiscovering(false);
|
|
229
207
|
setDiscoverError(null);
|
|
@@ -232,6 +210,7 @@ export function useSearch(): UseSearchReturn {
|
|
|
232
210
|
setRetrieveError(null);
|
|
233
211
|
pendingQueryRef.current = null;
|
|
234
212
|
inflightQueryRef.current = null;
|
|
213
|
+
lastExecutionTimeRef.current = 0; // Reset throttle timer
|
|
235
214
|
}, []);
|
|
236
215
|
|
|
237
216
|
return {
|