From 07b5830eed30e3b198be602585cafc784918d0d0 Mon Sep 17 00:00:00 2001 From: Konstantin Wohlwend Date: Tue, 14 Oct 2025 10:41:33 -0700 Subject: [PATCH 1/7] Test extra envvars --- .github/workflows/restart-dev-and-test.yaml | 3 +++ .../src/auto-migrations/auto-migration.tests.ts | 13 +++++++------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/.github/workflows/restart-dev-and-test.yaml b/.github/workflows/restart-dev-and-test.yaml index 58469200e..50bda5f82 100644 --- a/.github/workflows/restart-dev-and-test.yaml +++ b/.github/workflows/restart-dev-and-test.yaml @@ -17,6 +17,9 @@ env: jobs: restart-dev-and-test: runs-on: ubicloud-standard-8 + env: + STACK_DIRECT_DATABASE_CONNECTION_STRING: "postgres://postgres:PASSWORD-PLACEHOLDER--uqfEC1hmmv@localhost:5432/stackframe" + steps: - uses: actions/checkout@v3 diff --git a/apps/backend/src/auto-migrations/auto-migration.tests.ts b/apps/backend/src/auto-migrations/auto-migration.tests.ts index 386022b35..930a10fa5 100644 --- a/apps/backend/src/auto-migrations/auto-migration.tests.ts +++ b/apps/backend/src/auto-migrations/auto-migration.tests.ts @@ -14,8 +14,8 @@ const getTestDbURL = (testDbName: string) => { }; }; -const applySql = async (options: { sql: string | string[], fullDbURL: string }) => { - const sql = postgres(options.fullDbURL); +const applySql = async (options: { sql: string | string[], dbUrl: string }) => { + const sql = postgres(options.dbUrl); try { for (const query of Array.isArray(options.sql) ? options.sql : [options.sql]) { @@ -31,12 +31,12 @@ const setupTestDatabase = async () => { const randomSuffix = Math.random().toString(16).substring(2, 12); const testDbName = `${TEST_DB_PREFIX}_${randomSuffix}`; const dbURL = getTestDbURL(testDbName); - await applySql({ sql: `CREATE DATABASE ${testDbName}`, fullDbURL: dbURL.base }); + await applySql({ sql: `CREATE DATABASE ${testDbName}`, dbUrl: dbURL.base }); const prismaClient = new PrismaClient({ datasources: { db: { - url: dbURL.full, + url: `${dbURL.full}?connection_limit=1`, }, }, }); @@ -63,7 +63,7 @@ const teardownTestDatabase = async (prismaClient: PrismaClient, testDbName: stri `, `DROP DATABASE IF EXISTS ${testDbName}` ], - fullDbURL: dbURL.base + dbUrl: dbURL.base }); // Wait a bit to ensure connections are terminated @@ -239,7 +239,7 @@ import.meta.vitest?.test("applies migrations concurrently with 20 concurrent mig import.meta.vitest?.test("applies migration with a DB previously migrated with prisma", runTest(async ({ expect, prismaClient, dbURL }) => { - await applySql({ sql: examplePrismaBasedInitQueries, fullDbURL: dbURL.full }); + await applySql({ sql: examplePrismaBasedInitQueries, dbUrl: dbURL.full }); const result = await applyMigrations({ prismaClient, migrationFiles: examplePrismaBasedMigrationFiles, schema: 'public' }); expect(result.newlyAppliedMigrationNames).toEqual(['20250314215050_age']); @@ -302,6 +302,7 @@ import.meta.vitest?.test("applies migration while running an interactive transac expect(result[0].name).toBe('test_value'); }, { isolationLevel: undefined, + timeout: 15_000, }); })); From 0c720a37e5f6e951e9cc799603af4c4bf21f6d05 Mon Sep 17 00:00:00 2001 From: Konstantin Wohlwend Date: Tue, 14 Oct 2025 11:08:17 -0700 Subject: [PATCH 2/7] Fix tests (hopefully) --- apps/backend/package.json | 2 +- .../auto-migrations/auto-migration.tests.ts | 19 +++++++++++++++---- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/apps/backend/package.json b/apps/backend/package.json index 10337e386..816d60d7c 100644 --- a/apps/backend/package.json +++ b/apps/backend/package.json @@ -19,7 +19,7 @@ "codegen-route-info:watch": "pnpm run with-env tsx watch --clear-screen=false scripts/generate-route-info.ts", "codegen": "pnpm run with-env pnpm run generate-migration-imports && pnpm run with-env bash -c 'if [ \"$STACK_ACCELERATE_ENABLED\" = \"true\" ]; then pnpm run prisma generate --no-engine; else pnpm run codegen-prisma; fi' && pnpm run codegen-route-info", "codegen:watch": "concurrently -n \"prisma,docs,route-info,migration-imports\" -k \"pnpm run codegen-prisma:watch\" \"pnpm run watch-docs\" \"pnpm run codegen-route-info:watch\" \"pnpm run generate-migration-imports:watch\"", - "psql-inner": "psql $STACK_DATABASE_CONNECTION_STRING", + "psql-inner": "psql $(echo $STACK_DATABASE_CONNECTION_STRING | sed 's/\\?.*$//')", "psql": "pnpm run with-env pnpm run psql-inner", "prisma-studio": "pnpm run with-env prisma studio --port 8106 --browser none", "prisma": "pnpm run with-env prisma", diff --git a/apps/backend/src/auto-migrations/auto-migration.tests.ts b/apps/backend/src/auto-migrations/auto-migration.tests.ts index 930a10fa5..03d9d9057 100644 --- a/apps/backend/src/auto-migrations/auto-migration.tests.ts +++ b/apps/backend/src/auto-migrations/auto-migration.tests.ts @@ -8,9 +8,12 @@ const TEST_DB_PREFIX = 'stack_auth_test_db'; const getTestDbURL = (testDbName: string) => { // @ts-ignore - ImportMeta.env is provided by Vite const base = import.meta.env.STACK_DIRECT_DATABASE_CONNECTION_STRING.replace(/\/[^/]*$/, ''); + // @ts-ignore - ImportMeta.env is provided by Vite + const query = import.meta.env.STACK_DIRECT_DATABASE_CONNECTION_STRING.split('?')[1] ?? ''; return { full: `${base}/${testDbName}`, base, + query, }; }; @@ -36,7 +39,7 @@ const setupTestDatabase = async () => { const prismaClient = new PrismaClient({ datasources: { db: { - url: `${dbURL.full}?connection_limit=1`, + url: `${dbURL.full}?${dbURL.query}`, }, }, }); @@ -211,8 +214,8 @@ import.meta.vitest?.test("applies migrations concurrently", runTest(async ({ exp expect(result[0].name).toBe('test_value'); })); -import.meta.vitest?.test("applies migrations concurrently with 20 concurrent migrations", runTest(async ({ expect, prismaClient }) => { - const promises = Array.from({ length: 20 }, async (_, i) => { +import.meta.vitest?.test("applies migrations concurrently with 200 concurrent migrations", runTest(async ({ expect, prismaClient }) => { + const promises = Array.from({ length: 200 }, async (_, i) => { console.log("Applying migration", i); const result = await applyMigrations({ prismaClient, migrationFiles: exampleMigrationFiles1, artificialDelayInSeconds: 1, schema: 'public', logging: true }); console.log("Migration", i, "applied", result.newlyAppliedMigrationNames); @@ -234,9 +237,17 @@ import.meta.vitest?.test("applies migrations concurrently with 20 concurrent mig expect(result.length).toBe(1); expect(result[0].name).toBe('test_value'); }), { - timeout: 40_000, + timeout: 400_000, }); +// TODO: this test, or a variant of it, might fail because the migrations waiting for locks are exhausting all +// connections; the RUN_OUTSIDE_TRANSACTION_SENTINEL is then not able to run its own migration outside of the +// transaction +// +// The fix would be to only *try* acquiring the migration lock when we apply a migration, and if it fails to acquire, we +// wait *outside* of the transaction so it doesn't exhaust all connections +import.meta.vitest?.test.todo("applies migrations concurrently with 200 concurrent migrations with RUN_OUTSIDE_TRANSACTION_SENTINEL"); + import.meta.vitest?.test("applies migration with a DB previously migrated with prisma", runTest(async ({ expect, prismaClient, dbURL }) => { await applySql({ sql: examplePrismaBasedInitQueries, dbUrl: dbURL.full }); From 20874486cb98c5ddc41c2031ab777dd3890bde69 Mon Sep 17 00:00:00 2001 From: Konstantin Wohlwend Date: Tue, 14 Oct 2025 11:09:40 -0700 Subject: [PATCH 3/7] Fix tests --- apps/backend/src/auto-migrations/auto-migration.tests.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/backend/src/auto-migrations/auto-migration.tests.ts b/apps/backend/src/auto-migrations/auto-migration.tests.ts index 03d9d9057..305692fb3 100644 --- a/apps/backend/src/auto-migrations/auto-migration.tests.ts +++ b/apps/backend/src/auto-migrations/auto-migration.tests.ts @@ -214,8 +214,8 @@ import.meta.vitest?.test("applies migrations concurrently", runTest(async ({ exp expect(result[0].name).toBe('test_value'); })); -import.meta.vitest?.test("applies migrations concurrently with 200 concurrent migrations", runTest(async ({ expect, prismaClient }) => { - const promises = Array.from({ length: 200 }, async (_, i) => { +import.meta.vitest?.test("applies migrations concurrently with 20 concurrent migrations", runTest(async ({ expect, prismaClient }) => { + const promises = Array.from({ length: 20 }, async (_, i) => { console.log("Applying migration", i); const result = await applyMigrations({ prismaClient, migrationFiles: exampleMigrationFiles1, artificialDelayInSeconds: 1, schema: 'public', logging: true }); console.log("Migration", i, "applied", result.newlyAppliedMigrationNames); @@ -246,7 +246,7 @@ import.meta.vitest?.test("applies migrations concurrently with 200 concurrent mi // // The fix would be to only *try* acquiring the migration lock when we apply a migration, and if it fails to acquire, we // wait *outside* of the transaction so it doesn't exhaust all connections -import.meta.vitest?.test.todo("applies migrations concurrently with 200 concurrent migrations with RUN_OUTSIDE_TRANSACTION_SENTINEL"); +import.meta.vitest?.test.todo("applies migrations concurrently with 20 concurrent migrations with RUN_OUTSIDE_TRANSACTION_SENTINEL"); import.meta.vitest?.test("applies migration with a DB previously migrated with prisma", runTest(async ({ expect, prismaClient, dbURL }) => { From 1ed9c6150ff92024a3fd7f9451650d07aa48697f Mon Sep 17 00:00:00 2001 From: Konstantin Wohlwend Date: Tue, 14 Oct 2025 11:29:41 -0700 Subject: [PATCH 4/7] Use custom migration script for self-hosting container --- apps/backend/package.json | 2 +- .../db-migrations.tsup.config.ts} | 4 ++-- docker/server/Dockerfile | 10 +++++----- docker/server/entrypoint.sh | 6 ++++-- 4 files changed, 12 insertions(+), 10 deletions(-) rename apps/backend/{prisma/tsup.config.ts => scripts/db-migrations.tsup.config.ts} (82%) diff --git a/apps/backend/package.json b/apps/backend/package.json index 816d60d7c..0de94a60e 100644 --- a/apps/backend/package.json +++ b/apps/backend/package.json @@ -10,7 +10,7 @@ "dev": "concurrently -n \"dev,codegen,prisma-studio\" -k \"next dev --turbopack --port 8102\" \"pnpm run codegen:watch\" \"pnpm run prisma-studio\"", "build": "pnpm run codegen && next build", "docker-build": "pnpm run codegen && next build --experimental-build-mode compile", - "build-self-host-seed-script": "tsup --config prisma/tsup.config.ts", + "build-self-host-migration-script": "tsup --config scripts/db-migrations.tsup.config.ts", "analyze-bundle": "ANALYZE_BUNDLE=1 pnpm run build", "start": "next start --port 8102", "codegen-prisma": "pnpm run prisma generate", diff --git a/apps/backend/prisma/tsup.config.ts b/apps/backend/scripts/db-migrations.tsup.config.ts similarity index 82% rename from apps/backend/prisma/tsup.config.ts rename to apps/backend/scripts/db-migrations.tsup.config.ts index 4845065a1..c3c014a10 100644 --- a/apps/backend/prisma/tsup.config.ts +++ b/apps/backend/scripts/db-migrations.tsup.config.ts @@ -6,10 +6,10 @@ const customNoExternal = new Set([ ...Object.keys(packageJson.dependencies), ]); -// tsup config to build the self-hosting seed script so it can be +// tsup config to build the self-hosting migration script so it can be // run in the Docker container with no extra dependencies. export default defineConfig({ - entry: ['prisma/seed.ts'], + entry: ['scripts/db-migrations.ts'], format: ['cjs'], outDir: 'dist', target: 'node22', diff --git a/docker/server/Dockerfile b/docker/server/Dockerfile index 0a2d31713..f95d0f39f 100644 --- a/docker/server/Dockerfile +++ b/docker/server/Dockerfile @@ -29,6 +29,7 @@ RUN tsx ./scripts/generate-sdks.ts RUN turbo prune --scope=@stackframe/stack-backend --scope=@stackframe/stack-dashboard --docker + # Build stage FROM base AS builder @@ -55,7 +56,9 @@ ENV NEXT_CONFIG_OUTPUT=standalone RUN pnpm turbo run docker-build --filter=@stackframe/stack-backend... --filter=@stackframe/stack-dashboard... # Build the self-host seed script -RUN cd apps/backend && pnpm build-self-host-seed-script +RUN cd apps/backend && pnpm build-self-host-migration-script + + # Final image FROM node:${NODE_VERSION}-slim @@ -68,14 +71,11 @@ RUN apt-get update && \ apt-get install -y openssl socat && \ rm -rf /var/lib/apt/lists -# Install Prisma CLI globally so we can run migrations on startup -RUN npm i -g prisma - # Copy built backend COPY --from=builder --chown=node:node /app/apps/backend/.next/standalone ./ COPY --from=builder --chown=node:node /app/apps/backend/.next/static ./apps/backend/.next/static COPY --from=builder --chown=node:node /app/apps/backend/prisma ./apps/backend/prisma -COPY --from=builder --chown=node:node /app/apps/backend/dist/seed.js ./apps/backend +COPY --from=builder --chown=node:node /app/apps/backend/dist/db-migrations.js ./apps/backend # Copy built dashboard COPY --from=builder --chown=node:node /app/apps/dashboard/.next/standalone ./ diff --git a/docker/server/entrypoint.sh b/docker/server/entrypoint.sh index d2b9a7bc1..b94e732a9 100644 --- a/docker/server/entrypoint.sh +++ b/docker/server/entrypoint.sh @@ -37,7 +37,9 @@ if [ "$STACK_SKIP_MIGRATIONS" = "true" ]; then echo "Skipping migrations." else echo "Running migrations..." - pnpm run db:migrate + cd apps/backend + node db-migrations.js migrate + cd ../.. fi if [ "$STACK_SKIP_SEED_SCRIPT" = "true" ]; then @@ -45,7 +47,7 @@ if [ "$STACK_SKIP_SEED_SCRIPT" = "true" ]; then else echo "Running seed script..." cd apps/backend - node seed.js + node db-migrations.js seed cd ../.. fi From cd021134418552f79773f4b849aa7b6ff585f72e Mon Sep 17 00:00:00 2001 From: Konstantin Wohlwend Date: Tue, 14 Oct 2025 12:23:22 -0700 Subject: [PATCH 5/7] several changes --- apps/backend/package.json | 2 +- apps/backend/prisma/seed.ts | 16 +++++++++------- apps/backend/scripts/db-migrations.ts | 5 +---- docker/server/Dockerfile | 2 +- docker/server/entrypoint.sh | 4 ++-- 5 files changed, 14 insertions(+), 15 deletions(-) diff --git a/apps/backend/package.json b/apps/backend/package.json index 0de94a60e..1b4dd4b44 100644 --- a/apps/backend/package.json +++ b/apps/backend/package.json @@ -34,7 +34,7 @@ "watch-docs": "pnpm run with-env bash -c 'tsx watch --clear-screen=false scripts/generate-openapi-fumadocs.ts && pnpm run --filter=@stackframe/stack-docs generate-openapi-docs'", "generate-openapi-fumadocs": "pnpm run with-env tsx scripts/generate-openapi-fumadocs.ts", "generate-keys": "pnpm run with-env tsx scripts/generate-keys.ts", - "db-seed-script": "pnpm run with-env tsx prisma/seed.ts", + "db-seed-script": "pnpm run db:seed", "verify-data-integrity": "pnpm run with-env tsx scripts/verify-data-integrity.ts" }, "prisma": { diff --git a/apps/backend/prisma/seed.ts b/apps/backend/prisma/seed.ts index 45aea975a..84971f82c 100644 --- a/apps/backend/prisma/seed.ts +++ b/apps/backend/prisma/seed.ts @@ -10,7 +10,7 @@ import { errorToNiceString, throwErr } from '@stackframe/stack-shared/dist/utils const globalPrisma = new PrismaClient(); -async function seed() { +export async function seed() { console.log('Seeding database...'); // Optional default admin user @@ -473,9 +473,11 @@ async function seed() { process.env.STACK_SEED_MODE = 'true'; -seed().catch(async (e) => { - console.error(errorToNiceString(e)); - await globalPrisma.$disconnect(); - process.exit(1); - // eslint-disable-next-line @typescript-eslint/no-misused-promises -}).finally(async () => await globalPrisma.$disconnect()); +if (require.main === module) { + seed().catch(async (e) => { + console.error(errorToNiceString(e)); + await globalPrisma.$disconnect(); + process.exit(1); + // eslint-disable-next-line @typescript-eslint/no-misused-promises + }).finally(async () => await globalPrisma.$disconnect()); +} diff --git a/apps/backend/scripts/db-migrations.ts b/apps/backend/scripts/db-migrations.ts index 461cfdc46..bcab4e27b 100644 --- a/apps/backend/scripts/db-migrations.ts +++ b/apps/backend/scripts/db-migrations.ts @@ -4,6 +4,7 @@ import { globalPrismaClient, globalPrismaSchema, sqlQuoteIdent } from "@/prisma- import { Prisma } from "@prisma/client"; import { execSync } from "child_process"; import * as readline from 'readline'; +import { seed } from "../prisma/seed"; const dropSchema = async () => { await globalPrismaClient.$executeRaw(Prisma.sql`DROP SCHEMA ${sqlQuoteIdent(globalPrismaSchema)} CASCADE`); @@ -12,10 +13,6 @@ const dropSchema = async () => { await globalPrismaClient.$executeRaw(Prisma.sql`GRANT ALL ON SCHEMA ${sqlQuoteIdent(globalPrismaSchema)} TO public`); }; -const seed = async () => { - execSync('pnpm run db-seed-script', { stdio: 'inherit' }); -}; - const promptDropDb = async () => { const rl = readline.createInterface({ input: process.stdin, diff --git a/docker/server/Dockerfile b/docker/server/Dockerfile index f95d0f39f..6867bfe98 100644 --- a/docker/server/Dockerfile +++ b/docker/server/Dockerfile @@ -75,7 +75,7 @@ RUN apt-get update && \ COPY --from=builder --chown=node:node /app/apps/backend/.next/standalone ./ COPY --from=builder --chown=node:node /app/apps/backend/.next/static ./apps/backend/.next/static COPY --from=builder --chown=node:node /app/apps/backend/prisma ./apps/backend/prisma -COPY --from=builder --chown=node:node /app/apps/backend/dist/db-migrations.js ./apps/backend +COPY --from=builder --chown=node:node /app/apps/backend/dist ./apps/backend # Copy built dashboard COPY --from=builder --chown=node:node /app/apps/dashboard/.next/standalone ./ diff --git a/docker/server/entrypoint.sh b/docker/server/entrypoint.sh index b94e732a9..1ae2924cb 100644 --- a/docker/server/entrypoint.sh +++ b/docker/server/entrypoint.sh @@ -38,7 +38,7 @@ if [ "$STACK_SKIP_MIGRATIONS" = "true" ]; then else echo "Running migrations..." cd apps/backend - node db-migrations.js migrate + node dist/db-migrations.js migrate cd ../.. fi @@ -47,7 +47,7 @@ if [ "$STACK_SKIP_SEED_SCRIPT" = "true" ]; then else echo "Running seed script..." cd apps/backend - node db-migrations.js seed + node dist/db-migrations.js seed cd ../.. fi From b76fcb1282c5986de6f6c394265c0a1533d3c4ee Mon Sep 17 00:00:00 2001 From: Konstantin Wohlwend Date: Tue, 14 Oct 2025 12:41:35 -0700 Subject: [PATCH 6/7] fixes --- docker/server/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/server/Dockerfile b/docker/server/Dockerfile index 6867bfe98..c008a89f8 100644 --- a/docker/server/Dockerfile +++ b/docker/server/Dockerfile @@ -75,7 +75,7 @@ RUN apt-get update && \ COPY --from=builder --chown=node:node /app/apps/backend/.next/standalone ./ COPY --from=builder --chown=node:node /app/apps/backend/.next/static ./apps/backend/.next/static COPY --from=builder --chown=node:node /app/apps/backend/prisma ./apps/backend/prisma -COPY --from=builder --chown=node:node /app/apps/backend/dist ./apps/backend +COPY --from=builder --chown=node:node /app/apps/backend/dist ./apps/backend/dist # Copy built dashboard COPY --from=builder --chown=node:node /app/apps/dashboard/.next/standalone ./ From 6b08d8bfcd53d73f4aa6af7844367793e33a8eb5 Mon Sep 17 00:00:00 2001 From: Madison Date: Tue, 14 Oct 2025 15:03:56 -0500 Subject: [PATCH 7/7] update AI button w/ text (#947) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enhances AI chat button, and adds it to search as well. image image ## High-level PR Summary This PR enhances the AI chat button across the documentation site by adding text labels alongside the sparkles icon, making it more discoverable. The button now displays "AI Chat" text and has been updated consistently across the home layout and shared header. Additionally, an "Ask AI" fallback button has been added to the search dialog footer to help users who can't find what they're looking for through regular search. ⏱️ Estimated Review Time: 15-30 minutes
💡 Review Order Suggestion | Order | File Path | |-------|-----------| | 1 | `docs/src/components/layouts/home-layout.tsx` | | 2 | `docs/src/components/layouts/shared-header.tsx` | | 3 | `docs/src/components/layout/custom-search-dialog.tsx` |
[![Need help? Join our Discord](https://img.shields.io/badge/Need%20help%3F%20Join%20our%20Discord-5865F2?style=plastic&logo=discord&logoColor=white)](https://discord.gg/n3SsVDAW6U) [![Analyze latest changes](https://img.shields.io/badge/Analyze%20latest%20changes-238636?style=plastic)](https://squash-322339097191.europe-west3.run.app/interactive/ba7372824c36c80c2bff14f2602c6e7f03e00e9ed4fed3db9e9b1250f0839610/?repo_owner=stack-auth&repo_name=stack-auth&pr_number=947) ---- > [!IMPORTANT] > Enhances AI chat button with text labels and adds an "Ask AI" fallback button to the search dialog, improving discoverability and user experience. > > - **AI Chat Button Enhancements**: > - Added text label "AI Chat" to the button in `home-layout.tsx` and `shared-header.tsx`. > - Introduced compact icon-only variant for tighter spaces in `home-layout.tsx`. > - Updated styling for better visibility and state-awareness when chat is open. > - **Search Dialog**: > - Added "Ask AI" fallback button in the footer of `custom-search-dialog.tsx` to open AI chat if search results are insufficient. > - **Styling Adjustments**: > - Adjusted header spacing and layout for better alignment in `shared-header.tsx`. > - Refined footer structure in `custom-search-dialog.tsx` to accommodate AI fallback. > > This description was created by [Ellipsis](https://www.ellipsis.dev?ref=stack-auth%2Fstack-auth&utm_source=github&utm_medium=referral) for 6e13d5a894d6a7bfbd36d84e0833caf7bb75fae9. You can [customize](https://app.ellipsis.dev/stack-auth/settings/summaries) this summary. It will automatically update as commits are pushed. ---- ## Summary by CodeRabbit * **New Features** * Added an “Ask AI” option in the search dialog footer that opens AI chat as a fallback. * Introduced a compact icon-only variant of the AI Chat toggle for tighter spaces. * **Style** * Updated AI Chat controls with an inline label, smaller icon, and state-aware styling when chat is open. * Adjusted header spacing and layout for better alignment and readability. * Refined search dialog footer structure to accommodate the AI fallback row. --------- Co-authored-by: Konsti Wohlwend --- .../layout/custom-search-dialog.tsx | 55 +++++++++++++++---- docs/src/components/layouts/home-layout.tsx | 21 +++++-- docs/src/components/layouts/shared-header.tsx | 15 +++-- 3 files changed, 70 insertions(+), 21 deletions(-) diff --git a/docs/src/components/layout/custom-search-dialog.tsx b/docs/src/components/layout/custom-search-dialog.tsx index 8d3ad7aa4..2e4ca3d60 100644 --- a/docs/src/components/layout/custom-search-dialog.tsx +++ b/docs/src/components/layout/custom-search-dialog.tsx @@ -1,9 +1,10 @@ 'use client'; -import { AlignLeft, ChevronDown, ExternalLink, FileText, Hash, Search, X } from 'lucide-react'; +import { AlignLeft, ChevronDown, ExternalLink, FileText, Hash, Search, Sparkles, X } from 'lucide-react'; import Link from 'next/link'; import { useCallback, useEffect, useRef, useState } from 'react'; import { cn } from '../../lib/cn'; +import { useSidebar } from '../layouts/sidebar-context'; // Platform colors matching your theme const PLATFORM_COLORS = { @@ -137,10 +138,28 @@ export function CustomSearchDialog({ open, onOpenChange }: CustomSearchDialogPro const dropdownRef = useRef(null); const searchTimeoutRef = useRef(); + const sidebarContext = useSidebar(); // Available platforms for the dropdown const availablePlatforms = ['all', 'next', 'react', 'js', 'python', 'api']; + // Handle AI chat opening + const handleOpenAIChat = () => { + onOpenChange(false); // Close search dialog first + if (!sidebarContext) { + return; + } + + const { toggleChat } = sidebarContext; + + // Small delay to ensure search dialog closes smoothly + setTimeout(() => { + if (!sidebarContext.isChatOpen) { + toggleChat(); + } + }, 100); + }; + // Close dropdown when clicking outside useEffect(() => { const handleClickOutside = (event: MouseEvent) => { @@ -436,16 +455,30 @@ export function CustomSearchDialog({ open, onOpenChange }: CustomSearchDialogPro {/* Footer */} -
- Use ↑↓ to navigate, Enter to select, Esc to close - - {filteredResults.length} result group{filteredResults.length !== 1 ? 's' : ''} - {selectedPlatformFilter !== 'all' && filteredResults.length > 0 && ( - - • {PLATFORM_NAMES[selectedPlatformFilter as keyof typeof PLATFORM_NAMES]} only - - )} - +
+
+ Use ↑↓ to navigate, Enter to select, Esc to close + + {filteredResults.length} result group{filteredResults.length !== 1 ? 's' : ''} + {selectedPlatformFilter !== 'all' && filteredResults.length > 0 && ( + + • {PLATFORM_NAMES[selectedPlatformFilter as keyof typeof PLATFORM_NAMES]} only + + )} + +
+ + {/* AI Chat Fallback */} +
+ Can't find what you're looking for? + +
diff --git a/docs/src/components/layouts/home-layout.tsx b/docs/src/components/layouts/home-layout.tsx index 3de8c5941..4f4772960 100644 --- a/docs/src/components/layouts/home-layout.tsx +++ b/docs/src/components/layouts/home-layout.tsx @@ -45,20 +45,33 @@ function StackAuthLogo() { } // AI Chat Toggle Button for Home Layout -function HomeAIChatToggleButton() { +function HomeAIChatToggleButton({ compact = false }: { compact?: boolean }) { const sidebarContext = useSidebar(); const { isChatOpen, toggleChat } = sidebarContext || { isChatOpen: false, toggleChat: () => {}, }; + if (compact) { + return ( + + ); + } + return ( ); } @@ -248,7 +261,7 @@ function HomeNavbar() { {/* Compact AI Chat Toggle */} - + {/* Compact User Button */} diff --git a/docs/src/components/layouts/shared-header.tsx b/docs/src/components/layouts/shared-header.tsx index 082a3b56d..6f0d7d777 100644 --- a/docs/src/components/layouts/shared-header.tsx +++ b/docs/src/components/layouts/shared-header.tsx @@ -81,18 +81,21 @@ function AIChatToggleButton() { return null; } - const { toggleChat } = sidebarContext; + const { toggleChat, isChatOpen } = sidebarContext; return ( ); } @@ -312,7 +315,7 @@ export function SharedHeader({ {/* Right side - Mobile Menu and Search */} -
+
{/* Search Bar - Responsive sizing */} {showSearch && ( <> @@ -344,7 +347,7 @@ export function SharedHeader({
{/* User Button */} -
+