Merge pull request #867 from buster-so/big-nate-bus-1710-make-suggested-questions-dropdown

Big nate bus 1710 make suggested questions dropdown
This commit is contained in:
Nate Kelley 2025-09-11 17:31:40 -06:00 committed by GitHub
commit a19da883ab
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 460 additions and 58 deletions

1
apps/web/.gitignore vendored
View File

@ -18,3 +18,4 @@ storybook-static
.env.staging
.env.dev
.env.local
.vercel

View File

@ -10,6 +10,7 @@
"build:production": "cross-env NODE_OPTIONS=--max-old-space-size=8192 vite build --mode production -- --typecheck",
"build:local": "pnpm prebuild && cross-env NODE_OPTIONS=--max-old-space-size=12288 vite build -- --typecheck --local",
"build-storybook": "storybook build",
"log:staging": "WRANGLER_LOG=debug npx wrangler tail --format=pretty --env=staging",
"build:visualize": "npx vite-bundle-visualizer",
"deploy:dev": "pnpm run build && npx wrangler deploy .output/server/index.mjs --env dev --assets .output/public",
"deploy:production": "pnpm run build:production && npx wrangler deploy .output/server/index.mjs --env production --assets .output/public",
@ -42,7 +43,6 @@
"@electric-sql/react": "^1.0.10",
"@emoji-mart/data": "^1.2.1",
"@faker-js/faker": "^10.0.0",
"@modelcontextprotocol/sdk": "^1.17.5",
"@monaco-editor/react": "^4.7.0",
"@platejs/ai": "^50.1.2",
"@platejs/autoformat": "^49.0.0",
@ -161,6 +161,7 @@
"react-hotkeys-hook": "^5.1.0",
"react-lite-youtube-embed": "^2.5.6",
"react-markdown": "^10.1.0",
"react-mentions": "^4.4.10",
"react-player": "^3.3.2",
"react-textarea-autosize": "^8.5.9",
"remark-gfm": "^4.0.1",
@ -199,6 +200,7 @@
"@types/pluralize": "^0.0.33",
"@types/react": "catalog:",
"@types/react-dom": "catalog:",
"@types/react-mentions": "^4.4.1",
"@vitejs/plugin-react": "^5.0.2",
"@vitest/browser": "3.2.4",
"@vitest/coverage-v8": "3.2.4",

View File

@ -0,0 +1,25 @@
import type { Meta, StoryObj } from '@storybook/react-vite';
import { BusterInput } from './BusterInput';
const meta: Meta<typeof BusterInput> = {
title: 'UI/Inputs/BusterInput',
component: BusterInput,
tags: ['autodocs'],
args: {},
decorators: [
(Story) => (
<div className="p-4">
<Story />
</div>
),
],
};
export default meta;
type Story = StoryObj<typeof BusterInput>;
export const Default: Story = {
args: {
value: 'Sample text value',
},
};

View File

