@citamets/chat-native
v0.4.16
Published
Scholara's chat-native review UI — the React component the platform's authenticated dashboard renders, lifted out so the marketing landing on scholara.ai can mount the same chat anonymously.
Readme
@citamets/chat-native
Scholara's chat-native review UI, lifted out of the platform so the
marketing landing on scholara.ai can mount the same chat as the
authenticated dashboard.
Why this package exists
The platform's ReviewChatPanel is a 5,000-LOC React component that
drives the entire review lifecycle (PICO → search → screening →
analysis). The marketing site needs to render the same component for
anonymous visitors so the chat they see pre-signup is bit-identical to
what they get post-signup — same prompts, same UI, same affordances.
To make that possible without forking, the chat-native subtree was
refactored to read every platform-coupled hook (auth, session,
WebSocket, REST client) through a ChatNativeAdaptersProvider
context. The platform mounts the provider with its own hooks; the
website mounts it with buildAnonymousAdapters() from this package.
See the feat/chat-native-adapters PR series for the adapter
refactor; this package is the extraction step.
Status
v0.1.0 is workspace-internal and private: true to prevent an
accidental pnpm publish before Phase 3 closes the gap below.
The chat-native subtree lives here and the platform consumes it via
the workspace symlink + tsconfig path alias — tsc, jest, and
next build all resolve it cleanly. The package is NOT yet
npm-publishable because of two classes of unresolved couplings:
@/...platform paths — ~83 source files reference paths that point outside the package:@/lib/api/types,@/lib/auth,@/components/ui/Modal,@/components/protocol/ProtocolEditor, etc.- Bare-module imports not yet on the dep manifest — e.g.
@sentry/nextjs(used byCompletedScreeningCard.tsxandreasoning/ReasoningExportMenu.tsx) is markedexternalintsup.config.tsbut isn't declared as apeerDependencyorpeerDependenciesMeta.optional. After publish, an anonymous consumer without Sentry installed would hard-crash at module load. Audit + declare (or adapter-inject, or no-op shim) before v0.1.0 ships.
Phase 3 closes both classes by, for each reference, choosing: (a)
move the helper into the package, (b) adapter-inject it via
ChatNativeAdapters, or (c) declare it as a peer dep (optional
where appropriate) the consumer must provide. Until that work
lands, pnpm --filter @citamets/chat-native build will fail with
unresolved-module errors — that's expected, not a regression. The
platform doesn't go through dist/; it consumes source via the
workspace alias.
Usage (post-extraction)
import {
ReviewChatPanel,
ChatNativeAdaptersProvider,
} from "@citamets/chat-native";
import { buildAnonymousAdapters } from "@citamets/chat-native/adapters/anonymous";
import "@citamets/chat-native/styles.css";
// Call once at module scope. See the adapter's JSDoc — invoking this
// inside a render churns the api Proxy identity every paint and
// breaks every dep array that captures `api`.
const ANON_ADAPTERS = buildAnonymousAdapters();
export function AnonymousChatPage() {
return (
<ChatNativeAdaptersProvider value={ANON_ADAPTERS}>
<ReviewChatPanel sessionId="anon" />
</ChatNativeAdaptersProvider>
);
}Peer dependencies
The consumer is expected to provide these via its own bundler:
| Peer | Why |
|---|---|
| react, react-dom (>=18) | The component is a React tree |
| next (>=15) | Uses next/navigation, next/dynamic |
| @tanstack/react-query (>=5) | Hooks-level caching surface |
| lucide-react (>=0.500) | Icon set the chat references |
Heavy exporters (@react-pdf/renderer, docx, jszip, file-saver)
are kept behind the @citamets/chat-native/exporters subpath so the
anonymous bundle never pulls them in.
Build
pnpm --filter @citamets/chat-native buildProduces ESM + CJS + .d.ts for the three entry points, plus
styles.css.
Release
Tag-based publish to npm under the @citamets org. See RELEASING.md
(added in Phase 3).
