codicent-app-sdk 0.4.22 → 0.4.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +556 -556
  2. package/dist/cjs/components/ListView.d.ts.map +1 -1
  3. package/dist/cjs/components/ListView.js +1 -1
  4. package/dist/cjs/components/MessageItem.d.ts.map +1 -1
  5. package/dist/cjs/components/MessageItem.js +1 -1
  6. package/dist/cjs/components/RecordModal.d.ts.map +1 -1
  7. package/dist/cjs/components/RecordModal.js +1 -1
  8. package/dist/cjs/config/index.d.ts +0 -1
  9. package/dist/cjs/config/index.d.ts.map +1 -1
  10. package/dist/cjs/config/index.js +1 -1
  11. package/dist/cjs/hooks/useCodicentApp.d.ts.map +1 -1
  12. package/dist/cjs/hooks/useCodicentApp.js +1 -1
  13. package/dist/cjs/hooks/useRealtimeVoiceAI.d.ts +1 -1
  14. package/dist/cjs/hooks/useRealtimeVoiceAI.d.ts.map +1 -1
  15. package/dist/cjs/hooks/useRealtimeVoiceAI.js +1 -1
  16. package/dist/cjs/services/codicent.d.ts.map +1 -1
  17. package/dist/cjs/services/codicent.js +1 -1
  18. package/dist/cjs/types/index.d.ts +2 -0
  19. package/dist/cjs/types/index.d.ts.map +1 -1
  20. package/dist/esm/components/ListView.d.ts.map +1 -1
  21. package/dist/esm/components/ListView.js +1 -1
  22. package/dist/esm/components/MessageItem.d.ts.map +1 -1
  23. package/dist/esm/components/MessageItem.js +1 -1
  24. package/dist/esm/components/RecordModal.d.ts.map +1 -1
  25. package/dist/esm/components/RecordModal.js +1 -1
  26. package/dist/esm/config/index.d.ts +0 -1
  27. package/dist/esm/config/index.d.ts.map +1 -1
  28. package/dist/esm/config/index.js +1 -1
  29. package/dist/esm/hooks/useCodicentApp.d.ts.map +1 -1
  30. package/dist/esm/hooks/useCodicentApp.js +1 -1
  31. package/dist/esm/hooks/useRealtimeVoiceAI.d.ts +1 -1
  32. package/dist/esm/hooks/useRealtimeVoiceAI.d.ts.map +1 -1
  33. package/dist/esm/hooks/useRealtimeVoiceAI.js +1 -1
  34. package/dist/esm/services/codicent.d.ts.map +1 -1
  35. package/dist/esm/services/codicent.js +1 -1
  36. package/dist/esm/types/index.d.ts +2 -0
  37. package/dist/esm/types/index.d.ts.map +1 -1
  38. package/dist/index.d.ts +3 -2
  39. package/package.json +1 -1
