@ottocode/web-ui 0.1.173

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.
package/README.md ADDED
@@ -0,0 +1,450 @@
1
+ # @ottocode/web-ui
2
+
3
+ Pre-built, embeddable web UI for ottocode. This package contains the fully-built static assets from the ottocode web interface, ready to be served by any web server or framework.
4
+
5
+ ## Features
6
+
7
+ - 🎯 **One-Line Integration** - Import and serve with a single function call
8
+ - 📦 **Pre-built Assets** - No build step required in your project
9
+ - 🚀 **Framework Agnostic** - Works with Bun, Express, Fastify, Hono, or any HTTP server
10
+ - 🎨 **Full Featured** - Complete ottocode web interface with all functionality
11
+ - 📱 **Responsive** - Modern, mobile-friendly UI built with React and Tailwind CSS
12
+ - ⚡ **Fast** - Optimized production build with code splitting
13
+ - 🛣️ **Smart Routing** - Handles SPA routing and direct asset requests automatically
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ npm install @ottocode/web-ui
19
+ # or
20
+ yarn add @ottocode/web-ui
21
+ # or
22
+ pnpm add @ottocode/web-ui
23
+ # or
24
+ bun add @ottocode/web-ui
25
+ ```
26
+
27
+ ## Quick Start
28
+
29
+ ### Ultra-Simple (Recommended)
30
+
31
+ Just one line - everything is handled for you:
32
+
33
+ ```typescript
34
+ import { serveWebUI } from '@ottocode/web-ui';
35
+
36
+ Bun.serve({
37
+ port: 3000,
38
+ idleTimeout: 240, // IMPORTANT: prevents SSE timeout
39
+ fetch: serveWebUI({ prefix: '/ui' })
40
+ });
41
+
42
+ console.log('Web UI: http://localhost:3000/ui');
43
+ ```
44
+
45
+ > **⚠️ Important**: Always set `idleTimeout: 240` (or higher) in `Bun.serve()` to prevent SSE connection timeouts. The web UI uses Server-Sent Events for real-time streaming, and Bun's default timeout of 10 seconds will cause connections to drop.
46
+
47
+ That's it! The web UI will be available at `/ui` with:
48
+ - ✅ Automatic SPA routing
49
+ - ✅ Asset path handling (both `/ui/assets/*` and `/assets/*`)
50
+ - ✅ Proper MIME types
51
+ - ✅ 404 fallbacks
52
+ - ✅ Real-time SSE streaming
53
+
54
+ ### With Custom Routes
55
+
56
+ Combine the web UI with your own API routes:
57
+
58
+ ```typescript
59
+ import { serveWebUI } from '@ottocode/web-ui';
60
+
61
+ const webUI = serveWebUI({ prefix: '/ui' });
62
+
63
+ Bun.serve({
64
+ port: 3000,
65
+ async fetch(req) {
66
+ const url = new URL(req.url);
67
+
68
+ // Your API routes
69
+ if (url.pathname === '/api/hello') {
70
+ return new Response(JSON.stringify({ message: 'Hello!' }), {
71
+ headers: { 'Content-Type': 'application/json' }
72
+ });
73
+ }
74
+
75
+ // Try web UI handler
76
+ const webUIResponse = await webUI(req);
77
+ if (webUIResponse) return webUIResponse;
78
+
79
+ // Final fallback
80
+ return new Response('Not found', { status: 404 });
81
+ }
82
+ });
83
+ ```
84
+
85
+ ### With Root Redirect
86
+
87
+ Automatically redirect `/` to `/ui`:
88
+
89
+ ```typescript
90
+ import { serveWebUI } from '@ottocode/web-ui';
91
+
92
+ Bun.serve({
93
+ port: 3000,
94
+ fetch: serveWebUI({
95
+ prefix: '/ui',
96
+ redirectRoot: true // '/' → '/ui'
97
+ })
98
+ });
99
+ ```
100
+
101
+ ### Different Prefix
102
+
103
+ Serve the UI at any path you want:
104
+
105
+ ```typescript
106
+ import { serveWebUI } from '@ottocode/web-ui';
107
+
108
+ Bun.serve({
109
+ port: 3000,
110
+ fetch: serveWebUI({ prefix: '/admin' })
111
+ });
112
+
113
+ console.log('Web UI: http://localhost:3000/admin');
114
+ ```
115
+
116
+ ### Custom Server URL
117
+
118
+ When serving both the API and web UI from the same server, you can configure the web UI to connect to your server instead of the default `localhost:9100`:
119
+
120
+ ```typescript
121
+ import { createApp } from '@ottocode/server';
122
+ import { serveWebUI } from '@ottocode/web-ui';
123
+
124
+ const port = parseInt(process.env.PORT || '3000', 10);
125
+ const host = process.env.HOST || '127.0.0.1';
126
+
127
+ const app = createApp();
128
+ const handleWebUI = serveWebUI({
129
+ prefix: '/ui',
130
+ serverUrl: `http://${host}:${port}`, // Explicit server URL
131
+ });
132
+
133
+ // Or let it auto-detect (recommended for same-server setup):
134
+ // const handleWebUI = serveWebUI({ prefix: '/ui' });
135
+
136
+ const server = Bun.serve({
137
+ port,
138
+ hostname: host,
139
+ async fetch(req) {
140
+ // Serve web UI first
141
+ const webUIResponse = await handleWebUI(req);
142
+ if (webUIResponse) return webUIResponse;
143
+
144
+ // Then API routes
145
+ return app.fetch(req);
146
+ },
147
+ });
148
+
149
+ console.log(`Server: http://${host}:${server.port}/ui`);
150
+ ```
151
+
152
+ > **Note:** If you don't specify `serverUrl`, the web UI will automatically detect the server URL from the incoming request. This is recommended when serving both the API and UI from the same server.
153
+
154
+ ## API Reference
155
+
156
+ ### `serveWebUI(options?): (req: Request) => Promise<Response | null>`
157
+
158
+ Creates a request handler that serves the web UI.
159
+
160
+ **Options:**
161
+
162
+ | Option | Type | Default | Description |
163
+ |--------|------|---------|-------------|
164
+ | `prefix` | `string` | `'/ui'` | URL prefix for the web UI |
165
+ | `redirectRoot` | `boolean` | `false` | Redirect `/` to the prefix |
166
+ | `onNotFound` | `(req: Request) => Response \| null` | `null` | Custom 404 handler |
167
+ | `serverUrl` | `string` | Auto-detected | API server URL for the web UI to connect to. If not provided, auto-detects from request (e.g., `http://localhost:3000`) |
168
+
169
+ **Returns:** A request handler function that returns:
170
+ - `Response` if the request matches a web UI route
171
+ - `null` if the request should be handled by other routes
172
+
173
+ **Example:**
174
+
175
+ ```typescript
176
+ const handler = serveWebUI({
177
+ prefix: '/dashboard',
178
+ redirectRoot: true,
179
+ onNotFound: (req) => new Response('UI not found', { status: 404 })
180
+ });
181
+
182
+ Bun.serve({ port: 3000, fetch: handler });
183
+ ```
184
+
185
+ ### `getWebUIPath(): string`
186
+
187
+ Returns the absolute path to the directory containing all built web UI assets.
188
+
189
+ ```typescript
190
+ import { getWebUIPath } from '@ottocode/web-ui';
191
+
192
+ const assetsPath = getWebUIPath();
193
+ // => '/path/to/node_modules/@ottocode/web-ui/dist/web-assets'
194
+ ```
195
+
196
+ ### `getIndexPath(): string`
197
+
198
+ Returns the absolute path to the main `index.html` file.
199
+
200
+ ```typescript
201
+ import { getIndexPath } from '@ottocode/web-ui';
202
+
203
+ const indexPath = getIndexPath();
204
+ // => '/path/to/node_modules/@ottocode/web-ui/dist/web-assets/index.html'
205
+ ```
206
+
207
+ ### `isWebUIAvailable(): boolean`
208
+
209
+ Checks if the web UI assets are properly built and available.
210
+
211
+ ```typescript
212
+ import { isWebUIAvailable } from '@ottocode/web-ui';
213
+
214
+ if (!isWebUIAvailable()) {
215
+ console.error('Web UI assets not found!');
216
+ process.exit(1);
217
+ }
218
+ ```
219
+
220
+ ## Framework Examples
221
+
222
+ ### Bun (Recommended)
223
+
224
+ ```typescript
225
+ import { serveWebUI } from '@ottocode/web-ui';
226
+
227
+ Bun.serve({
228
+ port: 3000,
229
+ fetch: serveWebUI({ prefix: '/ui' })
230
+ });
231
+ ```
232
+
233
+ ### Express
234
+
235
+ ```typescript
236
+ import express from 'express';
237
+ import { getWebUIPath, getIndexPath } from '@ottocode/web-ui';
238
+
239
+ const app = express();
240
+
241
+ // Serve static assets
242
+ app.use('/ui', express.static(getWebUIPath()));
243
+
244
+ // SPA fallback
245
+ app.get('/ui/*', (req, res) => {
246
+ res.sendFile(getIndexPath());
247
+ });
248
+
249
+ app.listen(3000);
250
+ ```
251
+
252
+ ### Fastify
253
+
254
+ ```typescript
255
+ import Fastify from 'fastify';
256
+ import fastifyStatic from '@fastify/static';
257
+ import { getWebUIPath } from '@ottocode/web-ui';
258
+
259
+ const fastify = Fastify();
260
+
261
+ await fastify.register(fastifyStatic, {
262
+ root: getWebUIPath(),
263
+ prefix: '/ui/',
264
+ });
265
+
266
+ await fastify.listen({ port: 3000 });
267
+ ```
268
+
269
+ ### Hono
270
+
271
+ ```typescript
272
+ import { Hono } from 'hono';
273
+ import { serveStatic } from 'hono/bun';
274
+ import { getWebUIPath } from '@ottocode/web-ui';
275
+
276
+ const app = new Hono();
277
+
278
+ app.use('/ui/*', serveStatic({ root: getWebUIPath() }));
279
+
280
+ export default app;
281
+ ```
282
+
283
+ ### Node.js HTTP
284
+
285
+ ```typescript
286
+ import { createServer } from 'http';
287
+ import { serveWebUI } from '@ottocode/web-ui';
288
+
289
+ const handler = serveWebUI({ prefix: '/ui' });
290
+
291
+ createServer(async (req, res) => {
292
+ const request = new Request(`http://localhost${req.url}`);
293
+ const response = await handler(request);
294
+
295
+ if (response) {
296
+ res.writeHead(response.status, Object.fromEntries(response.headers));
297
+ res.end(await response.text());
298
+ } else {
299
+ res.writeHead(404);
300
+ res.end('Not found');
301
+ }
302
+ }).listen(3000);
303
+ ```
304
+
305
+ ## How It Works
306
+
307
+ The `serveWebUI()` function handles all the complexity for you:
308
+
309
+ 1. **Prefixed Routes** (`/ui/*`): Strips the prefix and serves the requested file
310
+ 2. **Direct Asset Requests** (`/assets/*`, `/vite.svg`, etc.): Serves assets directly (for when HTML references them)
311
+ 3. **SPA Fallback**: Returns `index.html` for any unmatched routes under the prefix
312
+ 4. **Security**: Prevents directory traversal attacks
313
+ 5. **MIME Types**: Automatically sets correct Content-Type headers
314
+ 6. **Cross-Runtime**: Works in both Bun and Node.js
315
+
316
+ This pattern solves the common issue where Vite-built apps reference assets like `/assets/index-*.js` directly, which would 404 without special handling.
317
+
318
+ ## Important Notes
319
+
320
+ ### Single Page Application (SPA)
321
+
322
+ The web UI is a React-based SPA with client-side routing. The `serveWebUI()` handler automatically:
323
+ - Serves static assets from the web UI path
324
+ - Falls back to `index.html` for client-side routes
325
+ - Handles both prefixed (`/ui/assets/*`) and direct (`/assets/*`) asset requests
326
+
327
+ ### API Configuration
328
+
329
+ The web UI expects an API to be available. By default, it will try to connect to:
330
+ - `http://localhost:3000/api` (in development)
331
+ - Same origin `/api` (in production)
332
+
333
+ You'll need to set up your API endpoints to handle ottocode requests. See the [ottocode documentation](https://github.com/yourusername/ottocode) for API implementation details.
334
+
335
+ ### CORS
336
+
337
+ If your API is on a different origin than the web UI, you'll need to configure CORS headers:
338
+
339
+ ```typescript
340
+ Bun.serve({
341
+ port: 3000,
342
+ async fetch(req) {
343
+ // Your routes...
344
+
345
+ const response = await handler(req);
346
+
347
+ // Add CORS headers
348
+ response.headers.set('Access-Control-Allow-Origin', '*');
349
+ response.headers.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
350
+ response.headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization');
351
+
352
+ return response;
353
+ }
354
+ });
355
+ ```
356
+
357
+ ## Examples
358
+
359
+ See the [examples](./examples) directory for complete working examples:
360
+
361
+ - **Bun Server** - Minimal example using Bun's HTTP server with `serveWebUI()`
362
+ - **Express Server** - Traditional Express.js integration
363
+
364
+ ## Development
365
+
366
+ This package is part of the ottocode monorepo. To build from source:
367
+
368
+ ```bash
369
+ # Clone the repository
370
+ git clone https://github.com/yourusername/ottocode
371
+ cd ottocode
372
+
373
+ # Install dependencies
374
+ bun install
375
+
376
+ # Build the package
377
+ cd packages/web-ui
378
+ bun run build
379
+ ```
380
+
381
+ The build process:
382
+ 1. Builds the web app from `apps/web` using Vite
383
+ 2. Copies the production build to `dist/web-assets`
384
+ 3. Compiles the TypeScript exports
385
+ 4. Generates type declarations
386
+
387
+ ## What's Included
388
+
389
+ The package includes:
390
+
391
+ - ✅ Complete ottocode web interface
392
+ - ✅ React 19 with optimized production build
393
+ - ✅ TailwindCSS for styling
394
+ - ✅ Code syntax highlighting
395
+ - ✅ Markdown rendering
396
+ - ✅ Real-time API communication
397
+ - ✅ Responsive mobile design
398
+ - ✅ Dark mode support (if configured)
399
+ - ✅ Smart request handler with automatic routing
400
+
401
+ ## Bundle Size
402
+
403
+ The production build is optimized and includes:
404
+ - Main JS bundle: ~1.1 MB (370 KB gzipped)
405
+ - CSS: ~31 KB (6.5 KB gzipped)
406
+ - Total initial load: ~376 KB gzipped
407
+
408
+ ## Browser Support
409
+
410
+ The web UI supports all modern browsers:
411
+ - Chrome/Edge (last 2 versions)
412
+ - Firefox (last 2 versions)
413
+ - Safari (last 2 versions)
414
+
415
+ ## Migration from Manual Setup
416
+
417
+ If you were using the old manual approach:
418
+
419
+ **Before:**
420
+ ```typescript
421
+ import { getWebUIPath, getIndexPath } from '@ottocode/web-ui';
422
+
423
+ // 50 lines of routing logic...
424
+ ```
425
+
426
+ **After:**
427
+ ```typescript
428
+ import { serveWebUI } from '@ottocode/web-ui';
429
+
430
+ Bun.serve({
431
+ port: 3000,
432
+ fetch: serveWebUI({ prefix: '/ui' })
433
+ });
434
+ ```
435
+
436
+ ## License
437
+
438
+ MIT
439
+
440
+ ## Related
441
+
442
+ - [ottocode](https://github.com/yourusername/ottocode) - The main CLI tool
443
+ - [ottocode API Documentation](https://github.com/yourusername/ottocode/docs/api) - API implementation guide
444
+
445
+ ## Support
446
+
447
+ For issues, questions, or contributions:
448
+ - 🐛 [Report a bug](https://github.com/yourusername/ottocode/issues)
449
+ - 💡 [Request a feature](https://github.com/yourusername/ottocode/issues)
450
+ - 📖 [Read the docs](https://github.com/yourusername/ottocode/docs)
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Get the absolute path to the web UI assets directory
3
+ */
4
+ export declare function getWebUIPath(): string;
5
+ /**
6
+ * Get the absolute path to the index.html file
7
+ */
8
+ export declare function getIndexPath(): string;
9
+ /**
10
+ * Check if web UI assets are available
11
+ */
12
+ export declare function isWebUIAvailable(): boolean;
13
+ export interface ServeWebUIOptions {
14
+ /**
15
+ * URL prefix for the web UI (default: '/ui')
16
+ * @example '/ui', '/admin', '/dashboard'
17
+ */
18
+ prefix?: string;
19
+ /**
20
+ * Whether to redirect root to prefix (default: false)
21
+ * @example If true, '/' → '/ui'
22
+ */
23
+ redirectRoot?: boolean;
24
+ /**
25
+ * Custom 404 handler
26
+ */
27
+ onNotFound?: (req: Request) => Response | Promise<Response> | null;
28
+ /**
29
+ * API server URL for the web UI to connect to
30
+ * If not provided, will attempt to auto-detect from the request
31
+ * or fall back to localhost:9100
32
+ * @example 'http://localhost:3000', 'https://api.example.com'
33
+ */
34
+ serverUrl?: string;
35
+ }
36
+ export declare function serveWebUI(options?: ServeWebUIOptions): (req: Request) => Promise<Response | null>;
37
+ declare const _default: {
38
+ getWebUIPath: typeof getWebUIPath;
39
+ getIndexPath: typeof getIndexPath;
40
+ isWebUIAvailable: typeof isWebUIAvailable;
41
+ serveWebUI: typeof serveWebUI;
42
+ };
43
+ export default _default;
44
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAYA;;GAEG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,OAAO,CAa1C;AAED,MAAM,WAAW,iBAAiB;IACjC;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB;;OAEG;IACH,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC;IAEnE;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAkCD,wBAAgB,UAAU,CAAC,OAAO,GAAE,iBAAsB,IAYrB,KAAK,OAAO,KAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAqJ3E;;;;;;;AAyBD,wBAKE"}