@ -0,0 +1,127 @@
import { Command } from 'cmdk';
import type React from 'react';
import { useState } from 'react';
import { Mention, MentionsInput } from 'react-mentions';
import { cn } from '@/lib/utils';
import type { BusterInputProps } from './BusterInput.types';
import { BusterInputEmpty } from './BusterInputEmpty';
import { BusterInputSeparator } from './BusterInputSeparator';
import { DEFAULT_MENTION_MARKUP } from './parse-input';
const users = [
{ id: '1', display: 'BigNate' },
{ id: '2', display: 'ReactFan42' },
{ id: '3', display: 'NextJSDev' },
];
export const BusterInput = ({ defaultValue, value: valueProp, onChange }: BusterInputProps) => {
const [value, setValue] = useState(valueProp ?? defaultValue);
return (
<div className="flex flex-col gap-2">
<MentionsInput
value={value}
onChange={(e) => {
setValue(e.target.value);
console.log(e.target.value);
}}
placeholder="Type @ to mention…"
style={{
control: { fontSize: 16, minHeight: 46 },
highlighter: { padding: 8 },
input: { padding: 8 },
}}
classNames={{
highlighter: 'bg-red-500/10',
suggestions: 'bg-blue-500/20 border',
item: 'text-red-500',
}}
>
{/* Always render a valid Mention node */}
<Mention
trigger="@"
markup={DEFAULT_MENTION_MARKUP}
data={users}
displayTransform={(_, display) => `@${display}`}
appendSpaceOnAdd
renderSuggestion={(d) => {
return d.display;
}}
/>
</MentionsInput>
<div className="w-full h-px bg-border" />
<Command
label="Command Menu"
onValueChange={(e) => {
console.log(e);
}}
>
<Command.Input
className="w-full outline-1 outline-amber-600"
value={value}
onValueChange={setValue}
asChild
autoFocus
readOnly
placeholder="Type @ to mention…"
>
<textarea />
</Command.Input>
<Command.List>
<BusterInputEmpty>No results found.</BusterInputEmpty>
<CommandGroup heading="Letters">
<CommandItem
onSelect={() => {
setValue('a');
}}
>
a
</CommandItem>
<CommandItem>b</CommandItem>
<BusterInputSeparator />
<CommandItem>c</CommandItem>
</CommandGroup>
<CommandItem>Apple</CommandItem>
</Command.List>
</Command>
</div>
);
};
const CommandGroup = ({
children,
...props
}: React.ComponentPropsWithoutRef<typeof Command.Group>) => {
return (
<Command.Group
className={cn(
'text-foreground overflow-hidden p-1 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium',
props.className
)}
{...props}
>
{children}
</Command.Group>
);
};
const CommandItem = ({
children,
...props
}: React.ComponentPropsWithoutRef<typeof Command.Item>) => {
return (
<Command.Item
className={cn(
'data-[selected=true]:bg-item-hover data-[selected=true]:text-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-base outline-none select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
props.className
)}
{...props}
>
{children}
</Command.Item>
);
};

View File

@ -0,0 +1,54 @@
import type React from 'react';
import type { DisplayTransformFunc } from 'react-mentions';
export type BusterInputDropdownItem<T = string> = {
value: T;
label: string | React.ReactNode;
shortcut?: string;
icon?: React.ReactNode;
onClick?: () => void;
disabled?: boolean;
loading?: boolean;
closeOnSelect?: boolean; //defaults to parent
};
export type BusterInputDropdownGroup<T = string> = {
label: string | React.ReactNode;
items: BusterInputDropdownItem<T>[];
};
export type BusterMentionItem<V = string> = {
value: V;
parsedValue?: string; //if this is undefined, the value will be used
label: string | React.ReactNode;
selected?: boolean;
};
export type BusterMentionItems<V = string, T = string> = {
items: BusterMentionItem<V>[];
displayTransform?: DisplayTransformFunc;
style?: React.CSSProperties;
appendSpaceOnAdd?: boolean; //defaults to true
trigger: T;
popoverContent?: (v: BusterMentionItem<V>) => React.ReactNode;
};
export type BusterMentionRecords<V = string, T extends string = string> = {
[K in T]: BusterMentionItems<V, T>;
};
export type BusterInputProps<T = string> = {
defaultValue: string;
value?: string;
onChange?: (value: string) => void;
submitting?: boolean;
onSubmit: (value: string) => void;
onStop: () => void;
onItemClick?: (value: string) => void;
items: (BusterInputDropdownItem<T> | BusterInputDropdownGroup<T>)[];
mentions?: BusterMentionRecords<T>;
variant?: 'default';
sendIcon?: React.ReactNode;
secondaryActions?: React.ReactNode;
closeOnSelect?: boolean; //defaults to true
};

View File