package/README.md CHANGED
@@ -1,556 +1,556 @@
1
- # codicent-app-sdk
2
-
3
- React SDK for building AI-powered, multi-tenant white-label applications on top of the Codicent platform.
4
-
5
- **Current version:** 0.4.16 · **License:** MIT · **Peer deps:** React ≥16.8, Fluent UI v9, react-router-dom v6
6
-
7
- ---
8
-
9
- ## What it provides
10
-
11
- - `CodicentService` — data CRUD, chat, file upload, token management
12
- - React components — `Page`, `Content`, `Chat`, `ListView`, `Markdown`, `UploadFile`, and ~25 more
13
- - Pages — `AppFrame`, `Chat`, `Compose`, `Sales`, `Search`, `Snap`, `Login`, `Logout`, and more
14
- - Hooks — `useCodicentApp`, `useLocalization`, `useChat`, `useRealtimeVoiceAI`, `useAuthState`, and more
15
- - Global config — `initCodicentApp()` initializer consumed by all SDK components
16
- - Full TypeScript types
17
-
18
- This SDK is the primary dependency of `codicentapp/` (the modern white-label Vite app) in the monorepo. See `codicentapp/` for a complete real-world usage reference.
19
-
20
- ---
21
-
22
- ## Installation
23
-
24
- ### From npm
25
- ```bash
26
- npm install codicent-app-sdk
27
- ```
28
-
29
- ### Monorepo local dependency (recommended during development)
30
- In your app's `package.json`:
31
- ```json
32
- "dependencies": {
33
- "codicent-app-sdk": "file:../codicent-app-sdk"
34
- }
35
- ```
36
- Then `npm install` in your app.
37
-
38
- ### Peer dependencies
39
- Install these alongside the SDK:
40
- ```bash
41
- npm install react react-dom react-router-dom @fluentui/react-components mermaid
42
- ```
43
-
44
- ---
45
-
46
- ## Usage
47
-
48
- ### 1. Initialize the SDK (once, at app startup)
49
-
50
- Call `initCodicentApp()` before rendering your React tree. It stores config globally so all SDK components and hooks can read it.
51
-
52
- ```tsx
53
- // main.tsx
54
- import { initCodicentApp } from 'codicent-app-sdk';
55
- import { translations } from './services/translationService';
56
- import { getAppConfig } from './appconfig';
57
-
58
- initCodicentApp({
59
- API_BASE_URL: 'https://codicent.com/',
60
- APP_NAME: 'my-crm',
61
- APP_PREFIX: 'mycrm',
62
- USER_PREFIX: 'users',
63
- APP_CONFIG: getAppConfig(), // buttons, listDefinitions, chatInstructions
64
- TRANSLATIONS: translations, // { sv: {...}, en: {...}, ... }
65
- USE_REALTIME_SESSION_ENDPOINT: true,
66
- });
67
- ```
68
-
69
- ### 2. Wrap with Auth0 and FluentProvider
70
-
71
- The SDK components depend on these providers being present in the React tree:
72
-
73
- ```tsx
74
- // main.tsx (continued)
75
- import { Auth0Provider } from '@auth0/auth0-react';
76
- import { FluentProvider, webLightTheme } from '@fluentui/react-components';
77
- import { createRoot } from 'react-dom/client';
78
-
79
- createRoot(document.getElementById('root')!).render(
80
- <Auth0Provider domain={AUTH0_DOMAIN} clientId={AUTH0_CLIENT_ID} authorizationParams={{ redirect_uri: window.location.origin }}>
81
- <FluentProvider theme={webLightTheme}>
82
- <App />
83
- </FluentProvider>
84
- </Auth0Provider>
85
- );
86
- ```
87
-
88
- ### 3. Use `useCodicentApp()` in your root App component
89
-
90
- ```tsx
91
- // App.tsx
92
- import { useCodicentApp } from 'codicent-app-sdk';
93
- import { useAuth0 } from '@auth0/auth0-react';
94
- import { HashRouter, Routes, Route } from 'react-router-dom';
95
- import { Chat } from 'codicent-app-sdk';
96
- import { ListPage } from './pages/ListPage';
97
-
98
- export default function App() {
99
- const auth0 = useAuth0();
100
- const state = useCodicentApp({ auth0 });
101
-
102
- if (!auth0.isAuthenticated) {
103
- auth0.loginWithRedirect();
104
- return null;
105
- }
106
-
107
- return (
108
- <HashRouter>
109
- <Routes>
110
- <Route path="/chat" element={<Chat state={state} title="Chat" />} />
111
- <Route path="/list" element={<ListPage state={state} />} />
112
- </Routes>
113
- </HashRouter>
114
- );
115
- }
116
- ```
117
-
118
- ### 4. Build pages using SDK components
119
-
120
- ```tsx
121
- // pages/ListPage.tsx
122
- import { Page, Content, ListView, useLocalization, CodicentAppState, DataMessage } from 'codicent-app-sdk';
123
- import { useEffect, useState } from 'react';
124
- import { APP_CONFIG } from '../appconfig';
125
- import { APP_BUTTONS } from '../constants';
126
-
127
- export const ListPage: React.FC<{ state: CodicentAppState }> = ({ state }) => {
128
- const { service } = state;
129
- const { t } = useLocalization();
130
- const [data, setData] = useState<DataMessage[]>([]);
131
-
132
- useEffect(() => {
133
- service.readDataMessages('customer2').then(setData);
134
- }, [service]);
135
-
136
- const columns = APP_CONFIG.apps[APP_BUTTONS].listDefinitions['customer2'];
137
-
138
- return (
139
- <Page title={t('Kunder')}>
140
- <Content>
141
- <ListView data={data} columns={columns} />
142
- </Content>
143
- </Page>
144
- );
145
- };
146
- ```
147
-
148
- ---
149
-
150
- ## `initCodicentApp()` configuration options
151
-
152
- **Required:**
153
-
154
- | Key | Type | Description |
155
- |-----|------|-------------|
156
- | `API_BASE_URL` | `string` | Codicent backend URL, e.g. `"https://codicent.com/"` |
157
- | `APP_NAME` | `string` | Application identifier |
158
-
159
- **Common optional:**
160
-
161
- | Key | Type | Description |
162
- |-----|------|-------------|
163
- | `APP_PREFIX` | `string` | URL/namespace prefix for this app |
164
- | `USER_PREFIX` | `string` | User namespace prefix (default: `"users"`) |
165
- | `APP_CONFIG` | `AppConfig` | Per-app config: buttons, listDefinitions, chatInstructions |
166
- | `TRANSLATIONS` | `object` | i18n map `{ sv: {...}, en: {...} }`. Swedish strings are used as keys. |
167
- | `DEFAULT_LANGUAGE` | `string` | Default language code (e.g. `"sv"`, `"en"`) |
168
- | `USE_REALTIME_SESSION_ENDPOINT` | `boolean` | Use secure backend session token for voice AI (default: `true`) |
169
- | `REALTIME_VOICE_MODEL` | `string` | Voice model: `"alloy"`, `"shimmer"`, or `"echo"` (default: `"alloy"`) |
170
- | `SUBSCRIPTION_NEEDED` | `boolean` | Redirect to purchase page if no active subscription |
171
- | `BUTTON_BORDER_RADIUS` | `string` | CSS border-radius for nav buttons |
172
- | `BUTTON_BACKGROUND_COLOR` | `string` | Background color for nav buttons |
173
-
174
- **Compose page GPS options:**
175
-
176
- | Key | Value | Behaviour |
177
- |-----|-------|-----------|
178
- | `COMPOSE_HIDE_LOCATION` | `"true"` | Hides GPS toggle button entirely |
179
- | `COMPOSE_HIDE_LOCATION` | `"false"` (default) | Shows GPS toggle; users opt-in per post |
180
- | `COMPOSE_AUTO_LOCATION` | `"true"` | Captures GPS automatically on page open |
181
-
182
- When a location is attached, `#gps(lat,lon)` is appended to message content — queryable via tag search and readable by AI.
183
-
184
- ---
185
-
186
- ## API Reference
187
-
188
- ### `CodicentService`
189
-
190
- The main service class for all data and chat operations. Access it via `state.service` from `useCodicentApp()`.
191
-
192
- **Data message CRUD:**
193
- ```ts
194
- createDataMessage(tag: string, data: object, codicent?: string): Promise<string>
195
-
196
- readDataMessages(
197
- tag: string,
198
- search?: string,
199
- codicent?: string,
200
- start?: number,
201
- length?: number,
202
- afterTimestamp?: string,
203
- beforeTimestamp?: string,
204
- dataFilters?: Record<string, string>
205
- ): Promise<DataMessage[]>
206
-
207
- readOneDataMessage(id: string): Promise<DataMessage | null>
208
-
209
- updateDataMessage(id: string, data: object, codicent?: string): Promise<string>
210
-
211
- deleteDataMessage(id: string, codicent?: string): Promise<string>
212
-
213
- getSchema(tag: string, codicent?: string): Promise<object | null>
214
- ```
215
-
216
- **Chat and messages:**
217
- ```ts
218
- sendMessage(message: string, parentId?: string, codicent?: string): Promise<Message>
219
- chat(message: string, messageId?: string, codicent?: string): Promise<Message>
220
- getMessages(tags: string[], codicent?: string, length?: number): Promise<Message[]>
221
- getMessagesFast(tags: string[], search?: string, length?: number, publicCodicent?: string, codicent?: string, start?: number): Promise<Message[]>
222
- ```
223
-
224
- **Files:**
225
- ```ts
226
- uploadFile(filename: string, formData: FormData): Promise<string>
227
- getFileInfo(fileId: string): Promise<FileInfo>
228
- static getImageUrl(fileId: string, width: number): string
229
- static getFileUrl(fileId: string, extension?: string): string
230
- ```
231
-
232
- **Auth and tokens:**
233
- ```ts
234
- getToken(): string
235
- generateApiToken(expires?: Date, forUserNickname?: string): Promise<string>
236
- ```
237
-
238
- ---
239
-
240
- ### `useCodicentApp(options)`
241
-
242
- Core hook that bootstraps app state, authentication, and the `CodicentService` instance.
243
-
244
- ```ts
245
- const state = useCodicentApp({
246
- auth0: useAuth0(), // Required: Auth0 hook result
247
- toolsConfig?: object, // Optional: custom AI tool handlers
248
- authOptions?: object, // Optional: override auth behaviour
249
- });
250
- ```
251
-
252
- Returns `CodicentAppState`:
253
- ```ts
254
- {
255
- service: CodicentService; // Use for all data/chat operations
256
- context: StateContext; // Project nickname, user info
257
- auth: UseAuthState;
258
- voice?: RealtimeVoice; // Voice AI connection (when active)
259
- audio: AudioRecorderState;
260
- stateMachine: AppStateMachine;
261
- state: string; // Current state machine state
262
- nickname: string; // Active project nickname
263
- error: string;
264
- isBusy: () => boolean;
265
- html: string; // AI-generated HTML output
266
- setHtml: (html: string) => void;
267
- }
268
- ```
269
-
270
- ### 5. Use `DataMessagePicker` for tagged record lookup
271
-
272
- ```tsx
273
- import { DataMessagePicker } from 'codicent-app-sdk';
274
-
275
- <DataMessagePicker
276
- service={state.service}
277
- tag="customer2"
278
- placeholder="Search customers"
279
- primaryKey="Company Name"
280
- displayKeys={["Company Name", "City"]}
281
- secondaryKeys={["Customer Number", "City"]}
282
- searchKeys={["Company Name", "Customer Number", "City"]}
283
- onSelect={(selection) => {
284
- console.log('Selected customer', selection.id, selection.data);
285
- }}
286
- />
287
- ```
288
-
289
- The picker debounces lookups through `readDataMessages(...)`, shows compact autosuggest results, and returns the raw `DataMessage` plus a normalized selection payload.
290
-
291
- ---
292
-
293
- ### `useLocalization()`
294
-
295
- ```ts
296
- const { t, tAsync, getLanguageInfo } = useLocalization();
297
-
298
- t('Kunder') // → "Customers" (in English)
299
- await tAsync('Kunder') // → API-backed translation with fallback
300
- getLanguageInfo() // → { code: 'en', name: 'English', ... }
301
- ```
302
-
303
- Swedish strings are used as keys. Pass your translation maps via `TRANSLATIONS` in `initCodicentApp()`.
304
-
305
- ---
306
-
307
- ### Components
308
-
309
- #### `Page`
310
- Full-screen layout wrapper with optional header and footer.
311
- ```tsx
312
- <Page title="Customers" hideFooter={false} audio={state.audio} voice={state.voice}>
313
- <Content>...</Content>
314
- </Page>
315
- ```
316
-
317
- #### `Content`
318
- Flex content container — use inside `Page`.
319
- ```tsx
320
- <Content>
321
- <ListView data={data} columns={columns} />
322
- </Content>
323
- ```
324
-
325
- #### `Chat` (page)
326
- Full chat UI with message history, input, typing indicators, file uploads.
327
- ```tsx
328
- <Chat
329
- state={state}
330
- title="AI Assistant"
331
- codicent="my-project" // Optional: override which project to chat with
332
- welcomeMessage="Hi!" // Optional: shown when no messages exist
333
- hideFooter={false}
334
- />
335
- ```
336
-
337
- #### `ListView`
338
- Tabular data view with sorting, filtering, and column actions.
339
- ```tsx
340
- import { ListView } from 'codicent-app-sdk';
341
-
342
- <ListView
343
- data={dataMessages}
344
- columns={[
345
- { key: 'name', title: 'Name', filterable: true },
346
- { key: ['offer_number', 'offerNumber'], title: 'Quote #', filterable: true },
347
- { key: 'grand_total', title: 'Total', format: formatNumber },
348
- { key: 'pdf', title: 'PDF', type: 'file' },
349
- ]}
350
- onSelect={(item) => navigate(`/chat?id=${item.originalMessageId}`)}
351
- />
352
- ```
353
-
354
- Column definition options:
355
- - `key: string | string[]` — JSON field name; array = fallback chain
356
- - `format: (value) => string` — display transformer
357
- - `filterable: true` — per-column text filter
358
- - `type: "file"` — renders download link
359
- - `type: "checkbox"` — renders checkbox
360
- - `action` — icon button with click handler
361
-
362
- #### `AppFrame`
363
- Embeds an external URL in an iframe within the page layout.
364
- ```tsx
365
- <AppFrame src="https://example.com/embed" title="External view" showFooter={true} />
366
- ```
367
-
368
- #### `Markdown`
369
- Renders markdown content including GFM and Mermaid diagrams.
370
- ```tsx
371
- <Markdown content={message.content} />
372
- ```
373
-
374
- #### `UploadFile`
375
- File upload UI with progress.
376
- ```tsx
377
- <UploadFile service={state.service} codicent="my-project" />
378
- ```
379
-
380
- ---
381
-
382
- ### Hooks summary
383
-
384
- | Hook | Purpose |
385
- |------|---------|
386
- | `useCodicentApp()` | Core app state, service, auth |
387
- | `useLocalization()` | i18n translation |
388
- | `useChat(service)` | Chat message state and send |
389
- | `useRealtimeVoiceAI(options)` | Real-time voice AI connection |
390
- | `useAuthState(auth0)` | Auth lifecycle and token |
391
- | `useTheme()` | App theme/branding |
392
- | `useToaster()` | Toast notification helpers |
393
- | `useStateWithLocalStorage()` | Persistent local state |
394
- | `useAudioRecorder()` | Microphone recording |
395
- | `useTemplateVariables()` | Template string utilities |
396
- | `useTools()` | AI tool handler registration |
397
- | `useEmbeddings()` | Vector embedding operations |
398
-
399
- ---
400
-
401
- ## TypeScript types
402
-
403
- ```tsx
404
- import type {
405
- CodicentAppState,
406
- ButtonConfig,
407
- ColumnDefinition,
408
- ListDefinitions,
409
- DataMessage,
410
- Message,
411
- } from 'codicent-app-sdk';
412
- ```
413
-
414
- **`ButtonConfig`** — navigation button with optional RBAC claims:
415
- ```ts
416
- {
417
- title: string;
418
- url: string; // "#/list?tag=customer2", "voice:...", "mailto:...", etc.
419
- claim?: string; // Show only if user has this claim
420
- notClaim?: string; // Hide if user has this claim
421
- subtitle?: string;
422
- options?: ButtonConfig[];
423
- }
424
- ```
425
-
426
- **`DataMessage`** — result from `readDataMessages()`:
427
- ```ts
428
- {
429
- id: string;
430
- originalMessageId: string; // Stable ID across updates — use this for references/tags
431
- content: string; // Raw: "@project #data #tag\n{json}"
432
- data: Record<string, unknown>; // Parsed JSON payload
433
- tags: string[];
434
- createdAt: string;
435
- }
436
- ```
437
-
438
- ---
439
-
440
- ## Alternative: `createCodicentApp()` factory
441
-
442
- For standalone deployments where you do not control the React entry point, `createCodicentApp()` wraps the full initialization (including Auth0Provider, FluentProvider, HashRouter) and renders to a DOM element:
443
-
444
- ```tsx
445
- import { createCodicentApp } from 'codicent-app-sdk';
446
-
447
- const app = createCodicentApp({
448
- name: 'My CRM',
449
- apiBaseUrl: 'https://codicent.com/',
450
- auth0: { domain: '...', clientId: '...' },
451
- buttons: [
452
- { title: 'Customers', url: './#/list?tag=customer2' },
453
- { title: 'Chat', url: './#/chat' },
454
- ],
455
- listDefinitions: {
456
- customer2: [
457
- { key: 'name', title: 'Name', filterable: true },
458
- { key: 'email', title: 'Email' },
459
- ],
460
- },
461
- chatInstructions: 'You are a helpful CRM assistant.',
462
- modules: { sales: true, voice: true },
463
- });
464
-
465
- app.render('#root');
466
- // app.unmount();
467
- // app.getConfig();
468
- ```
469
-
470
- The `codicentapp/` reference implementation uses `initCodicentApp()` + manual React tree setup, which gives more control over routing and layout. `createCodicentApp()` is appropriate for simpler or standalone deployments.
471
-
472
- ---
473
-
474
- ## Local development workflow
475
-
476
- ### Build the SDK
477
-
478
- ```bash
479
- cd codicent-app-sdk
480
- npm install
481
- npm run build # Output: dist/cjs/, dist/esm/, dist/index.d.ts
482
- npm run dev # Watch mode
483
- ```
484
-
485
- ### Test in codicentapp (file: reference)
486
-
487
- In `codicentapp/package.json`:
488
- ```json
489
- "codicent-app-sdk": "file:../codicent-app-sdk"
490
- ```
491
- Then:
492
- ```bash
493
- cd codicentapp
494
- npm install
495
- npm run build
496
- ```
497
-
498
- ### Test with npm link
499
-
500
- ```bash
501
- # In SDK directory
502
- npm link
503
-
504
- # In your app directory
505
- npm link codicent-app-sdk
506
- ```
507
-
508
- To restore npm version:
509
- ```bash
510
- npm uninstall codicent-app-sdk
511
- npm install codicent-app-sdk
512
- ```
513
-
514
- ### Build output layout
515
-
516
- ```
517
- dist/
518
- cjs/ CommonJS modules
519
- esm/ ES modules
520
- index.d.ts TypeScript definitions
521
- ```
522
-
523
- ### Release
524
-
525
- ```bash
526
- npm version patch # or minor / major
527
- npm run build
528
- npm publish --access public
529
- ```
530
-
531
- ---
532
-
533
- ## Debugging
534
-
535
- The SDK emits `codicent-log` custom events on `window`:
536
-
537
- ```tsx
538
- window.addEventListener('codicent-log', (event: CustomEvent) => {
539
- console.log(event.detail); // { level, message, context }
540
- });
541
- ```
542
-
543
- ---
544
-
545
- ## Related
546
-
547
- - `codicentapp/` — primary consumer; complete reference implementation
548
- - `codicent-api-client/` — framework-agnostic fetch-based API client (shared with web components)
549
- - `codicent-components/` — zero-build Web Components library using the same Codicent API
550
- - [Voice Upgrade Guide](VOICE_UPGRADE_GUIDE.md) — real-time voice AI setup and security
551
-
552
- ---
553
-
554
- ## License
555
-
556
- MIT © Codicent Inside AB
1
+ # codicent-app-sdk
2
+
3
+ React SDK for building AI-powered, multi-tenant white-label applications on top of the Codicent platform.
4
+
5
+ **Current version:** 0.4.16 · **License:** MIT · **Peer deps:** React ≥16.8, Fluent UI v9, react-router-dom v6
6
+
7
+ ---
8
+
9
+ ## What it provides
10
+
11
+ - `CodicentService` — data CRUD, chat, file upload, token management
12
+ - React components — `Page`, `Content`, `Chat`, `ListView`, `Markdown`, `UploadFile`, and ~25 more
13
+ - Pages — `AppFrame`, `Chat`, `Compose`, `Sales`, `Search`, `Snap`, `Login`, `Logout`, and more
14
+ - Hooks — `useCodicentApp`, `useLocalization`, `useChat`, `useRealtimeVoiceAI`, `useAuthState`, and more
15
+ - Global config — `initCodicentApp()` initializer consumed by all SDK components
16
+ - Full TypeScript types
17
+
18
+ This SDK is the primary dependency of `codicentapp/` (the modern white-label Vite app) in the monorepo. See `codicentapp/` for a complete real-world usage reference.
19
+
20
+ ---
21
+
22
+ ## Installation
23
+
24
+ ### From npm
25
+ ```bash
26
+ npm install codicent-app-sdk
27
+ ```
28
+
29
+ ### Monorepo local dependency (recommended during development)
30
+ In your app's `package.json`:
31
+ ```json
32
+ "dependencies": {
33
+ "codicent-app-sdk": "file:../codicent-app-sdk"
34
+ }
35
+ ```
36
+ Then `npm install` in your app.
37
+
38
+ ### Peer dependencies
39
+ Install these alongside the SDK:
40
+ ```bash
41
+ npm install react react-dom react-router-dom @fluentui/react-components mermaid
42
+ ```
43
+
44
+ ---
45
+
46
+ ## Usage
47
+
48
+ ### 1. Initialize the SDK (once, at app startup)
49
+
50
+ Call `initCodicentApp()` before rendering your React tree. It stores config globally so all SDK components and hooks can read it.
51
+
52
+ ```tsx
53
+ // main.tsx
54
+ import { initCodicentApp } from 'codicent-app-sdk';
55
+ import { translations } from './services/translationService';
56
+ import { getAppConfig } from './appconfig';
57
+
58
+ initCodicentApp({
59
+ API_BASE_URL: 'https://codicent.com/',
60
+ APP_NAME: 'my-crm',
61
+ APP_PREFIX: 'mycrm',
62
+ USER_PREFIX: 'users',
63
+ APP_CONFIG: getAppConfig(), // buttons, listDefinitions, chatInstructions
64
+ TRANSLATIONS: translations, // { sv: {...}, en: {...}, ... }
65
+ USE_REALTIME_SESSION_ENDPOINT: true,
66
+ });
67
+ ```
68
+
69
+ ### 2. Wrap with Auth0 and FluentProvider
70
+
71
+ The SDK components depend on these providers being present in the React tree:
72
+
73
+ ```tsx
74
+ // main.tsx (continued)
75
+ import { Auth0Provider } from '@auth0/auth0-react';
76
+ import { FluentProvider, webLightTheme } from '@fluentui/react-components';
77
+ import { createRoot } from 'react-dom/client';
78
+
79
+ createRoot(document.getElementById('root')!).render(
80
+ <Auth0Provider domain={AUTH0_DOMAIN} clientId={AUTH0_CLIENT_ID} authorizationParams={{ redirect_uri: window.location.origin }}>
81
+ <FluentProvider theme={webLightTheme}>
82
+ <App />
83
+ </FluentProvider>
84
+ </Auth0Provider>
85
+ );
86
+ ```
87
+
88
+ ### 3. Use `useCodicentApp()` in your root App component
89
+
90
+ ```tsx
91
+ // App.tsx
92
+ import { useCodicentApp } from 'codicent-app-sdk';
93
+ import { useAuth0 } from '@auth0/auth0-react';
94
+ import { HashRouter, Routes, Route } from 'react-router-dom';
95
+ import { Chat } from 'codicent-app-sdk';
96
+ import { ListPage } from './pages/ListPage';
97
+
98
+ export default function App() {
99
+ const auth0 = useAuth0();
100
+ const state = useCodicentApp({ auth0 });
101
+
102
+ if (!auth0.isAuthenticated) {
103
+ auth0.loginWithRedirect();
104
+ return null;
105
+ }
106
+
107
+ return (
108
+ <HashRouter>
109
+ <Routes>
110
+ <Route path="/chat" element={<Chat state={state} title="Chat" />} />
111
+ <Route path="/list" element={<ListPage state={state} />} />
112
+ </Routes>
113
+ </HashRouter>
114
+ );
115
+ }
116
+ ```
117
+
118
+ ### 4. Build pages using SDK components
119
+
120
+ ```tsx
121
+ // pages/ListPage.tsx
122
+ import { Page, Content, ListView, useLocalization, CodicentAppState, DataMessage } from 'codicent-app-sdk';
123
+ import { useEffect, useState } from 'react';
124
+ import { APP_CONFIG } from '../appconfig';
125
+ import { APP_BUTTONS } from '../constants';
126
+
127
+ export const ListPage: React.FC<{ state: CodicentAppState }> = ({ state }) => {
128
+ const { service } = state;
129
+ const { t } = useLocalization();
130
+ const [data, setData] = useState<DataMessage[]>([]);
131
+
132
+ useEffect(() => {
133
+ service.readDataMessages('customer2').then(setData);
134
+ }, [service]);
135
+
136
+ const columns = APP_CONFIG.apps[APP_BUTTONS].listDefinitions['customer2'];
137
+
138
+ return (
139
+ <Page title={t('Kunder')}>
140
+ <Content>
141
+ <ListView data={data} columns={columns} />
142
+ </Content>
143
+ </Page>
144
+ );
145
+ };
146
+ ```
147
+
148
+ ---
149
+
150
+ ## `initCodicentApp()` configuration options
151
+
152
+ **Required:**
153
+
154
+ | Key | Type | Description |
155
+ |-----|------|-------------|
156
+ | `API_BASE_URL` | `string` | Codicent backend URL, e.g. `"https://codicent.com/"` |
157
+ | `APP_NAME` | `string` | Application identifier |
158
+
159
+ **Common optional:**
160
+
161
+ | Key | Type | Description |
162
+ |-----|------|-------------|
163
+ | `APP_PREFIX` | `string` | URL/namespace prefix for this app |
164
+ | `USER_PREFIX` | `string` | User namespace prefix (default: `"users"`) |
165
+ | `APP_CONFIG` | `AppConfig` | Per-app config: buttons, listDefinitions, chatInstructions |
166
+ | `TRANSLATIONS` | `object` | i18n map `{ sv: {...}, en: {...} }`. Swedish strings are used as keys. |
167
+ | `DEFAULT_LANGUAGE` | `string` | Default language code (e.g. `"sv"`, `"en"`) |
168
+ | `USE_REALTIME_SESSION_ENDPOINT` | `boolean` | Use secure backend session token for voice AI (default: `true`) |
169
+ | `REALTIME_VOICE_MODEL` | `string` | Voice model: `"alloy"`, `"shimmer"`, or `"echo"` (default: `"alloy"`) |
170
+ | `SUBSCRIPTION_NEEDED` | `boolean` | Redirect to purchase page if no active subscription |
171
+ | `BUTTON_BORDER_RADIUS` | `string` | CSS border-radius for nav buttons |
172
+ | `BUTTON_BACKGROUND_COLOR` | `string` | Background color for nav buttons |
173
+
174
+ **Compose page GPS options:**
175
+
176
+ | Key | Value | Behaviour |
177
+ |-----|-------|-----------|
178
+ | `COMPOSE_HIDE_LOCATION` | `"true"` | Hides GPS toggle button entirely |
179
+ | `COMPOSE_HIDE_LOCATION` | `"false"` (default) | Shows GPS toggle; users opt-in per post |
180
+ | `COMPOSE_AUTO_LOCATION` | `"true"` | Captures GPS automatically on page open |
181
+
182
+ When a location is attached, `#gps(lat,lon)` is appended to message content — queryable via tag search and readable by AI.
183
+
184
+ ---
185
+
186
+ ## API Reference
187
+
188
+ ### `CodicentService`
189
+
190
+ The main service class for all data and chat operations. Access it via `state.service` from `useCodicentApp()`.
191
+
192
+ **Data message CRUD:**
193
+ ```ts
194
+ createDataMessage(tag: string, data: object, codicent?: string): Promise<string>
195
+
196
+ readDataMessages(
197
+ tag: string,
198
+ search?: string,
199
+ codicent?: string,
200
+ start?: number,
201
+ length?: number,
202
+ afterTimestamp?: string,
203
+ beforeTimestamp?: string,
204
+ dataFilters?: Record<string, string>
205
+ ): Promise<DataMessage[]>
206
+
207
+ readOneDataMessage(id: string): Promise<DataMessage | null>
208
+
209
+ updateDataMessage(id: string, data: object, codicent?: string): Promise<string>
210
+
211
+ deleteDataMessage(id: string, codicent?: string): Promise<string>
212
+
213
+ getSchema(tag: string, codicent?: string): Promise<object | null>
214
+ ```
215
+
216
+ **Chat and messages:**
217
+ ```ts
218
+ sendMessage(message: string, parentId?: string, codicent?: string): Promise<Message>
219
+ chat(message: string, messageId?: string, codicent?: string): Promise<Message>
220
+ getMessages(tags: string[], codicent?: string, length?: number): Promise<Message[]>
221
+ getMessagesFast(tags: string[], search?: string, length?: number, publicCodicent?: string, codicent?: string, start?: number): Promise<Message[]>
222
+ ```
223
+
224
+ **Files:**
225
+ ```ts
226
+ uploadFile(filename: string, formData: FormData): Promise<string>
227
+ getFileInfo(fileId: string): Promise<FileInfo>
228
+ static getImageUrl(fileId: string, width: number): string
229
+ static getFileUrl(fileId: string, extension?: string): string
230
+ ```
231
+
232
+ **Auth and tokens:**
233
+ ```ts
234
+ getToken(): string
235
+ generateApiToken(expires?: Date, forUserNickname?: string): Promise<string>
236
+ ```
237
+
238
+ ---
239
+
240
+ ### `useCodicentApp(options)`
241
+
242
+ Core hook that bootstraps app state, authentication, and the `CodicentService` instance.
243
+
244
+ ```ts
245
+ const state = useCodicentApp({
246
+ auth0: useAuth0(), // Required: Auth0 hook result
247
+ toolsConfig?: object, // Optional: custom AI tool handlers
248
+ authOptions?: object, // Optional: override auth behaviour
249
+ });
250
+ ```
251
+
252
+ Returns `CodicentAppState`:
253
+ ```ts
254
+ {
255
+ service: CodicentService; // Use for all data/chat operations
256
+ context: StateContext; // Project nickname, user info
257
+ auth: UseAuthState;
258
+ voice?: RealtimeVoice; // Voice AI connection (when active)
259
+ audio: AudioRecorderState;
260
+ stateMachine: AppStateMachine;
261
+ state: string; // Current state machine state
262
+ nickname: string; // Active project nickname
263
+ error: string;
264
+ isBusy: () => boolean;
265
+ html: string; // AI-generated HTML output
266
+ setHtml: (html: string) => void;
267
+ }
268
+ ```
269
+
270
+ ### 5. Use `DataMessagePicker` for tagged record lookup
271
+
272
+ ```tsx
273
+ import { DataMessagePicker } from 'codicent-app-sdk';
274
+
275
+ <DataMessagePicker
276
+ service={state.service}
277
+ tag="customer2"
278
+ placeholder="Search customers"
279
+ primaryKey="Company Name"
280
+ displayKeys={["Company Name", "City"]}
281
+ secondaryKeys={["Customer Number", "City"]}
282
+ searchKeys={["Company Name", "Customer Number", "City"]}
283
+ onSelect={(selection) => {
284
+ console.log('Selected customer', selection.id, selection.data);
285
+ }}
286
+ />
287
+ ```
288
+
289
+ The picker debounces lookups through `readDataMessages(...)`, shows compact autosuggest results, and returns the raw `DataMessage` plus a normalized selection payload.
290
+
291
+ ---
292
+
293
+ ### `useLocalization()`
294
+
295
+ ```ts
296
+ const { t, tAsync, getLanguageInfo } = useLocalization();
297
+
298
+ t('Kunder') // → "Customers" (in English)
299
+ await tAsync('Kunder') // → API-backed translation with fallback
300
+ getLanguageInfo() // → { code: 'en', name: 'English', ... }
301
+ ```
302
+
303
+ Swedish strings are used as keys. Pass your translation maps via `TRANSLATIONS` in `initCodicentApp()`.
304
+
305
+ ---
306
+
307
+ ### Components
308
+
309
+ #### `Page`
310
+ Full-screen layout wrapper with optional header and footer.
311
+ ```tsx
312
+ <Page title="Customers" hideFooter={false} audio={state.audio} voice={state.voice}>
313
+ <Content>...</Content>
314
+ </Page>
315
+ ```
316
+
317
+ #### `Content`
318
+ Flex content container — use inside `Page`.
319
+ ```tsx
320
+ <Content>
321
+ <ListView data={data} columns={columns} />
322
+ </Content>
323
+ ```
324
+
325
+ #### `Chat` (page)
326
+ Full chat UI with message history, input, typing indicators, file uploads.
327
+ ```tsx
328
+ <Chat
329
+ state={state}
330
+ title="AI Assistant"
331
+ codicent="my-project" // Optional: override which project to chat with
332
+ welcomeMessage="Hi!" // Optional: shown when no messages exist
333
+ hideFooter={false}
334
+ />
335
+ ```
336
+
337
+ #### `ListView`
338
+ Tabular data view with sorting, filtering, and column actions.
339
+ ```tsx
340
+ import { ListView } from 'codicent-app-sdk';
341
+
342
+ <ListView
343
+ data={dataMessages}
344
+ columns={[
345
+ { key: 'name', title: 'Name', filterable: true },
346
+ { key: ['offer_number', 'offerNumber'], title: 'Quote #', filterable: true },
347
+ { key: 'grand_total', title: 'Total', format: formatNumber },
348
+ { key: 'pdf', title: 'PDF', type: 'file' },
349
+ ]}
350
+ onSelect={(item) => navigate(`/chat?id=${item.originalMessageId}`)}
351
+ />
352
+ ```
353
+
354
+ Column definition options:
355
+ - `key: string | string[]` — JSON field name; array = fallback chain
356
+ - `format: (value) => string` — display transformer
357
+ - `filterable: true` — per-column text filter
358
+ - `type: "file"` — renders download link
359
+ - `type: "checkbox"` — renders checkbox
360
+ - `action` — icon button with click handler
361
+
362
+ #### `AppFrame`
363
+ Embeds an external URL in an iframe within the page layout.
364
+ ```tsx
365
+ <AppFrame src="https://example.com/embed" title="External view" showFooter={true} />
366
+ ```
367
+
368
+ #### `Markdown`
369
+ Renders markdown content including GFM and Mermaid diagrams.
370
+ ```tsx
371
+ <Markdown content={message.content} />
372
+ ```
373
+
374
+ #### `UploadFile`
375
+ File upload UI with progress.
376
+ ```tsx
377
+ <UploadFile service={state.service} codicent="my-project" />
378
+ ```
379
+
380
+ ---
381
+
382
+ ### Hooks summary
383
+
384
+ | Hook | Purpose |
385
+ |------|---------|
386
+ | `useCodicentApp()` | Core app state, service, auth |
387
+ | `useLocalization()` | i18n translation |
388
+ | `useChat(service)` | Chat message state and send |
389
+ | `useRealtimeVoiceAI(options)` | Real-time voice AI connection |
390
+ | `useAuthState(auth0)` | Auth lifecycle and token |
391
+ | `useTheme()` | App theme/branding |
392
+ | `useToaster()` | Toast notification helpers |
393
+ | `useStateWithLocalStorage()` | Persistent local state |
394
+ | `useAudioRecorder()` | Microphone recording |
395
+ | `useTemplateVariables()` | Template string utilities |
396
+ | `useTools()` | AI tool handler registration |
397
+ | `useEmbeddings()` | Vector embedding operations |
398
+
399
+ ---
400
+
401
+ ## TypeScript types
402
+
403
+ ```tsx
404
+ import type {
405
+ CodicentAppState,
406
+ ButtonConfig,
407
+ ColumnDefinition,
408
+ ListDefinitions,
409
+ DataMessage,
410
+ Message,
411
+ } from 'codicent-app-sdk';
412
+ ```
413
+
414
+ **`ButtonConfig`** — navigation button with optional RBAC claims:
415
+ ```ts
416
+ {
417
+ title: string;
418
+ url: string; // "#/list?tag=customer2", "voice:...", "mailto:...", etc.
419
+ claim?: string; // Show only if user has this claim
420
+ notClaim?: string; // Hide if user has this claim
421
+ subtitle?: string;
422
+ options?: ButtonConfig[];
423
+ }
424
+ ```
425
+
426
+ **`DataMessage`** — result from `readDataMessages()`:
427
+ ```ts
428
+ {
429
+ id: string;
430
+ originalMessageId: string; // Stable ID across updates — use this for references/tags
431
+ content: string; // Raw: "@project #data #tag\n{json}"
432
+ data: Record<string, unknown>; // Parsed JSON payload
433
+ tags: string[];
434
+ createdAt: string;
435
+ }
436
+ ```
437
+
438
+ ---
439
+
440
+ ## Alternative: `createCodicentApp()` factory
441
+
442
+ For standalone deployments where you do not control the React entry point, `createCodicentApp()` wraps the full initialization (including Auth0Provider, FluentProvider, HashRouter) and renders to a DOM element:
443
+
444
+ ```tsx
445
+ import { createCodicentApp } from 'codicent-app-sdk';
446
+
447
+ const app = createCodicentApp({
448
+ name: 'My CRM',
449
+ apiBaseUrl: 'https://codicent.com/',
450
+ auth0: { domain: '...', clientId: '...' },
451
+ buttons: [
452
+ { title: 'Customers', url: './#/list?tag=customer2' },
453
+ { title: 'Chat', url: './#/chat' },
454
+ ],
455
+ listDefinitions: {
456
+ customer2: [
457
+ { key: 'name', title: 'Name', filterable: true },
458
+ { key: 'email', title: 'Email' },
459
+ ],
460
+ },
461
+ chatInstructions: 'You are a helpful CRM assistant.',
462
+ modules: { sales: true, voice: true },
463
+ });
464
+
465
+ app.render('#root');
466
+ // app.unmount();
467
+ // app.getConfig();
468
+ ```
469
+
470
+ The `codicentapp/` reference implementation uses `initCodicentApp()` + manual React tree setup, which gives more control over routing and layout. `createCodicentApp()` is appropriate for simpler or standalone deployments.
471
+
472
+ ---
473
+
474
+ ## Local development workflow
475
+
476
+ ### Build the SDK
477
+
478
+ ```bash
479
+ cd codicent-app-sdk
480
+ npm install
481
+ npm run build # Output: dist/cjs/, dist/esm/, dist/index.d.ts
482
+ npm run dev # Watch mode
483
+ ```
484
+
485
+ ### Test in codicentapp (file: reference)
486
+
487
+ In `codicentapp/package.json`:
488
+ ```json
489
+ "codicent-app-sdk": "file:../codicent-app-sdk"
490
+ ```
491
+ Then:
492
+ ```bash
493
+ cd codicentapp
494
+ npm install
495
+ npm run build
496
+ ```
497
+
498
+ ### Test with npm link
499
+
500
+ ```bash
501
+ # In SDK directory
502
+ npm link
503
+
504
+ # In your app directory
505
+ npm link codicent-app-sdk
506
+ ```
507
+
508
+ To restore npm version:
509
+ ```bash
510
+ npm uninstall codicent-app-sdk
511
+ npm install codicent-app-sdk
512
+ ```
513
+
514
+ ### Build output layout
515
+
516
+ ```
517
+ dist/
518
+ cjs/ CommonJS modules
519
+ esm/ ES modules
520
+ index.d.ts TypeScript definitions
521
+ ```
522
+
523
+ ### Release
524
+
525
+ ```bash
526
+ npm version patch # or minor / major
527
+ npm run build
528
+ npm publish --access public
529
+ ```
530
+
531
+ ---
532
+
533
+ ## Debugging
534
+
535
+ The SDK emits `codicent-log` custom events on `window`:
536
+
537
+ ```tsx
538
+ window.addEventListener('codicent-log', (event: CustomEvent) => {
539
+ console.log(event.detail); // { level, message, context }
540
+ });
541
+ ```
542
+
543
+ ---
544
+
545
+ ## Related
546
+
547
+ - `codicentapp/` — primary consumer; complete reference implementation
548
+ - `codicent-api-client/` — framework-agnostic fetch-based API client (shared with web components)
549
+ - `codicent-components/` — zero-build Web Components library using the same Codicent API
550
+ - [Voice Upgrade Guide](VOICE_UPGRADE_GUIDE.md) — real-time voice AI setup and security
551
+
552
+ ---
553
+
554
+ ## License
555
+
556
+ MIT © Codicent Inside AB