npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@likemex/fs-react-lib

v0.0.8

Published

Shared FS AI assistant chat widget (REST + SSE) for FutureSkill Next.js apps

Readme

@likemex/fs-react-lib

npm license

Shared in-app AI assistant ("FS AI") chat widget for Next.js apps. REST + Server-Sent Events streaming. Drop-in panel + floating button + Next.js API proxy + Tailwind tokens.

Ships:

  • <AssistantPanel> — slide-in chat drawer (general or course-aware modes).
  • <AssistantNavButton> — toggle button (inline or floating FAB).
  • <AssistantContextProvider> + useAssistant() — panel open/close state, persisted to localStorage.
  • configureFsAi({ getToken }) — wires host app auth into the FS AI Axios client.
  • createFsAiProxyHandler() — Next.js API route factory that proxies /api/fs-ai/** to the upstream origin and injects the server-only X-API-Key.
  • Hooks: useAssistantConversation, useAssistantPhase, useAssistantStream.
  • Helpers: buildLearningMetadata, canUseLearningAssistant, isFsAiApiConfigured, conversation history store, user-profile store, SSE decoder, markdown sanitizer.

Install

npm install @likemex/fs-react-lib
# or
pnpm add @likemex/fs-react-lib
# or
yarn add @likemex/fs-react-lib

Peer deps (install whichever your app uses):

  • react ^17 || ^18 + react-dom ^17 || ^18
  • next ^12 || ^13 || ^14
  • axios >=0.24
  • antd ^5 + @ant-design/cssinjs ^1
  • @tanstack/react-query ^4
  • react-icons ^4 || ^5
  • react-markdown ^7 || ^8
  • remark-gfm ^3 || ^4

Quick start (Next.js Pages Router)

1. Wire pages/_app.tsx

import {
    AssistantContextProvider,
    AssistantPanel,
    AssistantNavButton,
    configureFsAi,
} from '@likemex/fs-react-lib';
import { auth } from 'services/auth';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ConfigProvider, App as AntdApp } from 'antd';
import { StyleProvider } from '@ant-design/cssinjs';
import 'antd/dist/reset.css';

configureFsAi({ getToken: () => auth.getToken() ?? null });
const queryClient = new QueryClient();

export default function MyApp({ Component, pageProps }) {
    return (
        <ConfigProvider theme={{ token: { colorPrimary: '#842CDD' } }}>
            <AntdApp>
                <StyleProvider hashPriority="high">
                    <QueryClientProvider client={queryClient}>
                        <AssistantContextProvider>
                            <Component {...pageProps} />
                            <AssistantNavButton floating canUse />
                            <AssistantPanel surface="general" canUse />
                        </AssistantContextProvider>
                    </QueryClientProvider>
                </StyleProvider>
            </AntdApp>
        </ConfigProvider>
    );
}

2. Add the proxy route

pages/api/fs-ai/[[...slug]].ts:

import { createFsAiProxyHandler } from '@likemex/fs-react-lib';

export const config = { api: { bodyParser: false, responseLimit: false as const } };
export default createFsAiProxyHandler();

3. Tailwind tokens + content path

tailwind.config.js:

const assistantTokens = require('@likemex/fs-react-lib/tailwind-tokens');

module.exports = {
    content: [
        './pages/**/*.{js,ts,jsx,tsx}',
        './components/**/*.{js,ts,jsx,tsx}',
        // scan compiled lib so Tailwind picks up its classes
        './node_modules/@likemex/fs-react-lib/dist/**/*.{js,d.ts}',
    ],
    theme: {
        extend: {
            colors: {
                // merge into existing palette
                primaryFS: assistantTokens.primaryFS,
                pinkFS: assistantTokens.pinkFS,
                blackFS: assistantTokens.blackFS,
                successFS: assistantTokens.successFS,
            },
        },
    },
};

4. Next 12 ESM peer compat

The compiled lib is CJS but pulls in ESM-only peers (react-markdown@8, remark-gfm@3). Next 12 needs:

// next.config.js
const withTM = require('next-transpile-modules')(['@likemex/fs-react-lib']);