@ -0,0 +1,17 @@
import { Command } from 'cmdk';
import type React from 'react';
import { cn } from '@/lib/utils';
export const BusterInputEmpty = ({
children,
...props
}: React.ComponentPropsWithoutRef<typeof Command.Empty>) => {
return (
<Command.Empty
className={cn('text-gray-light py-6 text-center text-base', props.className)}
{...props}
>
{children}
</Command.Empty>
);
};

View File

@ -0,0 +1,14 @@
import { Command } from 'cmdk';
import type React from 'react';
import { cn } from '@/lib/utils';
export const BusterInputSeparator = ({
children,
...props
}: React.ComponentPropsWithoutRef<typeof Command.Separator>) => {
return (
<Command.Separator className={cn('bg-border -mx-1 h-px', props.className)} {...props}>
{children}
</Command.Separator>
);
};

View File

@ -0,0 +1,80 @@
import { describe, expect, it } from 'vitest';
import type { BusterMentionItem } from './BusterInput.types';
import { parseMarkupInput } from './parse-input';
describe('parseMarkupInput', () => {
const mockItems: BusterMentionItem<string>[] = [
{
value: '1',
parsedValue: 'BigNate User',
label: 'BigNate',
},
{
value: '2',
label: 'ReactFan42',
// No parsedValue - should use value instead
},
{
value: '3',
parsedValue: 'Next.js Developer',
label: 'NextJSDev',
},
];
const mockItemsRecord = {
'@': {
items: mockItems,
trigger: '@' as const,
},
};
it('should replace mention markup with parsedValue when available', () => {
const input = 'Hello @[BigNate](1) how are you?';
const result = parseMarkupInput({
input,
items: mockItemsRecord,
});
expect(result).toBe('Hello BigNate User how are you?');
});
it('should replace mention markup with value when parsedValue is undefined', () => {
const input = 'Hey @[ReactFan42](2) welcome!';
const result = parseMarkupInput({
input,
items: mockItemsRecord,
});
expect(result).toBe('Hey 2 welcome!');
});
it('should handle multiple mentions in the same string', () => {
const input = 'Meeting with @[BigNate](1) and @[NextJSDev](3) at 3pm';
const result = parseMarkupInput({
input,
items: mockItemsRecord,
});
expect(result).toBe('Meeting with BigNate User and Next.js Developer at 3pm');
});
it('should return original text when no mentions are found', () => {
const input = 'This is just regular text without any mentions';
const result = parseMarkupInput({
input,
items: mockItemsRecord,
});
expect(result).toBe('This is just regular text without any mentions');
});
it('should handle mentions with unknown IDs by leaving them unchanged', () => {
const input = 'Hello @[UnknownUser](999) how are you?';
const result = parseMarkupInput({
input,
items: mockItemsRecord,
});
expect(result).toBe('Hello @[UnknownUser](999) how are you?');
});
});

View File

@ -0,0 +1,33 @@
import type { BusterMentionItems } from './BusterInput.types';
export const DEFAULT_MENTION_MARKUP = '@[__display__](__id__)';
const DEFAULT_MENTION_REGEX = /@\[([^\]]+)\]\(([^)]+)\)/g;
type BusterMentionItemsRecord<V = string, T extends string = string> = Record<
T,
Pick<BusterMentionItems<V, T>, 'items' | 'trigger'>
>;
export const parseMarkupInput = <V = string, T extends string = string>({
input,
items,
}: {
input: string;
items: BusterMentionItemsRecord<V, T>;
}): string => {
return input.replace(DEFAULT_MENTION_REGEX, (match, _display, id) => {
for (const mentionType of Object.values(items) as Pick<
BusterMentionItems<V, T>,
'items' | 'trigger'
>[]) {
const item = mentionType.items.find((item) => String(item.value) === String(id));
if (item) {
// Use parsedValue if available, otherwise use value
return item.parsedValue ?? String(item.value);
}
}
// If no matching item found in any mention type, return the original mention
return match;
});
};

View File

