Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# jcode-ui examples

Cloneable starters for the React chat component library.

| Example | Port | Pattern |
|---------|------|---------|
| [jcode-ui-minimal](./jcode-ui-minimal) | 5177 | `createMockRuntime` only — fastest start |
| [jcode-ui-zustand](./jcode-ui-zustand) | 5178 | `createExternalStoreRuntime` + Zustand |

## Prerequisites

```bash
cd packages/jcode-ui-core && pnpm build
cd ../jcode-ui && pnpm build
```

Each example is a **standalone pnpm workspace** (`ignore-workspace` via `.npmrc`) using
`file:../../packages/jcode-ui`, so installs stay local and always link monorepo packages.

If pnpm blocks esbuild postinstall, the example `pnpm-workspace.yaml` already allows it.

## Docs

- https://www.j-code.net/chat-ui/docs
- https://www.j-code.net/chat-ui/docs/installation
- https://www.j-code.net/chat-ui/docs/guides/external-store
4 changes: 4 additions & 0 deletions examples/jcode-ui-minimal/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
node_modules
dist
pnpm-lock.yaml
*.tsbuildinfo
1 change: 1 addition & 0 deletions examples/jcode-ui-minimal/.npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ignore-workspace=true
30 changes: 30 additions & 0 deletions examples/jcode-ui-minimal/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# jcode-ui minimal example

Drop-in chat UI with **`createMockRuntime`** — no backend, no store library.

## Run

From the monorepo root (packages must be built first):

```bash
# once
cd packages/jcode-ui-core && pnpm build
cd ../jcode-ui && pnpm build

cd ../../examples/jcode-ui-minimal
pnpm install
pnpm dev
```

Open http://localhost:5177

## What it shows

- `Thread` + `ChatInput`
- Default tool registry (seeded with one `execute` card)
- Interactive send → fake assistant echo

## Next

- Wire a real store: see [`../jcode-ui-zustand`](../jcode-ui-zustand)
- Docs: https://www.j-code.net/chat-ui/docs
12 changes: 12 additions & 0 deletions examples/jcode-ui-minimal/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!doctype html>
<html lang="en" class="dark">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>jcode-ui minimal</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
24 changes: 24 additions & 0 deletions examples/jcode-ui-minimal/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"name": "jcode-ui-minimal",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc --noEmit && vite build",
"preview": "vite preview"
},
"dependencies": {
"jcode-ui": "file:../../packages/jcode-ui",
"jcode-ui-core": "file:../../packages/jcode-ui-core",
"react": "^18.3.1",
"react-dom": "^18.3.1"
},
"devDependencies": {
"@types/react": "^18.3.18",
"@types/react-dom": "^18.3.5",
"@vitejs/plugin-react": "^4.3.4",
"typescript": "^5.7.2",
"vite": "^6.0.7"
}
}
7 changes: 7 additions & 0 deletions examples/jcode-ui-minimal/pnpm-workspace.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Standalone example (not part of the monorepo workspace).
packages:
- '.'
allowBuilds:
esbuild: true
onlyBuiltDependencies:
- esbuild
116 changes: 116 additions & 0 deletions examples/jcode-ui-minimal/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/**
* Minimal jcode-ui example — mock runtime only, no backend.
*
* pnpm install
* pnpm dev
*/

import { useMemo } from 'react'
import {
RuntimeProvider,
ToolRegistryProvider,
createDefaultToolRegistry,
createMockRuntime,
Thread,
ChatInput,
} from 'jcode-ui'
import type { ThreadItem } from 'jcode-ui-core'

function seed(runtime: ReturnType<typeof createMockRuntime>) {
let seq = 0
const push = (item: Omit<ThreadItem, 'seq'> & { seq?: number }) => {
runtime.push({ ...item, seq: ++seq } as ThreadItem)
}

push({
kind: 'message',
data: {
id: 'u1',
role: 'user',
content: 'Show me a minimal jcode-ui thread.',
timestamp: Date.now(),
},
})
push({
kind: 'message',
data: {
id: 'a1',
role: 'assistant',
content:
'This example uses `createMockRuntime` — no backend. Type below; actions are recorded in `runtime.calls`.',
timestamp: Date.now(),
durationMs: 800,
},
})
push({
kind: 'tool',
data: {
id: 't1',
name: 'execute',
args: JSON.stringify({ command: 'echo hello' }),
status: 'done',
timestamp: Date.now(),
output: 'hello\n',
displayInfo: { title: 'execute', subtitle: 'echo hello' },
},
})
}

