From f981ea04c3cc97db5ab68eee3b7adb57daef7102 Mon Sep 17 00:00:00 2001 From: Mendon Kissling <59585235+mendonk@users.noreply.github.com> Date: Thu, 18 Sep 2025 13:24:41 -0400 Subject: [PATCH 01/20] initial-build-no-errors --- docs/.gitignore | 20 ++ docs/README.md | 41 +++ docs/{ => docs/configure}/configuration.md | 5 + .../get-started/docker.mdx} | 5 + docs/docs/get-started/intro.mdx | 48 ++++ docs/{tui.md => docs/get-started/tui.mdx} | 7 +- .../reference/troubleshooting.mdx} | 5 + docs/docusaurus.config.js | 119 +++++++++ docs/sidebars.js | 65 +++++ docs/src/css/custom.css | 30 +++ docs/static/.nojekyll | 0 ...OpenRAG_TUI_2025-09-10T13_04_11_757637.svg | 237 ++++++++++++++++++ docs/static/img/favicon.ico | Bin 0 -> 15406 bytes docs/static/img/logo.svg | 1 + 14 files changed, 582 insertions(+), 1 deletion(-) create mode 100644 docs/.gitignore create mode 100644 docs/README.md rename docs/{ => docs/configure}/configuration.md (96%) rename docs/{docker.md => docs/get-started/docker.mdx} (91%) create mode 100644 docs/docs/get-started/intro.mdx rename docs/{tui.md => docs/get-started/tui.mdx} (92%) rename docs/{troubleshooting.md => docs/reference/troubleshooting.mdx} (89%) create mode 100644 docs/docusaurus.config.js create mode 100644 docs/sidebars.js create mode 100644 docs/src/css/custom.css create mode 100644 docs/static/.nojekyll create mode 100644 docs/static/img/OpenRAG_TUI_2025-09-10T13_04_11_757637.svg create mode 100644 docs/static/img/favicon.ico create mode 100644 docs/static/img/logo.svg diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 00000000..b2d6de30 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,20 @@ +# Dependencies +/node_modules + +# Production +/build + +# Generated files +.docusaurus +.cache-loader + +# Misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..b28211a9 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,41 @@ +# Website + +This website is built using [Docusaurus](https://docusaurus.io/), a modern static website generator. + +## Installation + +```bash +yarn +``` + +## Local Development + +```bash +yarn start +``` + +This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. + +## Build + +```bash +yarn build +``` + +This command generates static content into the `build` directory and can be served using any static contents hosting service. + +## Deployment + +Using SSH: + +```bash +USE_SSH=true yarn deploy +``` + +Not using SSH: + +```bash +GIT_USER= yarn deploy +``` + +If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. diff --git a/docs/configuration.md b/docs/docs/configure/configuration.md similarity index 96% rename from docs/configuration.md rename to docs/docs/configure/configuration.md index fcc76f37..a1986d93 100644 --- a/docs/configuration.md +++ b/docs/docs/configure/configuration.md @@ -1,3 +1,8 @@ +--- +title: Configuration +slug: /configure/configuration +--- + # Configuration OpenRAG uses environment variables for configuration. Copy `.env.example` to `.env` and populate with your values: diff --git a/docs/docker.md b/docs/docs/get-started/docker.mdx similarity index 91% rename from docs/docker.md rename to docs/docs/get-started/docker.mdx index ab0cb71d..a394bc69 100644 --- a/docs/docker.md +++ b/docs/docs/get-started/docker.mdx @@ -1,3 +1,8 @@ +--- +title: Docker Deployment +slug: /get-started/docker +--- + # Docker Deployment ## Standard Deployment diff --git a/docs/docs/get-started/intro.mdx b/docs/docs/get-started/intro.mdx new file mode 100644 index 00000000..22763874 --- /dev/null +++ b/docs/docs/get-started/intro.mdx @@ -0,0 +1,48 @@ +--- +title: What is OpenRAG? +slug: / +--- + +# OpenRAG Introduction + +Let's discover **Docusaurus in less than 5 minutes**. + +## Getting Started + +Get started by **creating a new site**. + +Or **try Docusaurus immediately** with **[docusaurus.new](https://docusaurus.new)**. + +### What you'll need + +- [Node.js](https://nodejs.org/en/download/) version 18.0 or above: + - When installing Node.js, you are recommended to check all checkboxes related to dependencies. + +## Generate a new site + +Generate a new Docusaurus site using the **classic template**. + +The classic template will automatically be added to your project after you run the command: + +```bash +npm init docusaurus@latest my-website classic +``` + +You can type this command into Command Prompt, Powershell, Terminal, or any other integrated terminal of your code editor. + +The command also installs all necessary dependencies you need to run Docusaurus. + +## Start your site + +Run the development server: + +```bash +cd my-website +npm run start +``` + +The `cd` command changes the directory you're working with. In order to work with your newly created Docusaurus site, you'll need to navigate the terminal there. + +The `npm run start` command builds your website locally and serves it through a development server, ready for you to view at http://localhost:3000/. + +Open `docs/intro.md` (this page) and edit some lines: the site **reloads automatically** and displays your changes. diff --git a/docs/tui.md b/docs/docs/get-started/tui.mdx similarity index 92% rename from docs/tui.md rename to docs/docs/get-started/tui.mdx index 87fedfe0..2f0a048d 100644 --- a/docs/tui.md +++ b/docs/docs/get-started/tui.mdx @@ -1,8 +1,13 @@ +--- +title: Terminal Interface (TUI) +slug: /get-started/tui +--- + # OpenRAG TUI Guide The OpenRAG Terminal User Interface (TUI) provides a streamlined way to set up, configure, and monitor your OpenRAG deployment directly from the terminal. -![OpenRAG TUI Interface](../assets/OpenRAG_TUI_2025-09-10T13_04_11_757637.svg) +![OpenRAG TUI Interface](@site/static/img/OpenRAG_TUI_2025-09-10T13_04_11_757637.svg) ## Launch diff --git a/docs/troubleshooting.md b/docs/docs/reference/troubleshooting.mdx similarity index 89% rename from docs/troubleshooting.md rename to docs/docs/reference/troubleshooting.mdx index 5ffd2bc8..c1893ef5 100644 --- a/docs/troubleshooting.md +++ b/docs/docs/reference/troubleshooting.mdx @@ -1,3 +1,8 @@ +--- +title: Troubleshooting +slug: /reference/troubleshooting +--- + # Troubleshooting ## Podman on macOS diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js new file mode 100644 index 00000000..22c0944f --- /dev/null +++ b/docs/docusaurus.config.js @@ -0,0 +1,119 @@ +// @ts-check +// `@type` JSDoc annotations allow editor autocompletion and type checking +// (when paired with `@ts-check`). +// There are various equivalent ways to declare your Docusaurus config. +// See: https://docusaurus.io/docs/api/docusaurus-config + +import {themes as prismThemes} from 'prism-react-renderer'; + +// This runs in Node.js - Don't use client-side code here (browser APIs, JSX...) + +/** @type {import('@docusaurus/types').Config} */ +const config = { + title: 'OpenRAG', + tagline: 'Open Source RAG Platform', + favicon: 'img/favicon.ico', + + // Future flags, see https://docusaurus.io/docs/api/docusaurus-config#future + future: { + v4: true, // Improve compatibility with the upcoming Docusaurus v4 + }, + + // Set the production url of your site here + url: 'https://openrag.io', + // Set the // pathname under which your site is served + // For GitHub pages deployment, it is often '//' + baseUrl: '/', + + // GitHub pages deployment config. + // If you aren't using GitHub pages, you don't need these. + organizationName: 'langflow-ai', // Usually your GitHub org/user name. + projectName: 'openrag', // Usually your repo name. + + onBrokenLinks: 'throw', + onBrokenMarkdownLinks: 'warn', + + // Even if you don't use internationalization, you can use this field to set + // useful metadata like html lang. For example, if your site is Chinese, you + // may want to replace "en" with "zh-Hans". + i18n: { + defaultLocale: 'en', + locales: ['en'], + }, + + presets: [ + [ + 'classic', + /** @type {import('@docusaurus/preset-classic').Options} */ + ({ + docs: { + sidebarPath: './sidebars.js', + // Please change this to your repo. + // Remove this to remove the "edit this page" links. + editUrl: + 'https://github.com/openrag/openrag/tree/main/docs/', + routeBasePath: '/', + }, + theme: { + customCss: './src/css/custom.css', + }, + }), + ], + ], + + themeConfig: + /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ + ({ + // Replace with your project's social card + image: 'img/docusaurus-social-card.jpg', + navbar: { + title: 'OpenRAG', + logo: { + alt: 'OpenRAG Logo', + src: 'img/logo.svg', + href: '/', + }, + items: [ + { + href: 'https://github.com/openrag/openrag', + label: 'GitHub', + position: 'right', + }, + ], + }, + footer: { + style: 'dark', + links: [ + { + title: 'Documentation', + items: [ + { + label: 'Getting Started', + to: '/', + }, + ], + }, + { + title: 'Community', + items: [ + { + label: 'GitHub', + href: 'https://github.com/openrag/openrag', + }, + { + label: 'Discord', + href: 'https://discord.gg/openrag', + }, + ], + }, + ], + copyright: `Copyright © ${new Date().getFullYear()} OpenRAG. Built with Docusaurus.`, + }, + prism: { + theme: prismThemes.github, + darkTheme: prismThemes.dracula, + }, + }), +}; + +export default config; diff --git a/docs/sidebars.js b/docs/sidebars.js new file mode 100644 index 00000000..51a4ddc3 --- /dev/null +++ b/docs/sidebars.js @@ -0,0 +1,65 @@ +// @ts-check + +// This runs in Node.js - Don't use client-side code here (browser APIs, JSX...) + +/** + * Creating a sidebar enables you to: + - create an ordered group of docs + - render a sidebar for each doc of that group + - provide next/previous navigation + + The sidebars can be generated from the filesystem, or explicitly defined here. + + Create as many sidebars as you want. + + @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} + */ +const sidebars = { + tutorialSidebar: [ + { + type: "category", + label: "Get Started", + items: [ + { + type: "doc", + id: "get-started/intro", + label: "Introduction" + }, + { + type: "doc", + id: "get-started/docker", + label: "Docker Deployment" + }, + { + type: "doc", + id: "get-started/tui", + label: "Terminal Interface (TUI)" + }, + ], + }, + { + type: "category", + label: "Configuration", + items: [ + { + type: "doc", + id: "configure/configuration", + label: "Environment Variables" + }, + ], + }, + { + type: "category", + label: "Reference", + items: [ + { + type: "doc", + id: "reference/troubleshooting", + label: "Troubleshooting" + }, + ], + }, + ], +}; + +export default sidebars; diff --git a/docs/src/css/custom.css b/docs/src/css/custom.css new file mode 100644 index 00000000..2bc6a4cf --- /dev/null +++ b/docs/src/css/custom.css @@ -0,0 +1,30 @@ +/** + * Any CSS included here will be global. The classic template + * bundles Infima by default. Infima is a CSS framework designed to + * work well for content-centric websites. + */ + +/* You can override the default Infima variables here. */ +:root { + --ifm-color-primary: #2e8555; + --ifm-color-primary-dark: #29784c; + --ifm-color-primary-darker: #277148; + --ifm-color-primary-darkest: #205d3b; + --ifm-color-primary-light: #33925d; + --ifm-color-primary-lighter: #359962; + --ifm-color-primary-lightest: #3cad6e; + --ifm-code-font-size: 95%; + --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); +} + +/* For readability concerns, you should choose a lighter palette in dark mode. */ +[data-theme='dark'] { + --ifm-color-primary: #25c2a0; + --ifm-color-primary-dark: #21af90; + --ifm-color-primary-darker: #1fa588; + --ifm-color-primary-darkest: #1a8870; + --ifm-color-primary-light: #29d5b0; + --ifm-color-primary-lighter: #32d8b4; + --ifm-color-primary-lightest: #4fddbf; + --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3); +} diff --git a/docs/static/.nojekyll b/docs/static/.nojekyll new file mode 100644 index 00000000..e69de29b diff --git a/docs/static/img/OpenRAG_TUI_2025-09-10T13_04_11_757637.svg b/docs/static/img/OpenRAG_TUI_2025-09-10T13_04_11_757637.svg new file mode 100644 index 00000000..f50bef7d --- /dev/null +++ b/docs/static/img/OpenRAG_TUI_2025-09-10T13_04_11_757637.svg @@ -0,0 +1,237 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OpenRAG TUI + + + + + + + + + + + + + + + + + + + + + + +██████╗ ██████╗ ███████╗███╗   ██╗██████╗  █████╗  ██████╗  +██╔═══██╗██╔══██╗██╔════╝████╗  ██║██╔══██╗██╔══██╗██╔════╝  +██║   ██║██████╔╝█████╗  ██╔██╗ ██║██████╔╝███████║██║  ███╗ +██║   ██║██╔═══╝ ██╔══╝  ██║╚██╗██║██╔══██╗██╔══██║██║   ██║ +╚██████╔╝██║     ███████╗██║ ╚████║██║  ██║██║  ██║╚██████╔╝ +╚═════╝ ╚═╝     ╚══════╝╚═╝  ╚═══╝╚═╝  ╚═╝╚═╝  ╚═╝╚═════╝ +Terminal User Interface for OpenRAG + +OAuth credentials detected — Advanced Setup recommended + + + + +▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + Advanced Setup  Monitor Services  +▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + + + + + + + + + + + + + q Quit  1 Basic Setup  2 Advanced Setup  3 Monitor Services  4 Diagnostics ^p palette + + + diff --git a/docs/static/img/favicon.ico b/docs/static/img/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..1225e208f5020eef37b182cbfedc8776ba1462ca GIT binary patch literal 15406 zcmeI2M~D?m6oz}8F^8E^#~kjcJO^{YhYISLKt)kR#D(BFyZ^j%cCdGluZ`t_?USg^p9U%h&@oH=tw zK79C4D;cw9&6555_shqRA7#;^MM0aJHf@qmpFYW>M~`If+O@&+C=5KCIdi5dfBEud zIdS5Ibn4Ve)~#D7SFc``#fulm*5@5{#*7(bWW0R&Qr^9LC-?5%Gcw}Z)8`vDY!K?a zdGp4|;CXRzvD~3kr^{)#I{fQyu3WwwQHA=LAq?&vOtFTv~%aq8tbbJ z%0ahp-!|nNHEJa7+O?A=O`80)zUtGeRjahhYrWqr_2FhttTQQ}%Y9~js)1_V@4k$r zY|;mb4b%f0U{CDQrHgd!+BL1cG+T3c+qP|H9p~rClPA)%XV0wLMYCqjq)nSP(!6=| zA7r08bt-6|%+se&vx=c($By#$?OW3Zd+pSzQ$rXI9Xb?hKie1v4jdSg&)I`>h>l-j zVWE_lmmA)@cW+QOTm8_ZM-Tb-?OPz1%$F};WbWL#0S3i_EpjGMdiCm+R99CUK6&z_ z8AC0bPD&q)9XnQL&z^10FUaQ{w{YRYpJGrfr1Vod?M9UyFknD{fqvPsV~4C-waW0c zY152+w;vS4=+UG9b^BnlF{o@(@KjV(1Q`R8J=n;r%ci~ z3`dV14ax7&p@Zpj`n`Mi?xA;?v}@M^1K*A|ZQs5=vg zdnvJjdT#^%5dD7%KZ)<6_5SiX@srehTTS+vdft!!!^a(@#{qqJ|lF<^*CU=D_5>GadyS^{P}YkF=B+|=jZ=m6LI*pE`FI@ z>VQuk`*rE@*W7CTlFJ%7w`8Dc5%#3)9tAj0vJU(6SfyDJi zCeP_V<{WoA;9k9Y$+c_OLQNANnm2D=jqRd?YfB*^{-0_;#-wqHOt99r4!+Kd7cZLF zlVZTGmn>N#ef##6K7INaE-NdO)~#Cydd1n2%E%Rd^dx424lWT-Q|3MtzGXX>%Qz70 z;+;+CUs+ixEnBt>^sQ}wW?GJMaBa!PM4q+k(xpow9axu#4<9ZQCr%8Qm<0C887fme zZarin6WQ93N>3h~qmskhU!5xQv`}Uc;cig{&i6=4_l+b~Dce&DmxDhy_ z2wa?ZY#nUf$@7jm$(SkWIIs?Lk3yN4T{ibF+Gj1U_SfV%LI>BDTphr-apT5N91$I^ zU%xKgb5P#Bd$+l>=$@%udFlLT|0V9rT~E$&F!w2Jin1lQ4dxL#*yCW!&^&igSzq1y z+5FFb#Cphjt)#X@Y?}RF8T--oka2KrN$YFMSm$E@w&)kuPcXQ literal 0 HcmV?d00001 diff --git a/docs/static/img/logo.svg b/docs/static/img/logo.svg new file mode 100644 index 00000000..249f6888 --- /dev/null +++ b/docs/static/img/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file From e33adda6fb5f32ce433b7c4055e8c6b0809be670 Mon Sep 17 00:00:00 2001 From: Cole Goldsmith Date: Thu, 18 Sep 2025 16:33:16 -0500 Subject: [PATCH 02/20] start creating a new filters component --- frontend/components/knowledge-filter-list.tsx | 340 ++++++++++++++++++ frontend/components/navigation.tsx | 12 + frontend/package-lock.json | 9 + frontend/package.json | 1 + .../api/queries/useGetFiltersSearchQuery.ts | 63 ++++ frontend/src/app/globals.css | 2 +- frontend/tailwind.config.ts | 2 + 7 files changed, 428 insertions(+), 1 deletion(-) create mode 100644 frontend/components/knowledge-filter-list.tsx create mode 100644 frontend/src/app/api/queries/useGetFiltersSearchQuery.ts diff --git a/frontend/components/knowledge-filter-list.tsx b/frontend/components/knowledge-filter-list.tsx new file mode 100644 index 00000000..5815391a --- /dev/null +++ b/frontend/components/knowledge-filter-list.tsx @@ -0,0 +1,340 @@ +"use client"; + +import { useState, useEffect, useRef } from "react"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Card, CardContent } from "@/components/ui/card"; + +import { Label } from "@/components/ui/label"; +import { Textarea } from "@/components/ui/textarea"; +import { + ChevronDown, + Filter, + Search, + X, + Loader2, + Plus, + Save, +} from "lucide-react"; +import { cn } from "@/lib/utils"; +import { + useGetFiltersSearchQuery, + type KnowledgeFilter, +} from "@/src/app/api/queries/useGetFiltersSearchQuery"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; + +interface ParsedQueryData { + query: string; + filters: { + data_sources: string[]; + document_types: string[]; + owners: string[]; + }; + limit: number; + scoreThreshold: number; +} + +interface KnowledgeFilterListProps { + selectedFilter: KnowledgeFilter | null; + onFilterSelect: (filter: KnowledgeFilter | null) => void; +} + +export function KnowledgeFilterList({ + selectedFilter, + onFilterSelect, +}: KnowledgeFilterListProps) { + const [searchQuery, setSearchQuery] = useState(""); + const [showCreateModal, setShowCreateModal] = useState(false); + const [createName, setCreateName] = useState(""); + const [createDescription, setCreateDescription] = useState(""); + const [creating, setCreating] = useState(false); + + const { + data, + isFetching: loading, + refetch, + } = useGetFiltersSearchQuery(searchQuery, 20, { enabled: true }); + const filters: KnowledgeFilter[] = (data ?? []) as KnowledgeFilter[]; + + const deleteFilter = async (filterId: string, e: React.MouseEvent) => { + e.stopPropagation(); + + try { + const response = await fetch(`/api/knowledge-filter/${filterId}`, { + method: "DELETE", + }); + + if (response.ok) { + // If this was the selected filter, clear selection + if (selectedFilter?.id === filterId) { + onFilterSelect(null); + } + // Refresh list + refetch(); + } else { + console.error("Failed to delete knowledge filter"); + } + } catch (error) { + console.error("Error deleting knowledge filter:", error); + } + }; + + const handleFilterSelect = (filter: KnowledgeFilter) => { + onFilterSelect(filter); + }; + + const handleClearFilter = () => { + onFilterSelect(null); + }; + + const handleCreateNew = () => { + setShowCreateModal(true); + }; + + const handleCreateFilter = async () => { + if (!createName.trim()) return; + + setCreating(true); + try { + // Create a basic filter with wildcards (match everything by default) + const defaultFilterData = { + query: "", + filters: { + data_sources: ["*"], + document_types: ["*"], + owners: ["*"], + }, + limit: 10, + scoreThreshold: 0, + }; + + const response = await fetch("/api/knowledge-filter", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + name: createName.trim(), + description: createDescription.trim(), + queryData: JSON.stringify(defaultFilterData), + }), + }); + + const result = await response.json(); + if (response.ok && result.success) { + // Create the new filter object + const newFilter: KnowledgeFilter = { + id: result.filter.id, + name: createName.trim(), + description: createDescription.trim(), + query_data: JSON.stringify(defaultFilterData), + owner: result.filter.owner, + created_at: result.filter.created_at, + updated_at: result.filter.updated_at, + }; + + // Select the new filter + onFilterSelect(newFilter); + + // Close modal and reset form + setShowCreateModal(false); + setCreateName(""); + setCreateDescription(""); + // Refresh list to include newly created filter + refetch(); + } else { + console.error("Failed to create knowledge filter:", result.error); + } + } catch (error) { + console.error("Error creating knowledge filter:", error); + } finally { + setCreating(false); + } + }; + + const handleCancelCreate = () => { + setShowCreateModal(false); + setCreateName(""); + setCreateDescription(""); + }; + + const parseQueryData = (queryData: string): ParsedQueryData => { + return JSON.parse(queryData) as ParsedQueryData; + }; + + const getFilterSummary = (filter: KnowledgeFilter): string => { + try { + const parsed = JSON.parse(filter.query_data) as ParsedQueryData; + const parts = []; + + if (parsed.query) parts.push(`"${parsed.query}"`); + if (parsed.filters.data_sources.length > 0) + parts.push(`${parsed.filters.data_sources.length} sources`); + if (parsed.filters.document_types.length > 0) + parts.push(`${parsed.filters.document_types.length} types`); + if (parsed.filters.owners.length > 0) + parts.push(`${parsed.filters.owners.length} owners`); + + return parts.join(" • ") || "No filters"; + } catch { + return "Invalid filter"; + } + }; + + return ( + <> +
+
+
+ Knowledge Filters +
+ +
+ {loading ? ( +
+ + + Loading... + +
+ ) : filters.length === 0 ? ( +
+ {searchQuery ? "No filters found" : "No saved filters"} +
+ ) : ( + filters.map((filter) => ( +
handleFilterSelect(filter)} + className={cn( + "flex items-center gap-3 px-3 py-2 w-full rounded-lg hover:bg-accent hover:text-accent-foreground cursor-pointer group transition-colors", + selectedFilter?.id === filter.id && + "bg-accent text-accent-foreground" + )} + > +
+
+ +
+ {filter.name} +
+
+ {filter.description && ( +
+ {filter.description} +
+ )} +
+
+ {new Date(filter.created_at).toLocaleDateString(undefined, { + month: "short", + day: "numeric", + year: "numeric", + })} +
+ + {(() => { + const count = parseQueryData(filter.query_data).filters + .data_sources.length; + return `${count} ${count === 1 ? "source" : "sources"}`; + })()} + +
+
+ +
+ )) + )} +
+ {/* Create Filter Dialog */} + + + + Create New Knowledge Filter + + Save a reusable filter to quickly scope searches across your + knowledge base. + + +
+
+
+ + setCreateName(e.target.value)} + className="mt-1" + /> +
+
+ +