@ -28,8 +28,8 @@ export const createCspHeader = (isEmbed = false): string => {
return [
// Default directives
"default-src 'self'",
// Scripts
"script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.jsdelivr.net https://*.cloudflareinsights.com https://*.posthog.com https://us-assets.i.posthog.com https://eu-assets.i.posthog.com",
// Scripts - removed 'unsafe-eval' which can cause hydration issues in Cloudflare
"script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://*.cloudflareinsights.com https://*.posthog.com https://us-assets.i.posthog.com https://eu-assets.i.posthog.com",
// Styles
"style-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://cdn.jsdelivr.net",
// Images

View File

@ -4,9 +4,8 @@ export const htmlCacheHeaders = [
{ httpEquiv: 'Pragma', content: 'cache' },
];
// Only use aggressive no-cache for specific routes that need it
// Use less aggressive caching to work better with Cloudflare
export const preventBrowserCacheHeaders = [
{ httpEquiv: 'Cache-Control', content: 'no-cache, no-store, must-revalidate' },
{ httpEquiv: 'Cache-Control', content: 'public, max-age=0, must-revalidate' },
{ httpEquiv: 'Pragma', content: 'no-cache' },
{ httpEquiv: 'Expires', content: '0' },
];

View File

@ -1,6 +1,7 @@
import { createRootRouteWithContext, HeadContent, Scripts } from '@tanstack/react-router';
import { ErrorBoundary } from 'react-error-boundary';
import { RootProviders } from '@/context/Providers';
import { htmlCacheHeaders } from '@/middleware/shared-headers';
import { preventBrowserCacheHeaders } from '@/middleware/shared-headers';
import shareImage from '../assets/png/default_preview.png';
import favicon from '../assets/png/favicon.ico';
import { TanstackDevtools } from '../integrations/tanstack-dev-tools/tanstack-devtools';
@ -31,7 +32,7 @@ export const Route = createRootRouteWithContext<AppRouterContext>()({
{ name: 'og:type', content: 'website' },
{ name: 'og:locale', content: 'en_US' },
{ name: 'og:site_name', content: 'Buster' },
...htmlCacheHeaders,
...preventBrowserCacheHeaders,
],
links: [
{ rel: 'stylesheet', href: appCss },
@ -55,6 +56,7 @@ function RootDocument({ children }: { children: React.ReactNode }) {
</head>
<body>
<RootProviders>{children}</RootProviders>
<TanstackDevtools />
<Scripts />
</body>

View File

@ -14,6 +14,7 @@ const DEFAULT_LAYOUT: LayoutSize = ['230px', 'auto'];
export const Route = createFileRoute('/app')({
head: () => {
console.log('app head');
return {
meta: [...preventBrowserCacheHeaders],
};
@ -21,7 +22,10 @@ export const Route = createFileRoute('/app')({
context: ({ context }) => ({ ...context, getAppLayout }),
beforeLoad: async () => {
try {
console.log('app beforeLoad - getSupabaseSession');
const { isExpired, accessToken = '' } = await getSupabaseSession();
console.log('app beforeLoad - getSupabaseSession - isExpired', isExpired);
console.log('app beforeLoad - getSupabaseSession - accessToken', accessToken);
if (isExpired || !accessToken) {
console.error('Access token is expired or not found');
@ -39,6 +43,7 @@ export const Route = createFileRoute('/app')({
loader: async ({ context }) => {
const { queryClient, accessToken } = context;
try {
console.log('app loader - getAppLayout');
const [initialLayout, user] = await Promise.all([
getAppLayout({ id: PRIMARY_APP_LAYOUT_ID }),
getSupabaseUser(),
@ -47,6 +52,8 @@ export const Route = createFileRoute('/app')({
prefetchListDatasources(queryClient),
prefetchGetDatasets(queryClient),
]);
console.log('app loader - getAppLayout - initialLayout', initialLayout);
console.log('app loader - getAppLayout - user', user);
if (!user) {
console.error('User not found - redirecting to login');
@ -66,7 +73,10 @@ export const Route = createFileRoute('/app')({
}
},
component: () => {
console.log('app component');
const { user, accessToken } = Route.useLoaderData();
console.log('app component - user', user);
console.log('app component - accessToken', accessToken);
return (
<AppProviders user={user} accessToken={accessToken}>

View File

@ -11,7 +11,7 @@ const config = defineConfig(({ command, mode }) => {
const isTypecheck = process.argv.includes('--typecheck') || process.env.TYPECHECK === 'true';
const useChecker = !process.env.VITEST && isBuild;
const isLocalBuild = process.argv.includes('--local') || mode === 'development';
const target = isLocalBuild ? ('bun' as const) : ('cloudflare-module' as const);
const target = isLocalBuild ? ('bun' as const) : ('vercel' as const);
return {
server: { port: 3000 },

View File

@ -406,9 +406,6 @@ importers:
'@faker-js/faker':
specifier: ^10.0.0
version: 10.0.0
'@modelcontextprotocol/sdk':
specifier: ^1.17.5
version: 1.17.5
'@monaco-editor/react':
specifier: ^4.7.0
version: 4.7.0(monaco-editor@0.53.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
@ -763,6 +760,9 @@ importers:
react-markdown:
specifier: ^10.1.0
version: 10.1.0(@types/react@19.1.12)(react@19.1.1)
react-mentions:
specifier: ^4.4.10
version: 4.4.10(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
react-player:
specifier: ^3.3.2
version: 3.3.2(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
@ -872,6 +872,9 @@ importers:
'@types/react-dom':
specifier: 'catalog:'
version: 19.1.9(@types/react@19.1.12)
'@types/react-mentions':
specifier: ^4.4.1
version: 4.4.1
'@vitejs/plugin-react':
specifier: ^5.0.2
version: 5.0.2(vite@7.1.4(@types/node@24.0.10)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.92.1)(terser@5.43.1)(tsx@4.20.5)(yaml@2.8.1))
@ -1918,6 +1921,9 @@ packages:
resolution: {integrity: sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==}
engines: {node: '>=6.9.0'}
'@babel/runtime@7.4.5':
resolution: {integrity: sha512-TuI4qpWZP6lGOGIuGWtp9sPluqYICmbk8T/1vpSysqJxRPkudh/ofFWyqdcMsDf2s7KvDL4/YHgKyvcS3g9CJQ==}
'@babel/template@7.27.2':
resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==}
engines: {node: '>=6.9.0'}
@ -3402,10 +3408,6 @@ packages:
'@microsoft/fetch-event-source@2.0.1':
resolution: {integrity: sha512-W6CLUJ2eBMw3Rec70qrsEW0jOm/3twwJv21mrmj2yORiaVmVYGS4sSS5yUwvQc1ZlDLYGPnClVWmUUMagKNsfA==}
'@modelcontextprotocol/sdk@1.17.5':
resolution: {integrity: sha512-QakrKIGniGuRVfWBdMsDea/dx1PNE739QJ7gCM41s9q+qaCYTHCdsIBXQVVXry3mfWAiaM9kT22Hyz53Uw8mfg==}
engines: {node: '>=18'}
'@monaco-editor/loader@1.5.0':
resolution: {integrity: sha512-hKoGSM+7aAc7eRTRjpqAZucPmoNOC4UUbknb/VNoTkEIkCPhqV8LfbsgM1webRM7S/z21eHEx9Fkwx8Z/C/+Xw==}
@ -5952,6 +5954,9 @@ packages:
peerDependencies:
'@types/react': ^19.0.0
'@types/react-mentions@4.4.1':
resolution: {integrity: sha512-65QdcZYkGe2I4GnOLY2OhlXCGz/Csd8NhytwE5r59CoFeYafMltAE/WqFB/Y6SoPU8LvF7EyUrq6Rxrf0Kzxkg==}
'@types/react@19.1.12':
resolution: {integrity: sha512-cMoR+FoAf/Jyq6+Df2/Z41jISvGZZ2eTlnsaJRptmZ76Caldwy1odD4xTr/gNV9VLj0AWgg/nmkevIyUfIIq5w==}
@ -7667,12 +7672,6 @@ packages:
resolution: {integrity: sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==}
engines: {node: '>=12.0.0'}
express-rate-limit@7.5.1:
resolution: {integrity: sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==}
engines: {node: '>= 16'}
peerDependencies:
express: '>= 4.11'
express@4.21.2:
resolution: {integrity: sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==}
engines: {node: '>= 0.10.0'}
@ -8333,6 +8332,9 @@ packages:
resolution: {integrity: sha512-zDml/jzr2PKU9I8J/xyZBQn8rPCAY//UOYNmR01XwNwyfhEWObo2SWfSl1+0tm1u6PhxLwDnfsT/6jB7OUxqFA==}
engines: {node: '>= 0.10'}
invariant@2.2.4:
resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==}
ioredis@5.7.0:
resolution: {integrity: sha512-NUcA93i1lukyXU+riqEyPtSEkyFq8tX90uL659J+qpCZ3rEdViB/APC58oAhIh3+bJln2hzdlZbBZsGNrlsR8g==}
engines: {node: '>=12.22.0'}
@ -9878,10 +9880,6 @@ packages:
resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==}
engines: {node: '>= 6'}
pkce-challenge@5.0.0:
resolution: {integrity: sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==}
engines: {node: '>=16.20.0'}
pkg-dir@4.2.0:
resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==}
engines: {node: '>=8'}
@ -10179,6 +10177,12 @@ packages:
'@types/react': '>=18'
react: '>=18'
react-mentions@4.4.10:
resolution: {integrity: sha512-JHiQlgF1oSZR7VYPjq32wy97z1w1oE4x10EuhKjPr4WUKhVzG1uFQhQjKqjQkbVqJrmahf+ldgBTv36NrkpKpA==}
peerDependencies:
react: '>=16.8.3'
react-dom: '>=16.8.3'
react-player@3.3.2:
resolution: {integrity: sha512-MBSCxTA1FPyMR19Wy+2LtVjguhrLl9p2l5nODY4fbumgsoaCEuhMLpZvxh8RWjzzvqL8V3jYcPfw/XhqrbTzFw==}
peerDependencies:
@ -10294,6 +10298,9 @@ packages:
redux@4.2.1:
resolution: {integrity: sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==}
regenerator-runtime@0.13.11:
resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==}
regex-recursion@6.0.2:
resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==}
@ -10849,6 +10856,11 @@ packages:
babel-plugin-macros:
optional: true
substyle@9.4.1:
resolution: {integrity: sha512-VOngeq/W1/UkxiGzeqVvDbGDPM8XgUyJVWjrqeh+GgKqspEPiLYndK+XRcsKUHM5Muz/++1ctJ1QCF/OqRiKWA==}
peerDependencies:
react: '>=16.8.3'
sucrase@3.35.0:
resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==}
engines: {node: '>=16 || 14 >=14.17'}
@ -13461,6 +13473,10 @@ snapshots:
'@babel/runtime@7.27.6': {}
'@babel/runtime@7.4.5':
dependencies:
regenerator-runtime: 0.13.11
'@babel/template@7.27.2':
dependencies:
'@babel/code-frame': 7.27.1
@ -14600,23 +14616,6 @@ snapshots:
'@microsoft/fetch-event-source@2.0.1': {}
'@modelcontextprotocol/sdk@1.17.5':
dependencies:
ajv: 6.12.6
content-type: 1.0.5
cors: 2.8.5
cross-spawn: 7.0.6
eventsource: 3.0.7
eventsource-parser: 3.0.6
express: 5.1.0
express-rate-limit: 7.5.1(express@5.1.0)
pkce-challenge: 5.0.0
raw-body: 3.0.1
zod: 3.25.76
zod-to-json-schema: 3.24.6(zod@3.25.76)
transitivePeerDependencies:
- supports-color
'@monaco-editor/loader@1.5.0':
dependencies:
state-local: 1.0.7
@ -17665,6 +17664,10 @@ snapshots:
dependencies:
'@types/react': 19.1.12
'@types/react-mentions@4.4.1':
dependencies:
'@types/react': 19.1.12
'@types/react@19.1.12':
dependencies:
csstype: 3.1.3
@ -17917,7 +17920,7 @@ snapshots:
sirv: 3.0.1
tinyglobby: 0.2.14
tinyrainbow: 2.0.0
vitest: 3.2.4(@edge-runtime/vm@3.2.0)(@types/debug@4.1.12)(@types/node@22.18.0)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(jiti@2.5.1)(jsdom@26.1.0)(lightningcss@1.30.1)(msw@2.11.2(@types/node@22.18.0)(typescript@5.9.2))(sass@1.92.1)(terser@5.43.1)(tsx@4.20.5)(yaml@2.8.1)
vitest: 3.2.4(@edge-runtime/vm@3.2.0)(@types/debug@4.1.12)(@types/node@24.0.10)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(jiti@2.5.1)(jsdom@26.1.0)(lightningcss@1.30.1)(msw@2.11.2(@types/node@24.0.10)(typescript@5.9.2))(sass@1.92.1)(terser@5.43.1)(tsx@4.20.5)(yaml@2.8.1)
'@vitest/utils@3.2.4':
dependencies:
@ -18100,6 +18103,7 @@ snapshots:
dependencies:
mime-types: 3.0.1
negotiator: 1.0.0
optional: true
acorn-import-attributes@1.9.5(acorn@8.15.0):
dependencies:
@ -18157,6 +18161,7 @@ snapshots:
fast-json-stable-stringify: 2.1.0
json-schema-traverse: 0.4.1
uri-js: 4.4.1
optional: true
ajv@8.17.1:
dependencies:
@ -18432,6 +18437,7 @@ snapshots:
type-is: 2.0.1
transitivePeerDependencies:
- supports-color
optional: true
boolbase@1.0.0: {}
@ -18857,6 +18863,7 @@ snapshots:
content-disposition@1.0.0:
dependencies:
safe-buffer: 5.2.1
optional: true
content-type@1.0.5: {}
@ -18872,7 +18879,8 @@ snapshots:
cookie-signature@1.0.6: {}
cookie-signature@1.2.2: {}
cookie-signature@1.2.2:
optional: true
cookie@0.4.2: {}
@ -19607,10 +19615,6 @@ snapshots:
expect-type@1.2.2: {}
express-rate-limit@7.5.1(express@5.1.0):
dependencies:
express: 5.1.0
express@4.21.2:
dependencies:
accepts: 1.3.8
@ -19678,6 +19682,7 @@ snapshots:
vary: 1.1.2
transitivePeerDependencies:
- supports-color
optional: true
exsolve@1.0.7: {}
@ -19703,7 +19708,8 @@ snapshots:
merge2: 1.4.1
micromatch: 4.0.8
fast-json-stable-stringify@2.1.0: {}
fast-json-stable-stringify@2.1.0:
optional: true
fast-levenshtein@2.0.6:
optional: true
@ -19796,6 +19802,7 @@ snapshots:
statuses: 2.0.2
transitivePeerDependencies:
- supports-color
optional: true
find-cache-dir@3.3.2:
dependencies:
@ -20322,6 +20329,7 @@ snapshots:
iconv-lite@0.7.0:
dependencies:
safer-buffer: 2.1.2
optional: true
ieee754@1.2.1: {}
@ -20435,6 +20443,10 @@ snapshots:
install@0.13.0: {}
invariant@2.2.4:
dependencies:
loose-envify: 1.4.0
ioredis@5.7.0:
dependencies:
'@ioredis/commands': 1.3.1
@ -20538,7 +20550,8 @@ snapshots:
is-potential-custom-element-name@1.0.1: {}
is-promise@4.0.0: {}
is-promise@4.0.0:
optional: true
is-property@1.0.2: {}
@ -20735,7 +20748,8 @@ snapshots:
json-parse-even-better-errors@2.3.1:
optional: true
json-schema-traverse@0.4.1: {}
json-schema-traverse@0.4.1:
optional: true
json-schema-traverse@1.0.0: {}
@ -21235,13 +21249,15 @@ snapshots:
media-typer@0.3.0: {}
media-typer@1.1.0: {}
media-typer@1.1.0:
optional: true
mensch@0.3.4: {}
merge-descriptors@1.0.3: {}
merge-descriptors@2.0.0: {}
merge-descriptors@2.0.0:
optional: true
merge-stream@2.0.0: {}
@ -21837,7 +21853,8 @@ snapshots:
negotiator@0.6.3: {}
negotiator@1.0.0: {}
negotiator@1.0.0:
optional: true
neo-async@2.6.2:
optional: true
@ -22308,7 +22325,8 @@ snapshots:
path-to-regexp@6.3.0: {}
path-to-regexp@8.3.0: {}
path-to-regexp@8.3.0:
optional: true
path-type@6.0.0: {}
@ -22409,8 +22427,6 @@ snapshots:
pirates@4.0.7: {}
pkce-challenge@5.0.0: {}
pkg-dir@4.2.0:
dependencies:
find-up: 4.1.0
@ -22605,6 +22621,7 @@ snapshots:
qs@6.14.0:
dependencies:
side-channel: 1.1.0
optional: true
quansync@0.2.11: {}
@ -22637,6 +22654,7 @@ snapshots:
http-errors: 2.0.0
iconv-lite: 0.7.0
unpipe: 1.0.0
optional: true
rc9@2.1.2:
dependencies:
@ -22738,6 +22756,15 @@ snapshots:
transitivePeerDependencies:
- supports-color
react-mentions@4.4.10(react-dom@19.1.1(react@19.1.1))(react@19.1.1):
dependencies:
'@babel/runtime': 7.4.5
invariant: 2.2.4
prop-types: 15.8.1
react: 19.1.1
react-dom: 19.1.1(react@19.1.1)
substyle: 9.4.1(react@19.1.1)
react-player@3.3.2(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1):
dependencies:
'@mux/mux-player-react': 3.5.1(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
@ -22869,6 +22896,8 @@ snapshots:
dependencies:
'@babel/runtime': 7.27.6
regenerator-runtime@0.13.11: {}
regex-recursion@6.0.2:
dependencies:
regex-utilities: 2.3.0
@ -23032,6 +23061,7 @@ snapshots:
path-to-regexp: 8.3.0
transitivePeerDependencies:
- supports-color
optional: true
rrweb-cssom@0.8.0: {}
@ -23609,6 +23639,12 @@ snapshots:
'@babel/core': 7.28.4
optional: true
substyle@9.4.1(react@19.1.1):
dependencies:
'@babel/runtime': 7.27.6
invariant: 2.2.4
react: 19.1.1
sucrase@3.35.0:
dependencies:
'@jridgewell/gen-mapping': 0.3.12
@ -23997,6 +24033,7 @@ snapshots:
content-type: 1.0.5
media-typer: 1.1.0
mime-types: 3.0.1
optional: true
typedarray-to-buffer@3.1.5:
dependencies:
@ -24213,6 +24250,7 @@ snapshots:
uri-js@4.4.1:
dependencies:
punycode: 2.3.1
optional: true
use-callback-ref@1.3.3(@types/react@19.1.12)(react@19.1.1):
dependencies: