@evelan/jexity-widget

中文文档
0.35.0发布于 2026/11/13MITTypeScriptESM + CJS近 30 天下载 2154

Embeddable AI chat widget by Evelan — add customer support chat to any website with a single script tag or npm install

原始使用文档
文档目录15 节
  1. Quick Start
  2. Script Tag (No Build Tools Required)
  3. NPM
  4. React Provider
  5. Signed-in users: sign the pass on your server
  6. Configuration
  7. Granular Theme Overrides
  8. Programmatic API
  9. IIFE (Script Tag) Attributes
  10. Build Outputs
  11. Development
  12. Environment variables
  13. URL Parameters
  14. Build
  15. License

@evelan/jexity-widget

Embeddable AI chat widget for any website. Add customer support chat with a single <script> tag or via npm.

Quick Start

Script Tag (No Build Tools Required)

<script
  src="https://cdn.jexitychat.de/widget/latest/widget.js"
  data-org-slug="YOUR_ORG_SLUG"
  async
></script>

NPM

npm install @evelan/jexity-widget
import { JexityChat } from '@evelan/jexity-widget'

const chat = JexityChat({
  orgSlug: 'YOUR_ORG_SLUG',
})

// Later:
chat.shutdown()

React Provider

import { JexityChatProvider, useJexityChat } from '@evelan/jexity-widget/react'

function App() {
  return (
    <JexityChatProvider orgSlug="YOUR_ORG_SLUG">
      <HomePage />
    </JexityChatProvider>
  )
}

function HomePage() {
  const { open, close, toggle, isOpen } = useJexityChat()
  return <button onClick={open}>Chat with us</button>
}

Signed-in users: sign the pass on your server

@evelan/jexity-widget/server signs the pass for the user who is signed in, on your server only (Node 20+, Next.js route handlers, edge runtimes, Convex, Deno, Bun). No dependencies. The key decides the algorithm: a sign-in key from the Sign-in window signs HS256, a P-256 private key (JWK) whose public half is registered signs ES256.

// app/api/jexity-pass/route.ts (Next.js)
import { createJexityPass } from '@evelan/jexity-widget/server'

export async function GET() {
  const user = await getSignedInUser() // your app's own login, checked on the server
  if (!user) return new Response('', { status: 401 })
  const pass = await createJexityPass(
    { userId: user.id, name: user.name, email: user.emailVerified ? user.email : null, accountId: user.companyId },
    process.env.JEXITY_SIGN_IN_KEY,
  )
  return new Response(pass)
}

It keeps the pass inside what Jexity accepts: a long name is cut, an invalid email left out, iat/exp in seconds, 1 hour by default ({ lifetimeSeconds } up to 24 hours for a pass in the page). A user id over 128 characters throws instead of being cut, so two users never share a chat. A broken key throws a fixed message that never repeats the key.


Configuration

