Skip to content

Commit 792a602

Browse files
committed
fix: simplify external dependencies in vite.config.ts
1 parent 493c654 commit 792a602

7 files changed

Lines changed: 468 additions & 18 deletions

File tree

apps/qwksearch-web/components/ResearchAgent/components/MessageComposer/ChatInputBox.tsx

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ const ChatInputBox = () => {
103103
const [suggestions, setSuggestions] = useState<string[]>([]);
104104
const [highlightedIndex, setHighlightedIndex] = useState(-1);
105105
const [suggestionsOpen, setSuggestionsOpen] = useState(false);
106+
const [dropdownPosition, setDropdownPosition] = useState<'below' | 'above'>('below');
106107
const suppressNextFetchRef = useRef(false);
107108
const autocompleteAbortRef = useRef<AbortController | null>(null);
108109

@@ -125,14 +126,32 @@ const ChatInputBox = () => {
125126
autocompleteAbortRef.current = controller;
126127
try {
127128
const res = await fetch(
128-
`/api/agent/autocomplete?q=${encodeURIComponent(query)}`,
129+
`/api/agent/autocomplete?q=${encodeURIComponent(query)}&limit=8`,
129130
{ signal: controller.signal },
130131
);
131132
if (!res.ok) return;
132133
const data = await res.json();
133134
const list: string[] = Array.isArray(data?.suggestions) ? data.suggestions : [];
134135
setSuggestions(list);
135-
setSuggestionsOpen(list.length > 0);
136+
if (list.length > 0) {
137+
// Calculate dropdown position based on viewport space
138+
if (wrapperRef.current) {
139+
const rect = wrapperRef.current.getBoundingClientRect();
140+
const spaceBelow = window.innerHeight - rect.bottom;
141+
const spaceAbove = rect.top;
142+
const dropdownHeight = Math.min(288, list.length * 48); // max-h-72 = 288px, ~48px per item
143+
144+
// On mobile (or when not enough space below), show above if there's more space
145+
if (spaceBelow < dropdownHeight && spaceAbove > spaceBelow) {
146+
setDropdownPosition('above');
147+
} else {
148+
setDropdownPosition('below');
149+
}
150+
}
151+
setSuggestionsOpen(true);
152+
} else {
153+
setSuggestionsOpen(false);
154+
}
136155
setHighlightedIndex(-1);
137156
} catch (err: any) {
138157
if (err?.name !== "AbortError") console.error("Autocomplete fetch failed", err);
@@ -391,11 +410,15 @@ const ChatInputBox = () => {
391410
// no-op; rendered outside wrapper, handled by mousedown stopPropagation below
392411
}
393412
}}
394-
initial={{ opacity: 0, y: -4 }}
413+
initial={{ opacity: 0, y: dropdownPosition === 'above' ? 4 : -4 }}
395414
animate={{ opacity: 1, y: 0 }}
396-
exit={{ opacity: 0, y: -4 }}
415+
exit={{ opacity: 0, y: dropdownPosition === 'above' ? 4 : -4 }}
397416
transition={{ duration: 0.12 }}
398-
className="absolute left-2 right-2 md:left-0 md:right-0 top-full mt-2 z-20 rounded-2xl bg-gray-50 dark:bg-[#30302E] border border-bg-300 dark:border-transparent shadow-xl overflow-hidden"
417+
className={`absolute left-2 right-2 md:left-0 md:right-0 z-20 rounded-2xl bg-gray-50 dark:bg-[#30302E] border border-bg-300 dark:border-transparent shadow-xl overflow-hidden ${
418+
dropdownPosition === 'above'
419+
? 'bottom-full mb-2'
420+
: 'top-full mt-2'
421+
}`}
399422
onMouseDown={(e) => e.preventDefault()}
400423
>
401424
<ul className="max-h-72 overflow-y-auto custom-scrollbar py-1">
@@ -405,10 +428,10 @@ const ChatInputBox = () => {
405428
type="button"
406429
onMouseEnter={() => setHighlightedIndex(i)}
407430
onClick={() => selectSuggestion(s)}
408-
className={`w-full text-left px-4 py-2 text-[15px] flex items-center gap-2 transition-colors ${
431+
className={`w-full text-left px-4 py-2.5 text-[15px] flex items-center gap-3 transition-colors ${
409432
i === highlightedIndex
410433
? 'bg-bg-200 text-text-100'
411-
: 'text-text-200 hover:bg-bg-200'
434+
: 'text-text-200 hover:bg-bg-100 dark:hover:bg-[#3A3A38]'
412435
}`}
413436
>
414437
<Search className="w-4 h-4 text-text-400 shrink-0" />

apps/qwksearch-web/vite.config.ts

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,24 +24,20 @@ export default defineConfig(({ command }) => ({
2424
resolve: {
2525
alias: {
2626
"shadcn-app-dock": resolve(__dirname, "../../packages/shadcn-app-dock/src/index.ts"),
27+
"extract-webpage": resolve(__dirname, "../../packages/extract-webpage/src"),
28+
"chat-agent-toolkit": resolve(__dirname, "../../packages/agent-toolkit/src"),
29+
"extract-pdf": resolve(__dirname, "../../packages/extract-pdf/src"),
30+
"extract-youtube": resolve(__dirname, "../../packages/extract-youtube/src"),
31+
"qwksearch-api-client": resolve(__dirname, "../../packages/qwksearch-api-client/src"),
32+
"grab-url": resolve(__dirname, "../../node_modules/grab-url"),
2733
},
2834
},
2935
build: {
3036
rollupOptions: {
3137
// `fsevents` is an optional macOS-only native module that rollup/chokidar
3238
// require() lazily inside a try/catch; it has no place in the Worker
3339
// bundle and is never installed on Linux, so leave it external.
34-
// Workspace packages with subpath imports must also be external since
35-
// Rolldown cannot resolve them during bundling.
36-
external: [
37-
"fsevents",
38-
/^extract-webpage/,
39-
/^chat-agent-toolkit/,
40-
/^extract-pdf/,
41-
/^extract-youtube/,
42-
/^qwksearch-api-client/,
43-
/^grab-url/,
44-
],
40+
external: ["fsevents"],
4541
},
4642
},
4743
plugins: [
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/**
2+
* @fileoverview Re-export ModelRegistry from config directory
3+
*/
4+
export { default } from "../config/model-registry";
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/**
2+
* @fileoverview Re-export model types from config directory
3+
*/
4+
export type { Model, ConfigModelProvider } from "../config/config-types";
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { pipeline, type TextGenerationSingle } from "@huggingface/transformers";
2+
3+
/**
4+
* Generates the next words for a given prompt using a text generation model.
5+
*
6+
* The DistilGPT2 model is a distilled, smaller version of OpenAI's GPT-2, featuring
7+
* around 82 million parameters (~80MB), and available in Hugging Face Transformers
8+
* for efficient next word (next-token) prediction tasks in English text. It is
9+
* well-suited for sequence generation and can be used as a next word prediction
10+
* model with code similar to GPT2, but offers faster and lighter inference compared
11+
* GPT-2 (124M parameter) model, preserving most capabilities but running
12+
* significantly faster and requiring less memory.
13+
* @see https://huggingface.co/Xenova/distilgpt2
14+
* @param {string} prompt - The input prompt to complete.
15+
* @param {Object} [options] - Generation options.
16+
* @param {number} [options.maxTokens=16] - Maximum number of new tokens to generate.
17+
* @param {string} [options.model='Xenova/distilgpt2'] - The model identifier to use.
18+
* @returns {Promise<string>} - The generated text completions.
19+
*/
20+
export async function predictNextWordsWithSmallLocalModel(
21+
prompt: string,
22+
{ model = "Xenova/distilgpt2", maxTokens = 16, modelParams = {} } = {}
23+
): Promise<string> {
24+
if (!prompt) throw new Error("Prompt is required");
25+
return (
26+
await (
27+
await pipeline("text-generation", model, {
28+
dtype: "fp16",
29+
device: "cpu",
30+
...modelParams,
31+
})
32+
)(prompt, { max_new_tokens: maxTokens })
33+
)
34+
.map((res) => (res as TextGenerationSingle).generated_text)
35+
.join(" ")
36+
.replace(prompt, "")
37+
.trim();
38+
}

0 commit comments

Comments
 (0)