mirror of
https://github.com/lobehub/lobehub.git
synced 2026-03-27 13:29:15 +07:00
fix: CMDK freeze (#11440)
* fix: CMDK freeze * fix: Cannot ssearch folder * fix: Cannot ssearch folder * fix: Update translation
This commit is contained in:
@@ -134,6 +134,8 @@
|
||||
"cmdk.search.communityAgent": "وكيل المجتمع",
|
||||
"cmdk.search.file": "ملف",
|
||||
"cmdk.search.files": "ملفات",
|
||||
"cmdk.search.folder": "مجلد",
|
||||
"cmdk.search.folders": "مجلدات",
|
||||
"cmdk.search.loading": "يتم البحث...",
|
||||
"cmdk.search.market": "المجتمع",
|
||||
"cmdk.search.mcp": "خادم MCP",
|
||||
@@ -389,4 +391,4 @@
|
||||
"userPanel.setting": "الإعدادات",
|
||||
"userPanel.usages": "إحصائيات الاستخدام",
|
||||
"version": "الإصدار"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,9 +12,9 @@
|
||||
"interests.area.writing": "إنشاء المحتوى",
|
||||
"interests.hint": "يمكنك تغيير هذا في أي وقت من الإعدادات",
|
||||
"interests.placeholder": "أدخل اهتماماتك...",
|
||||
"interests.title": "هل يمكنك إخباري بالمجالات التي تهمك؟",
|
||||
"interests.title2": "سيساعدني هذا في تكوين انطباع أولي عنك~",
|
||||
"interests.title3": "خذ وقتك، وسأتعرف عليك أكثر فأكثر",
|
||||
"interests.title": "ما هي المجالات التي تهمك؟",
|
||||
"interests.title2": "سيساعدني هذا على التعرف عليك بشكل أفضل",
|
||||
"interests.title3": "خذ وقتك، سأتعرف عليك أكثر",
|
||||
"modeSelection.desc": "اختر الوضع الذي يناسبك",
|
||||
"modeSelection.hint": "يمكنك تغييره في أي وقت من الإعدادات",
|
||||
"modeSelection.lite.desc": "مثالي للمحادثات اليومية، الأسئلة والأجوبة، الإنتاجية الخفيفة، وتجربة النماذج",
|
||||
@@ -63,4 +63,4 @@
|
||||
"username.title": "بالمناسبة، ماذا يجب أن أُسميك؟",
|
||||
"username.title2": "دعنا نتعرف على بعضنا أولاً!",
|
||||
"username.title3": "هكذا يمكننا التحدث بشكل طبيعي أكثر من الآن فصاعدًا~"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,9 +12,9 @@
|
||||
"interests.area.writing": "Създаване на съдържание",
|
||||
"interests.hint": "Можете да го промените по всяко време в настройките",
|
||||
"interests.placeholder": "Въведете вашите интереси...",
|
||||
"interests.title": "Може ли да ми кажете в кои области се интересувате?",
|
||||
"interests.title2": "Това ще ми помогне да добия първо впечатление за вас~",
|
||||
"interests.title3": "Отделете си време, ще ви опознавам все по-добре",
|
||||
"interests.title": "В какви области се интересуваш?",
|
||||
"interests.title2": "Това ще ми помогне да те опозная по-добре",
|
||||
"interests.title3": "Не бързай, ще те опозная постепенно",
|
||||
"modeSelection.desc": "Изберете режима, който най-добре ви подхожда",
|
||||
"modeSelection.hint": "Можете да го промените по всяко време в настройките",
|
||||
"modeSelection.lite.desc": "Идеален за ежедневни разговори, въпроси и отговори, лека продуктивност и тестване на модели",
|
||||
@@ -63,4 +63,4 @@
|
||||
"username.title": "Между другото, как да ви наричам?",
|
||||
"username.title2": "Нека първо се опознаем!",
|
||||
"username.title3": "Така ще можем да си говорим по-естествено оттук нататък~"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,9 +12,6 @@
|
||||
"interests.area.writing": "Texterstellung",
|
||||
"interests.hint": "Du kannst das jederzeit in den Einstellungen ändern",
|
||||
"interests.placeholder": "Gib deine Interessen ein...",
|
||||
"interests.title": "Wofür interessierst du dich?",
|
||||
"interests.title2": "So bekomme ich einen ersten Eindruck von dir~",
|
||||
"interests.title3": "Nimm dir Zeit – ich lerne dich immer besser kennen",
|
||||
"modeSelection.desc": "Wähle den Modus, der am besten zu dir passt",
|
||||
"modeSelection.hint": "Du kannst das jederzeit in den Einstellungen ändern",
|
||||
"modeSelection.lite.desc": "Ideal für alltägliche Gespräche, Fragen & Antworten, leichte Produktivität und zum Ausprobieren von Modellen",
|
||||
|
||||
@@ -134,6 +134,8 @@
|
||||
"cmdk.search.communityAgent": "Community Agent",
|
||||
"cmdk.search.file": "File",
|
||||
"cmdk.search.files": "Files",
|
||||
"cmdk.search.folder": "Folder",
|
||||
"cmdk.search.folders": "Folders",
|
||||
"cmdk.search.loading": "Searching...",
|
||||
"cmdk.search.market": "Community",
|
||||
"cmdk.search.mcp": "MCP Server",
|
||||
|
||||
@@ -12,9 +12,9 @@
|
||||
"interests.area.writing": "Content Creation",
|
||||
"interests.hint": "You can change this anytime in settings",
|
||||
"interests.placeholder": "Enter your interests...",
|
||||
"interests.title": "Could you tell me what areas you're interested in?",
|
||||
"interests.title2": "This will help me get a first impression of you~",
|
||||
"interests.title3": "Take your time, I'll get to know you better and better",
|
||||
"interests.title": "What areas you're interested in?",
|
||||
"interests.title2": "This will help me know you better",
|
||||
"interests.title3": "Take your time, I'll get to know you better",
|
||||
"modeSelection.desc": "Choose the mode that suits you best",
|
||||
"modeSelection.hint": "You can change this anytime in settings",
|
||||
"modeSelection.lite.desc": "Ideal for everyday conversations, Q&A, light productivity, and trying out models",
|
||||
|
||||
@@ -12,9 +12,6 @@
|
||||
"interests.area.writing": "Creación de Contenido",
|
||||
"interests.hint": "Puedes cambiar esto en cualquier momento desde la configuración",
|
||||
"interests.placeholder": "Escribe tus intereses...",
|
||||
"interests.title": "¿Podrías decirme en qué áreas estás interesado?",
|
||||
"interests.title2": "Esto me ayudará a tener una primera impresión de ti~",
|
||||
"interests.title3": "Tómate tu tiempo, te iré conociendo cada vez mejor",
|
||||
"modeSelection.desc": "Elige el modo que mejor se adapte a ti",
|
||||
"modeSelection.hint": "Puedes cambiar esto en cualquier momento desde la configuración",
|
||||
"modeSelection.lite.desc": "Ideal para conversaciones cotidianas, preguntas y respuestas, productividad ligera y probar modelos",
|
||||
|
||||
@@ -12,9 +12,6 @@
|
||||
"interests.area.writing": "تولید محتوا",
|
||||
"interests.hint": "میتوانید هر زمان از تنظیمات این را تغییر دهید",
|
||||
"interests.placeholder": "علایق خود را وارد کنید...",
|
||||
"interests.title": "میتونی بهم بگی به چه حوزههایی علاقهمندی؟",
|
||||
"interests.title2": "این کمکم میکنه یه دید اولیه ازت داشته باشم~",
|
||||
"interests.title3": "عجله نکن، کمکم بیشتر باهات آشنا میشم",
|
||||
"modeSelection.desc": "حالت کاریای که برات مناسبتره رو انتخاب کن",
|
||||
"modeSelection.hint": "میتونی هر زمان از تنظیمات تغییرش بدی",
|
||||
"modeSelection.lite.desc": "مناسب برای گفتگوهای روزمره، پرسش و پاسخ، بهرهوری سبک و تست مدلها",
|
||||
|
||||
@@ -12,9 +12,6 @@
|
||||
"interests.area.writing": "Création de Contenu",
|
||||
"interests.hint": "Vous pouvez modifier cela à tout moment dans les paramètres",
|
||||
"interests.placeholder": "Indiquez vos centres d’intérêt...",
|
||||
"interests.title": "Pourriez-vous me dire quels sont vos domaines d’intérêt ?",
|
||||
"interests.title2": "Cela m’aidera à me faire une première idée de vous~",
|
||||
"interests.title3": "Prenez votre temps, je vais apprendre à mieux vous connaître",
|
||||
"modeSelection.desc": "Choisissez le mode qui vous convient le mieux",
|
||||
"modeSelection.hint": "Vous pouvez modifier cela à tout moment dans les paramètres",
|
||||
"modeSelection.lite.desc": "Idéal pour les conversations quotidiennes, les questions-réponses, la productivité légère et l’exploration de modèles",
|
||||
|
||||
@@ -12,9 +12,6 @@
|
||||
"interests.area.writing": "Creazione di Contenuti",
|
||||
"interests.hint": "Puoi modificarlo in qualsiasi momento nelle impostazioni",
|
||||
"interests.placeholder": "Inserisci i tuoi interessi...",
|
||||
"interests.title": "Mi diresti in quali ambiti sei interessato?",
|
||||
"interests.title2": "Questo mi aiuterà a farmi una prima impressione di te~",
|
||||
"interests.title3": "Prenditi il tuo tempo, ti conoscerò sempre meglio",
|
||||
"modeSelection.desc": "Scegli la modalità che fa più per te",
|
||||
"modeSelection.hint": "Puoi modificarla in qualsiasi momento nelle impostazioni",
|
||||
"modeSelection.lite.desc": "Ideale per conversazioni quotidiane, domande e risposte, produttività leggera e test dei modelli",
|
||||
|
||||
@@ -12,9 +12,6 @@
|
||||
"interests.area.writing": "コンテンツ制作",
|
||||
"interests.hint": "設定からいつでも変更できます",
|
||||
"interests.placeholder": "興味のある分野を入力してください...",
|
||||
"interests.title": "興味のある分野を教えてもらえますか?",
|
||||
"interests.title2": "あなたの第一印象をつかむ手助けをさせてください~",
|
||||
"interests.title3": "ゆっくりで大丈夫、もっとあなたを理解していきます",
|
||||
"modeSelection.desc": "あなたに合った使用モードを選択してください",
|
||||
"modeSelection.hint": "設定からいつでも変更できます",
|
||||
"modeSelection.lite.desc": "日常会話やQ&A、軽作業のサポートやモデル体験に最適",
|
||||
|
||||
@@ -12,9 +12,6 @@
|
||||
"interests.area.writing": "콘텐츠 제작",
|
||||
"interests.hint": "설정에서 언제든지 수정할 수 있어요",
|
||||
"interests.placeholder": "관심 있는 분야를 입력해 주세요...",
|
||||
"interests.title": "관심 있는 분야를 알려주실 수 있나요?",
|
||||
"interests.title2": "당신에 대한 첫인상을 형성하는 데 도움이 돼요~",
|
||||
"interests.title3": "천천히 해요, 점점 더 잘 알게 될 거예요",
|
||||
"modeSelection.desc": "당신에게 맞는 사용 모드를 선택하세요",
|
||||
"modeSelection.hint": "설정에서 언제든지 변경할 수 있어요",
|
||||
"modeSelection.lite.desc": "일상 대화, Q&A, 가벼운 업무 보조 및 모델 체험에 적합",
|
||||
|
||||
@@ -12,9 +12,6 @@
|
||||
"interests.area.writing": "Contentcreatie",
|
||||
"interests.hint": "Je kunt dit op elk moment aanpassen in de instellingen",
|
||||
"interests.placeholder": "Voer je interesses in...",
|
||||
"interests.title": "Kun je me vertellen waar je in geïnteresseerd bent?",
|
||||
"interests.title2": "Zo krijg ik alvast een eerste indruk van je~",
|
||||
"interests.title3": "Neem je tijd, ik leer je steeds beter kennen",
|
||||
"modeSelection.desc": "Kies de modus die het beste bij je past",
|
||||
"modeSelection.hint": "Je kunt dit op elk moment aanpassen in de instellingen",
|
||||
"modeSelection.lite.desc": "Ideaal voor dagelijkse gesprekken, Q&A, lichte productiviteit en het uitproberen van modellen",
|
||||
|
||||
@@ -12,9 +12,6 @@
|
||||
"interests.area.writing": "Tworzenie treści",
|
||||
"interests.hint": "Możesz to zmienić w każdej chwili w ustawieniach",
|
||||
"interests.placeholder": "Wpisz swoje zainteresowania...",
|
||||
"interests.title": "Powiesz mi, czym się interesujesz?",
|
||||
"interests.title2": "To pomoże mi wyrobić sobie o Tobie pierwsze wrażenie~",
|
||||
"interests.title3": "Nie spiesz się, z czasem poznam Cię coraz lepiej",
|
||||
"modeSelection.desc": "Wybierz tryb pracy, który najbardziej Ci odpowiada",
|
||||
"modeSelection.hint": "Możesz to zmienić w każdej chwili w ustawieniach",
|
||||
"modeSelection.lite.desc": "Idealny do codziennych rozmów, pytań i odpowiedzi, lekkiej pracy i testowania modeli",
|
||||
|
||||
@@ -12,9 +12,6 @@
|
||||
"interests.area.writing": "Criação de Conteúdo",
|
||||
"interests.hint": "Você pode mudar isso a qualquer momento nas configurações",
|
||||
"interests.placeholder": "Digite seus interesses...",
|
||||
"interests.title": "Você pode me contar em quais áreas tem interesse?",
|
||||
"interests.title2": "Isso vai me ajudar a ter uma primeira impressão sobre você~",
|
||||
"interests.title3": "Sem pressa, vou te conhecer cada vez melhor",
|
||||
"modeSelection.desc": "Escolha o modo que combina mais com você",
|
||||
"modeSelection.hint": "Você pode mudar isso a qualquer momento nas configurações",
|
||||
"modeSelection.lite.desc": "Ideal para conversas do dia a dia, perguntas e respostas, produtividade leve e testes de modelos",
|
||||
|
||||
@@ -12,9 +12,6 @@
|
||||
"interests.area.writing": "Создание контента",
|
||||
"interests.hint": "Вы можете изменить это в любое время в настройках",
|
||||
"interests.placeholder": "Введите ваши интересы...",
|
||||
"interests.title": "Расскажите, пожалуйста, какие области вам интересны?",
|
||||
"interests.title2": "Это поможет мне составить первое впечатление о вас~",
|
||||
"interests.title3": "Не спешите, я буду узнавать вас всё лучше и лучше",
|
||||
"modeSelection.desc": "Выберите режим, который вам больше подходит",
|
||||
"modeSelection.hint": "Вы можете изменить это в любое время в настройках",
|
||||
"modeSelection.lite.desc": "Идеально для повседневного общения, вопросов и ответов, лёгкой продуктивности и тестирования моделей",
|
||||
|
||||
@@ -12,9 +12,6 @@
|
||||
"interests.area.writing": "İçerik Üretimi",
|
||||
"interests.hint": "Bunu ayarlardan istediğiniz zaman değiştirebilirsiniz",
|
||||
"interests.placeholder": "İlgi alanlarınızı girin...",
|
||||
"interests.title": "Hangi alanlara ilgi duyduğunuzu söyler misiniz?",
|
||||
"interests.title2": "Bu, sizinle ilgili ilk izlenimi edinmeme yardımcı olacak~",
|
||||
"interests.title3": "Acelemiz yok, sizi zamanla daha iyi tanıyacağım",
|
||||
"modeSelection.desc": "Size en uygun modu seçin",
|
||||
"modeSelection.hint": "Bunu ayarlardan istediğiniz zaman değiştirebilirsiniz",
|
||||
"modeSelection.lite.desc": "Günlük sohbetler, soru-cevap, hafif işler ve modelleri denemek için ideal",
|
||||
|
||||
@@ -12,9 +12,6 @@
|
||||
"interests.area.writing": "Sáng tạo nội dung",
|
||||
"interests.hint": "Bạn có thể thay đổi điều này bất cứ lúc nào trong cài đặt",
|
||||
"interests.placeholder": "Nhập sở thích của bạn...",
|
||||
"interests.title": "Bạn có thể cho tôi biết bạn quan tâm đến lĩnh vực nào không?",
|
||||
"interests.title2": "Điều này sẽ giúp tôi có ấn tượng đầu tiên về bạn~",
|
||||
"interests.title3": "Cứ từ từ nhé, tôi sẽ hiểu bạn rõ hơn theo thời gian",
|
||||
"modeSelection.desc": "Chọn chế độ làm việc phù hợp với bạn nhất",
|
||||
"modeSelection.hint": "Bạn có thể thay đổi điều này bất cứ lúc nào trong cài đặt",
|
||||
"modeSelection.lite.desc": "Phù hợp cho trò chuyện hàng ngày, hỏi đáp, làm việc nhẹ và thử nghiệm mô hình",
|
||||
|
||||
@@ -134,6 +134,8 @@
|
||||
"cmdk.search.communityAgent": "社区助理",
|
||||
"cmdk.search.file": "文件",
|
||||
"cmdk.search.files": "文件",
|
||||
"cmdk.search.folder": "文件夹",
|
||||
"cmdk.search.folders": "文件夹",
|
||||
"cmdk.search.loading": "搜索中…",
|
||||
"cmdk.search.market": "社区",
|
||||
"cmdk.search.mcp": "MCP 服务器",
|
||||
|
||||
@@ -12,9 +12,6 @@
|
||||
"interests.area.writing": "内容创作",
|
||||
"interests.hint": "可随时在设置中修改",
|
||||
"interests.placeholder": "搜索或输入领域…",
|
||||
"interests.title": "能告诉我你感兴趣的领域吗?",
|
||||
"interests.title2": "帮我建立对你的初步印象",
|
||||
"interests.title3": "慢慢来,我会越来越懂你",
|
||||
"modeSelection.desc": "选择适合你的工作方式",
|
||||
"modeSelection.hint": "可随时在设置中切换",
|
||||
"modeSelection.lite.desc": "适合日常对话、问答与轻量任务",
|
||||
|
||||
@@ -12,9 +12,6 @@
|
||||
"interests.area.writing": "內容創作",
|
||||
"interests.hint": "你可以隨時在設定中修改",
|
||||
"interests.placeholder": "請輸入你感興趣的領域...",
|
||||
"interests.title": "可以告訴我你感興趣的領域嗎?",
|
||||
"interests.title2": "幫助我建立對你的初步印象~",
|
||||
"interests.title3": "慢慢來,我會越來越了解你",
|
||||
"modeSelection.desc": "選擇適合你的使用模式",
|
||||
"modeSelection.hint": "你可以隨時在設定中修改",
|
||||
"modeSelection.lite.desc": "適合日常對話與問答、輕度工作輔助與模型體驗",
|
||||
|
||||
@@ -9,6 +9,7 @@ export type SearchResultType =
|
||||
| 'agent'
|
||||
| 'topic'
|
||||
| 'file'
|
||||
| 'folder'
|
||||
| 'memory'
|
||||
| 'message'
|
||||
| 'mcp'
|
||||
@@ -60,6 +61,12 @@ export interface FileSearchResult extends BaseSearchResult {
|
||||
url: string | null;
|
||||
}
|
||||
|
||||
export interface FolderSearchResult extends BaseSearchResult {
|
||||
knowledgeBaseId: string | null;
|
||||
slug: string | null;
|
||||
type: 'folder';
|
||||
}
|
||||
|
||||
export interface MessageSearchResult extends BaseSearchResult {
|
||||
agentId: string | null;
|
||||
content: string;
|
||||
@@ -106,6 +113,7 @@ export type SearchResult =
|
||||
| AgentSearchResult
|
||||
| TopicSearchResult
|
||||
| FileSearchResult
|
||||
| FolderSearchResult
|
||||
| MessageSearchResult
|
||||
| MCPSearchResult
|
||||
| PluginSearchResult
|
||||
@@ -167,6 +175,9 @@ export class SearchRepo {
|
||||
if ((!type || type === 'file') && limits.file > 0) {
|
||||
queries.push(this.buildFileQuery(searchTerm, exactQuery, prefixQuery, limits.file));
|
||||
}
|
||||
if ((!type || type === 'folder') && limits.folder > 0) {
|
||||
queries.push(this.buildFolderQuery(searchTerm, exactQuery, prefixQuery, limits.folder));
|
||||
}
|
||||
if ((!type || type === 'page') && limits.page > 0) {
|
||||
queries.push(this.buildPageQuery(searchTerm, exactQuery, prefixQuery, limits.page));
|
||||
}
|
||||
@@ -192,7 +203,7 @@ export class SearchRepo {
|
||||
* Calculate result limits based on context
|
||||
* - Agent context: expand topics (6) and messages (6), limit others (3 each)
|
||||
* - Page context: expand pages (6), limit others (3 each)
|
||||
* - Resource context: expand files (6), limit others (3 each)
|
||||
* - Resource context: expand files (6) and folders (6), limit others (3 each)
|
||||
* - General context: limit all types to 3 each
|
||||
*/
|
||||
private calculateLimits(
|
||||
@@ -203,6 +214,7 @@ export class SearchRepo {
|
||||
): {
|
||||
agent: number;
|
||||
file: number;
|
||||
folder: number;
|
||||
message: number;
|
||||
page: number;
|
||||
pageContent: number;
|
||||
@@ -213,6 +225,7 @@ export class SearchRepo {
|
||||
return {
|
||||
agent: type === 'agent' ? baseLimit : 0,
|
||||
file: type === 'file' ? baseLimit : 0,
|
||||
folder: type === 'folder' ? baseLimit : 0,
|
||||
message: type === 'message' ? baseLimit : 0,
|
||||
page: type === 'page' ? baseLimit : 0,
|
||||
pageContent: type === 'pageContent' ? baseLimit : 0,
|
||||
@@ -225,6 +238,7 @@ export class SearchRepo {
|
||||
return {
|
||||
agent: 3,
|
||||
file: 3,
|
||||
folder: 3,
|
||||
message: 3,
|
||||
page: 6,
|
||||
pageContent: 0, // Not available yet
|
||||
@@ -232,11 +246,12 @@ export class SearchRepo {
|
||||
};
|
||||
}
|
||||
|
||||
// Resource context: expand files to 6, limit others to 3
|
||||
// Resource context: expand files and folders to 6, limit others to 3
|
||||
if (contextType === 'resource') {
|
||||
return {
|
||||
agent: 3,
|
||||
file: 6,
|
||||
folder: 6,
|
||||
message: 3,
|
||||
page: 3,
|
||||
pageContent: 0, // Not available yet
|
||||
@@ -249,6 +264,7 @@ export class SearchRepo {
|
||||
return {
|
||||
agent: 3,
|
||||
file: 3,
|
||||
folder: 3,
|
||||
message: 6,
|
||||
page: 3,
|
||||
pageContent: 0, // Not available yet
|
||||
@@ -260,6 +276,7 @@ export class SearchRepo {
|
||||
return {
|
||||
agent: 3,
|
||||
file: 3,
|
||||
folder: 3,
|
||||
message: 3,
|
||||
page: 3,
|
||||
pageContent: 0, // Not available yet
|
||||
@@ -267,6 +284,20 @@ export class SearchRepo {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Truncate content at database level with ellipsis indicator
|
||||
* Uses SQL LEFT() function for efficient truncation
|
||||
* Note: This helper is defined for documentation but not currently used.
|
||||
* Truncation is implemented inline in query methods for better SQL readability.
|
||||
*/
|
||||
private truncateContent(columnName: string, maxLength: number): string {
|
||||
return `CASE
|
||||
WHEN LENGTH(${columnName}) > ${maxLength}
|
||||
THEN LEFT(${columnName}, ${maxLength}) || '...'
|
||||
ELSE ${columnName}
|
||||
END`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build agent search query
|
||||
* Searches: title, description, slug, tags (JSONB array)
|
||||
@@ -362,7 +393,10 @@ export class SearchRepo {
|
||||
t.id,
|
||||
'topic' as type,
|
||||
t.title,
|
||||
t.content as description,
|
||||
CASE
|
||||
WHEN length(COALESCE(t.content, '')) > 200 THEN substring(COALESCE(t.content, ''), 1, 200) || '...'
|
||||
ELSE t.content
|
||||
END as description,
|
||||
NULL::varchar(100) as slug,
|
||||
NULL::text as avatar,
|
||||
NULL::text as background_color,
|
||||
@@ -447,7 +481,7 @@ export class SearchRepo {
|
||||
m.id,
|
||||
'message' as type,
|
||||
CASE
|
||||
WHEN length(m.content) > 100 THEN substring(m.content, 1, 100) || '...'
|
||||
WHEN length(m.content) > 200 THEN substring(m.content, 1, 200) || '...'
|
||||
ELSE m.content
|
||||
END as title,
|
||||
COALESCE(a.title, 'General Chat') as description,
|
||||
@@ -492,7 +526,10 @@ export class SearchRepo {
|
||||
f.id,
|
||||
'file' as type,
|
||||
f.name as title,
|
||||
d.content as description,
|
||||
CASE
|
||||
WHEN length(COALESCE(d.content, '')) > 200 THEN substring(COALESCE(d.content, ''), 1, 200) || '...'
|
||||
ELSE d.content
|
||||
END as description,
|
||||
NULL::varchar(100) as slug,
|
||||
NULL::text as avatar,
|
||||
NULL::text as background_color,
|
||||
@@ -520,13 +557,16 @@ export class SearchRepo {
|
||||
AND f.name ILIKE ${searchTerm}
|
||||
`;
|
||||
|
||||
// Query for standalone documents (not pages and not linked to files)
|
||||
// Query for standalone documents (not pages, not folders, and not linked to files)
|
||||
const documentQuery = sql`
|
||||
SELECT
|
||||
d.id,
|
||||
'file' as type,
|
||||
COALESCE(d.title, d.filename, 'Untitled') as title,
|
||||
d.content as description,
|
||||
CASE
|
||||
WHEN length(COALESCE(d.content, '')) > 200 THEN substring(COALESCE(d.content, ''), 1, 200) || '...'
|
||||
ELSE d.content
|
||||
END as description,
|
||||
NULL::varchar(100) as slug,
|
||||
NULL::text as avatar,
|
||||
NULL::text as background_color,
|
||||
@@ -552,6 +592,7 @@ export class SearchRepo {
|
||||
WHERE d.user_id = ${this.userId}
|
||||
AND d.source_type != 'file'
|
||||
AND d.file_type != 'custom/document'
|
||||
AND d.file_type != 'custom/folder'
|
||||
AND (
|
||||
COALESCE(d.title, '') ILIKE ${searchTerm}
|
||||
OR COALESCE(d.filename, '') ILIKE ${searchTerm}
|
||||
@@ -571,6 +612,54 @@ export class SearchRepo {
|
||||
`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build folder search query
|
||||
* Searches folders in the documents table (file_type='custom/folder')
|
||||
*/
|
||||
private buildFolderQuery(
|
||||
searchTerm: string,
|
||||
exactQuery: string,
|
||||
prefixQuery: string,
|
||||
limit: number,
|
||||
): ReturnType<typeof sql> {
|
||||
return sql`
|
||||
SELECT
|
||||
d.id,
|
||||
'folder' as type,
|
||||
COALESCE(d.title, d.filename, 'Untitled') as title,
|
||||
d.description,
|
||||
d.slug,
|
||||
NULL::text as avatar,
|
||||
NULL::text as background_color,
|
||||
NULL::jsonb as tags,
|
||||
d.created_at,
|
||||
d.updated_at,
|
||||
CASE
|
||||
WHEN COALESCE(d.title, d.filename) ILIKE ${exactQuery} THEN 1
|
||||
WHEN COALESCE(d.title, d.filename) ILIKE ${prefixQuery} THEN 2
|
||||
ELSE 3
|
||||
END as relevance,
|
||||
NULL::boolean as favorite,
|
||||
NULL::text as session_id,
|
||||
NULL::text as agent_id,
|
||||
COALESCE(d.title, d.filename, 'Untitled') as name,
|
||||
d.file_type,
|
||||
NULL::integer as size,
|
||||
NULL::text as url,
|
||||
d.knowledge_base_id
|
||||
FROM ${documents} d
|
||||
WHERE d.user_id = ${this.userId}
|
||||
AND d.file_type = 'custom/folder'
|
||||
AND (
|
||||
COALESCE(d.title, '') ILIKE ${searchTerm}
|
||||
OR COALESCE(d.filename, '') ILIKE ${searchTerm}
|
||||
OR COALESCE(d.description, '') ILIKE ${searchTerm}
|
||||
)
|
||||
ORDER BY relevance ASC, updated_at DESC
|
||||
LIMIT ${limit}
|
||||
`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build page search query
|
||||
* Fast search on page titles only (no content search for better performance)
|
||||
@@ -635,7 +724,10 @@ export class SearchRepo {
|
||||
d.id,
|
||||
'pageContent' as type,
|
||||
COALESCE(d.title, d.filename, 'Untitled') as title,
|
||||
d.content as description,
|
||||
CASE
|
||||
WHEN length(COALESCE(d.content, '')) > 200 THEN substring(COALESCE(d.content, ''), 1, 200) || '...'
|
||||
ELSE d.content
|
||||
END as description,
|
||||
NULL::varchar(100) as slug,
|
||||
NULL::text as avatar,
|
||||
NULL::text as background_color,
|
||||
@@ -737,6 +829,14 @@ export class SearchRepo {
|
||||
url: row.url,
|
||||
};
|
||||
}
|
||||
case 'folder': {
|
||||
return {
|
||||
...base,
|
||||
knowledgeBaseId: row.knowledge_base_id,
|
||||
slug: row.slug,
|
||||
type: 'folder' as const,
|
||||
};
|
||||
}
|
||||
case 'message': {
|
||||
return {
|
||||
...base,
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
Bot,
|
||||
ChevronRight,
|
||||
FileText,
|
||||
Folder,
|
||||
MessageCircle,
|
||||
MessageSquare,
|
||||
Plug,
|
||||
@@ -75,6 +76,18 @@ const SearchResults = memo<SearchResultsProps>(
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'folder': {
|
||||
// Navigate to folder by slug
|
||||
if (result.knowledgeBaseId && result.slug) {
|
||||
navigate(`/resource/library/${result.knowledgeBaseId}/${result.slug}`);
|
||||
} else if (result.slug) {
|
||||
navigate(`/resource/library/${result.slug}`);
|
||||
} else {
|
||||
// Fallback to library root if no slug
|
||||
navigate(`/resource/library`);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'page': {
|
||||
navigate(`/page/${result.id.split('_')[1]}`);
|
||||
break;
|
||||
@@ -109,6 +122,9 @@ const SearchResults = memo<SearchResultsProps>(
|
||||
case 'file': {
|
||||
return <FileText size={16} />;
|
||||
}
|
||||
case 'folder': {
|
||||
return <Folder size={16} />;
|
||||
}
|
||||
case 'page': {
|
||||
return <FileText size={16} />;
|
||||
}
|
||||
@@ -138,6 +154,9 @@ const SearchResults = memo<SearchResultsProps>(
|
||||
case 'file': {
|
||||
return t('cmdk.search.file');
|
||||
}
|
||||
case 'folder': {
|
||||
return t('cmdk.search.folder');
|
||||
}
|
||||
case 'page': {
|
||||
return t('cmdk.search.page');
|
||||
}
|
||||
@@ -198,6 +217,7 @@ const SearchResults = memo<SearchResultsProps>(
|
||||
const agentResults = results.filter((r) => r.type === 'agent');
|
||||
const topicResults = results.filter((r) => r.type === 'topic');
|
||||
const fileResults = results.filter((r) => r.type === 'file');
|
||||
const folderResults = results.filter((r) => r.type === 'folder');
|
||||
const pageResults = results.filter((r) => r.type === 'page');
|
||||
const mcpResults = results.filter((r) => r.type === 'mcp');
|
||||
const pluginResults = results.filter((r) => r.type === 'plugin');
|
||||
@@ -315,6 +335,13 @@ const SearchResults = memo<SearchResultsProps>(
|
||||
</Command.Group>
|
||||
)}
|
||||
|
||||
{folderResults.length > 0 && (
|
||||
<Command.Group>
|
||||
{folderResults.map((result) => renderResultItem(result))}
|
||||
{renderSearchMore('folder', folderResults.length)}
|
||||
</Command.Group>
|
||||
)}
|
||||
|
||||
{mcpResults.length > 0 && (
|
||||
<Command.Group>
|
||||
{mcpResults.map((result) => renderResultItem(result))}
|
||||
|
||||
@@ -16,6 +16,7 @@ const VALID_TYPES = [
|
||||
'topic',
|
||||
'message',
|
||||
'file',
|
||||
'folder',
|
||||
'page',
|
||||
'mcp',
|
||||
'plugin',
|
||||
|
||||
@@ -148,6 +148,8 @@ export default {
|
||||
'cmdk.search.communityAgent': 'Community Agent',
|
||||
'cmdk.search.file': 'File',
|
||||
'cmdk.search.files': 'Files',
|
||||
'cmdk.search.folder': 'Folder',
|
||||
'cmdk.search.folders': 'Folders',
|
||||
'cmdk.search.loading': 'Searching...',
|
||||
'cmdk.search.market': 'Community',
|
||||
'cmdk.search.mcp': 'MCP Server',
|
||||
|
||||
@@ -12,9 +12,9 @@ export default {
|
||||
'interests.area.writing': 'Content Creation',
|
||||
'interests.hint': 'You can change this anytime in settings',
|
||||
'interests.placeholder': 'Enter your interests...',
|
||||
'interests.title': "Could you tell me what areas you're interested in?",
|
||||
'interests.title2': 'This will help me get a first impression of you~',
|
||||
'interests.title3': "Take your time, I'll get to know you better and better",
|
||||
'interests.title': "What areas you're interested in?",
|
||||
'interests.title2': 'This will help me know you better',
|
||||
'interests.title3': "Take your time, I'll get to know you better",
|
||||
'modeSelection.desc': 'Choose the mode that suits you best',
|
||||
'modeSelection.hint': 'You can change this anytime in settings',
|
||||
'modeSelection.lite.desc':
|
||||
|
||||
@@ -45,7 +45,17 @@ export const searchRouter = router({
|
||||
offset: z.number().optional(),
|
||||
query: z.string(),
|
||||
type: z
|
||||
.enum(['agent', 'topic', 'file', 'message', 'page', 'mcp', 'plugin', 'communityAgent'])
|
||||
.enum([
|
||||
'agent',
|
||||
'topic',
|
||||
'file',
|
||||
'folder',
|
||||
'message',
|
||||
'page',
|
||||
'mcp',
|
||||
'plugin',
|
||||
'communityAgent',
|
||||
])
|
||||
.optional(),
|
||||
}),
|
||||
)
|
||||
@@ -58,8 +68,8 @@ export const searchRouter = router({
|
||||
// Build search promises based on type filter
|
||||
const searchPromises: Promise<any>[] = [];
|
||||
|
||||
// Database searches (agent, topic, file, message, page)
|
||||
if (!type || ['agent', 'topic', 'file', 'message', 'page'].includes(type)) {
|
||||
// Database searches (agent, topic, file, folder, message, page)
|
||||
if (!type || ['agent', 'topic', 'file', 'folder', 'message', 'page'].includes(type)) {
|
||||
searchPromises.push(ctx.searchRepo.search(input));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user