module.exports = withTM({
    experimental: { esmExternals: 'loose' },
    // ...rest of your config
});

Next 13+: replace next-transpile-modules with built-in transpilePackages: ['@likemex/fs-react-lib'].

5. Env vars

NEXT_PUBLIC_FS_AI_USE_PROXY=true
FS_AI_API_URL=https://<your-fs-ai-host>
FS_AI_X_API_KEY=<server-only-key>

| Var | Side | Purpose | |-----|------|---------| | NEXT_PUBLIC_FS_AI_USE_PROXY | Client | "true" routes through the Next API proxy (recommended — keeps the API key server-side). | | NEXT_PUBLIC_FS_AI_API_URL | Client | Direct upstream origin (when proxy disabled). | | FS_AI_API_URL | Server | Upstream origin used by the proxy factory. | | FS_AI_X_API_KEY | Server | X-API-Key header injected by the proxy. Never exposed to the browser. | | NEXT_PUBLIC_LEARNING_ASSISTANT_SHOW_FOR_ALL | Client | Dev flag — show the assistant regardless of pass entitlement. |

Modes

AssistantPanel accepts a modes prop. When modes.length === 1 the ModePicker screen is skipped — chat opens directly.

// Course-bound app (5-mode picker)
<AssistantPanel
    surface="watch"
    courseId={courseId}
    lessonId={lessonId}
    modes={['general', 'before_class', 'during_class', 'after_class', 'apply']}
/>

// General-only app (no picker, no course)
<AssistantPanel surface="general" />

courseId is optional. When omitted:

  • learning_mode='general' is set at conversation creation.
  • patchLearningMode is skipped.
  • learning_state.course_id is omitted from message metadata.
  • Conversation history is bucketed under surface='general' with courseId=null.

Server-side rendering

AssistantPanel uses TanStack Query and reads localStorage. For pages built with getStaticProps (SSG), wrap the panel with next/dynamic to skip server render:

import dynamic from 'next/dynamic';
const AssistantPanel = dynamic(
    () => import('@likemex/fs-react-lib').then(m => m.AssistantPanel),
    { ssr: false }
);

Pages using getServerSideProps or no data fetching do not need this — the QueryClientProvider in _app is sufficient.

API surface

import {
    // Components
    AssistantPanel,
    AssistantNavButton,
    AssistantContextProvider,
    Composer,
    MessageList,
    MessageBubble,
    ModePicker,
    SuggestedActions,
    WelcomeMessage,
    ASSISTANT_PANEL_WIDTH,
    SUGGESTED_ACTIONS_BY_MODE,

    // Context
    useAssistant,
    AssistantContext,

    // Auth + service
    configureFsAi,
    fsAiApi,

    // Hooks
    useAssistantConversation,
    useAssistantPhase,
    useAssistantStream,

    // Helpers
    buildLearningMetadata,
    canUseLearningAssistant,
    isFsAiApiConfigured,
    listAssistantConversations,
    upsertAssistantConversation,
    removeAssistantConversation,
    readAssistantFullPagePreference,
    writeAssistantFullPagePreference,
    readAssistantUserProfile,
    writeAssistantUserProfile,
    clearAssistantUserProfile,
    isAssistantUserProfileComplete,
    decodeSseDataPayload,
    extractTextFromStreamJson,
    filterDisplayableAssistantSources,
    sanitizeAssistantMarkdown,
    visibleStripped,
    isOrphanListMarkerLine,

    // Proxy factory
    createFsAiProxyHandler,
} from '@likemex/fs-react-lib';

Full type definitions in dist/index.d.ts.

Tailwind tokens subpath

// CommonJS (tailwind.config.js)
const tokens = require('@likemex/fs-react-lib/tailwind-tokens');
// → { primaryFS, pinkFS, blackFS, successFS }

Development

git clone https://github.com/LikeMeX/fs-react-lib.git
cd fs-react-lib
pnpm install
pnpm run build    # tsc → dist/
pnpm run lint     # tsc --noEmit

Publish (maintainers): tag a release, GitHub Action runs build + npm publish --provenance. See .github/workflows/publish.yaml.

License

MIT © LikeMeX