OptionTypeDefaultDescription
orgSlugstringrequiredYour organization slug (e.g. acme-corp)
containerstring | HTMLElementauto-created divCSS selector or element to mount into
position'bottom-right' | 'bottom-left''bottom-right'Widget position on screen
colorScheme'light' | 'dark' | 'auto''auto'Force color scheme; 'auto' follows system preference
titlestring'Jexity Assistant'Chat window header title
greetingstring'Hi! How can I help?'Initial bot message shown on open
primaryColorstring'#0070f3'Shorthand brand color — tints FAB, header, user bubbles, send button
accentColorstring—Secondary accent color
fontFamilystring—Override widget font family
fontSizenumber—Base font size in px
borderRadiusstring—Override widget corner radius (e.g. '8px')
windowWidthnumber—Chat window width in px
windowHeightnumber—Chat window height in px
triggerIcon'chat' | 'bot' | 'sparkles''chat'Floating action button icon
triggerRadius'none' | 'sm' | 'md' | 'lg' | 'full''full'FAB border radius shape
triggerLabelstring—Text label shown next to the FAB
hideBrandingbooleanfalseHide the "Powered by Jexity" footer
localestringbrowser / 'de'Force locale ('de' | 'en'). Overrides browser detection
parentOriginstringwindow.location.originTrusted origin for postMessage dashboard preview updates
themeJexityTheme—Granular per-element color overrides (see below)
userTokenstring | () => string | Promise<string>—A signed-in user's pass: a JWT your server signs with the project's sign-in key, best with createJexityPass from @evelan/jexity-widget/server (HS256 with a secret key, ES256 with a registered public key's private half) (sub, optional name, email, account_id, iat, exp ≤ 24 h). A function is called on every bootstrap, including the hourly refresh. A refused pass stops the widget; it never falls back to anonymous. Script tag: data-user-token. A pass for another user (a changed attribute or string prop) starts over; with a function, call logout() at sign-out. See the help center: Recognize signed-in users.

Granular Theme Overrides

theme lets you override individual element colors. Unset keys fall back to primaryColor or brand defaults.

JexityChat({
  orgSlug: 'acme',
  theme: {
    bg: {
      trigger: '#1a1a2e',    // FAB background
      header: '#16213e',     // Chat header
      msgUser: '#0f3460',    // User bubble background
      msgBot: '#f5f5f5',     // Bot bubble background
      window: '#ffffff',     // Chat window background
      sendBtn: '#e94560',    // Send button background
    },
    text: {
      header: '#ffffff',     // Header text/icon color
      msgUser: '#ffffff',    // User bubble text
      msgBot: '#333333',     // Bot bubble text
      main: '#212121',       // Primary body text
      muted: '#888888',      // Secondary/muted text
    },
    border: {
      window: '#e0e0e0',     // Window border
      input: '#cccccc',      // Input field border
    },
  },
})

Programmatic API

const chat = JexityChat(options)

chat.open()                    // Open the chat window
chat.close()                   // Close the chat window
chat.toggle()                  // Toggle open/closed
chat.ask("How do I crawl?")    // Open the chat and send this question
chat.shutdown()                // Remove widget from DOM
chat.logout()                  // Signed-in user signed out: forget this tab's chats, start over with the current pass
chat.isOpen                    // readonly boolean — current open state
chat.subscribe(cb)             // Subscribe to open/close changes
                               // Returns unsubscribe function

// Your user signed out, also before the chat has loaded on this page:
import { logout } from "@evelan/jexity-widget" // or "@evelan/jexity-widget/react"
logout()                       // Forget this tab's chats

// IIFE global (script tag):
window.JexityChat.open()
window.JexityChat.close()
window.JexityChat.toggle()
window.JexityChat.shutdown()

IIFE (Script Tag) Attributes

All options can be set as data-* attributes on the <script> tag:

<script
  src="https://cdn.jexitychat.de/widget/latest/widget.js"
  data-org-slug="acme-corp"
  data-locale="de"
  data-primary-color="#ff6600"
  data-trigger-icon="sparkles"
  data-trigger-radius="lg"
  data-trigger-label="Chat with us"
  data-hide-branding="true"
  data-convex-url="https://your-deployment.convex.cloud"
  async
></script>

Then control programmatically after load:

<script>
  window.JexityChat.open()
  window.JexityChat.shutdown()
</script>

Build Outputs

dist/
  widget.js          ← IIFE for <script> embed (CDN)
  index.mjs          ← ESM for bundlers (npm)
  react.mjs          ← React bindings (externalizes react + core)
  server.mjs/.cjs    ← createJexityPass for your server (no Preact, React or Convex)
  types/index.d.ts   ← TypeScript declarations (core)
  types/react.d.ts   ← TypeScript declarations (React)

Target sizes: IIFE ≤ 32 KB · ESM ≤ 42 KB (gzipped)


Development

# From repo root:
pnpm dev --filter @evelan/jexity-widget

# Or directly:
cd apps/widget
pnpm dev

Open http://localhost:3002 — the dev playground with live config controls.

Environment variables

Create apps/widget/.env.local:

# Convex deployment URL — used for both WebSocket (real-time) and HTTP actions (config fetch).
# The widget auto-derives .convex.site from .convex.cloud via convex-url.ts.
# Copy from packages/backend/.env.local (CONVEX_URL).
VITE_CONVEX_URL=https://<your-dev-deployment>.convex.cloud

# Reserved — not consumed yet. Future escape hatch if we ever swap Convex for
# a traditional REST backend (Express, Fastify, etc.). Once wired, widget code
# will read this instead of VITE_CONVEX_URL so the backend is hot-swappable
# via env alone.
# VITE_API_BASE_URL=https://api.example.com

URL Parameters

ParameterExamplePurpose
?orgSlug=?orgSlug=demoSet org slug (default: dev_local)
?primaryColor=?primaryColor=%230070f3Set primary color
?locale=?locale=enLock locale
?position=?position=bottom-leftWidget position
?title=?title=My+AssistantWidget title
?hideBranding=true—Hide footer branding
?skipHomeScreen=true—Open directly to chat (skip landing)

Build

pnpm build          # Full build (all 5 outputs)
pnpm check-types    # Type-check only (fast)

License

MIT

版本与发布记录

@evelan/jexity-widget 共发布 85 个版本,首次发布于 2026/03/04;过去 12 个月发布了 85 个版本。

版本发布日期距今
0.35.0latest2026/11/131 天前
0.35.0-canary.93canary2026/11/131 天前
0.34.0-canary.922026/11/131 天前
0.34.0-canary.912026/11/122 天前
0.34.02026/11/112 天前
0.34.0-canary.902026/11/113 天前

关于 @evelan/jexity-widget 的常见问题

@evelan/jexity-widget 是什么?

Embeddable AI chat widget by Evelan — add customer support chat to any website with a single script tag or npm install

如何安装 @evelan/jexity-widget?

在项目目录运行 npm install @evelan/jexity-widget,或使用 pnpm add / yarn add / bun add。

@evelan/jexity-widget 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@evelan/jexity-widget 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@evelan/jexity-widget 可以商用吗?

@evelan/jexity-widget 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@evelan/jexity-widget 的下载量有多少?

根据 npm 官方下载统计,@evelan/jexity-widget 在 2026/10/14 – 2026/11/12 这 30 天内被下载 2,154 次,日均约 72 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@evelan/jexity-widget 还在维护吗?

最新版本 0.35.0 发布于 2026/11/13(1 天前);至今共发布 85 个版本,其中过去 12 个月发布了 85 个;首次发布于 2026/03/04。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。