export function App() {
const registry = useMemo(() => createDefaultToolRegistry(), [])
const runtime = useMemo(() => {
const rt = createMockRuntime({
actions: {
sendMessage: (text) => {
const items = rt.getState().items
const seq = items.reduce((m, i) => Math.max(m, i.seq), 0) + 1
rt.push({
kind: 'message',
seq,
data: {
id: `u_${Date.now()}`,
role: 'user',
content: text,
timestamp: Date.now(),
},
})
// Fake assistant reply
window.setTimeout(() => {
const s2 = rt.getState().items.reduce((m, i) => Math.max(m, i.seq), 0) + 1
rt.push({
kind: 'message',
seq: s2,
data: {
id: `a_${Date.now()}`,
role: 'assistant',
content: `Echo: ${text}`,
timestamp: Date.now(),
},
})
}, 400)
},
},
})
seed(rt)
return rt
}, [])

return (
<div className="app">
<header className="app-header">
<strong>jcode-ui</strong>
<span>minimal · mock runtime</span>
</header>
<RuntimeProvider runtime={runtime}>
<ToolRegistryProvider registry={registry}>
<main className="app-main">
<Thread virtualize={false} overscanBottom={8} />
</main>
<footer className="app-footer">
<ChatInput placeholder="Say something…" showContextBar={false} />
</footer>
</ToolRegistryProvider>
</RuntimeProvider>
</div>
)
}
11 changes: 11 additions & 0 deletions examples/jcode-ui-minimal/src/main.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import { App } from './App'
import 'jcode-ui/styles.css'
import './styles.css'

createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
</StrictMode>,
)
45 changes: 45 additions & 0 deletions examples/jcode-ui-minimal/src/styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
* {
box-sizing: border-box;
}
html,
body,
#root {
height: 100%;
margin: 0;
}
body {
font-family: system-ui, -apple-system, sans-serif;
background: #0d0d0d;
color: #e4e4e7;
}

.app {
height: 100%;
display: flex;
flex-direction: column;
}

.app-header {
display: flex;
align-items: baseline;
gap: 12px;
padding: 12px 16px;
border-bottom: 1px solid #2e2e2e;
font-size: 14px;
}

.app-header span {
color: #888;
font-size: 12px;
}

.app-main {
flex: 1;
min-height: 0;
overflow: hidden;
}

.app-footer {
border-top: 1px solid #2e2e2e;
padding: 8px 12px 12px;
}
14 changes: 14 additions & 0 deletions examples/jcode-ui-minimal/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"compilerOptions": {
"target": "ES2022",
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
"moduleResolution": "bundler",
"jsx": "react-jsx",
"strict": true,
"noEmit": true,
"isolatedModules": true
},
"include": ["src"]
}
12 changes: 12 additions & 0 deletions examples/jcode-ui-minimal/tsconfig.node.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"compilerOptions": {
"target": "ES2022",
"lib": ["ES2023"],
"module": "ESNext",
"skipLibCheck": true,
"moduleResolution": "bundler",
"strict": true,
"noEmit": true
},
"include": ["vite.config.ts"]
}
7 changes: 7 additions & 0 deletions examples/jcode-ui-minimal/vite.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

export default defineConfig({
plugins: [react()],
server: { port: 5177 },
})
4 changes: 4 additions & 0 deletions examples/jcode-ui-zustand/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
node_modules
dist
pnpm-lock.yaml
*.tsbuildinfo
1 change: 1 addition & 0 deletions examples/jcode-ui-zustand/.npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ignore-workspace=true
42 changes: 42 additions & 0 deletions examples/jcode-ui-zustand/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# jcode-ui + Zustand

Production-shaped integration: **your store owns the timeline**, jcode-ui renders via `createExternalStoreRuntime`.

## Run

```bash
# build packages once
cd packages/jcode-ui-core && pnpm build
cd ../jcode-ui && pnpm build

cd ../../examples/jcode-ui-zustand
pnpm install
pnpm dev
```

Open http://localhost:5178

## Pattern

```ts
const runtime = createExternalStoreRuntime({
store: useChatStore, // Zustand: getState + subscribe
select: (s) => ({
items: s.items,
isRunning: s.isRunning,
}),
actions: {
sendMessage: (text) => { /* mutate store + call API */ },
// …
},
})
```

See also: [External store guide](https://www.j-code.net/chat-ui/docs/guides/external-store)

## Files

| File | Role |
|------|------|
| `src/store.ts` | Zustand timeline + mutations |
| `src/App.tsx` | Runtime wiring + UI |
12 changes: 12 additions & 0 deletions examples/jcode-ui-zustand/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!doctype html>
<html lang="en" class="dark">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>jcode-ui + Zustand</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
Loading
Loading