agent-office 0.0.13 → 0.0.14

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.
@@ -59,11 +59,12 @@ function renderMessage(msg, humanName, spacingClass) {
59
59
  const isMine = msg.from_name === humanName;
60
60
  const bubbleClass = isMine ? "bubble bubble-mine" : "bubble bubble-theirs";
61
61
  const wrapClass = `msg-wrap ${isMine ? "msg-wrap-mine" : "msg-wrap-theirs"} ${spacingClass}`;
62
- const bodyHtml = escapeHtml(msg.body).replace(/\n/g, "<br>");
62
+ // Base64 encode to preserve newlines and special chars reliably
63
+ const bodyEncoded = Buffer.from(msg.body).toString('base64');
63
64
  const unreadDot = !isMine && !msg.read ? `<span class="unread-dot"></span>` : "";
64
65
  return `<div class="${wrapClass}" data-id="${msg.id}">
65
66
  <div class="${bubbleClass}">
66
- <div class="bubble-body">${bodyHtml}</div>
67
+ <div class="bubble-body markdown-body" data-markdown-b64="${bodyEncoded}"></div>
67
68
  <div class="bubble-time">${unreadDot}${formatTime(msg.created_at)}</div>
68
69
  </div>
69
70
  </div>`;
@@ -91,6 +92,9 @@ function renderPage(coworker, msgs, humanName) {
91
92
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
92
93
  <title>${escapeHtml(coworker)} — agent-office</title>
93
94
  <script src="https://unpkg.com/htmx.org@2.0.4/dist/htmx.min.js"></script>
95
+ <script src="https://cdn.jsdelivr.net/npm/marked@11.1.1/marked.min.js"></script>
96
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/github-markdown-css@5.5.1/github-markdown-light.min.css" media="(prefers-color-scheme: light)">
97
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/github-markdown-css@5.5.1/github-markdown-dark.min.css" media="(prefers-color-scheme: dark)">
94
98
  <style>
95
99
  *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
96
100
 
@@ -264,6 +268,66 @@ function renderPage(coworker, msgs, humanName) {
264
268
  }
265
269
 
266
270
  .bubble-body { font-size: 14.5px; }
271
+ .bubble-body.markdown-body {
272
+ background: transparent;
273
+ color: inherit;
274
+ font-size: 14px;
275
+ line-height: 1.5;
276
+ }
277
+ .bubble-body.markdown-body p { margin: 0 0 8px 0; }
278
+ .bubble-body.markdown-body p:last-child { margin-bottom: 0; }
279
+ .bubble-body.markdown-body pre {
280
+ background: rgba(0,0,0,0.3);
281
+ border-radius: 6px;
282
+ padding: 8px 12px;
283
+ overflow-x: auto;
284
+ margin: 8px 0;
285
+ }
286
+ .bubble-body.markdown-body code {
287
+ background: rgba(0,0,0,0.2);
288
+ padding: 2px 5px;
289
+ border-radius: 3px;
290
+ font-size: 13px;
291
+ font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
292
+ }
293
+ .bubble-body.markdown-body pre code {
294
+ background: transparent;
295
+ padding: 0;
296
+ }
297
+ .bubble-body.markdown-body ul, .bubble-body.markdown-body ol {
298
+ margin: 8px 0;
299
+ padding-left: 20px;
300
+ }
301
+ .bubble-body.markdown-body li { margin: 4px 0; }
302
+ .bubble-body.markdown-body blockquote {
303
+ border-left: 3px solid var(--accent);
304
+ margin: 8px 0;
305
+ padding-left: 12px;
306
+ color: var(--text-dim);
307
+ }
308
+ .bubble-body.markdown-body h1, .bubble-body.markdown-body h2,
309
+ .bubble-body.markdown-body h3, .bubble-body.markdown-body h4 {
310
+ margin: 12px 0 8px 0;
311
+ font-size: 15px;
312
+ font-weight: 600;
313
+ }
314
+ .bubble-body.markdown-body table {
315
+ border-collapse: collapse;
316
+ margin: 8px 0;
317
+ font-size: 13px;
318
+ }
319
+ .bubble-body.markdown-body th, .bubble-body.markdown-body td {
320
+ border: 1px solid var(--border);
321
+ padding: 6px 10px;
322
+ }
323
+ .bubble-body.markdown-body th {
324
+ background: var(--surface2);
325
+ }
326
+ .bubble-body.markdown-body strong,
327
+ .bubble-body.markdown-body b {
328
+ color: #ffffff;
329
+ font-weight: 600;
330
+ }
267
331
  .bubble-time {
268
332
  font-size: 10px;
269
333
  color: var(--text-dim);
@@ -498,6 +562,28 @@ function renderPage(coworker, msgs, humanName) {
498
562
  clearTimeout(el._hideTimer)
499
563
  el._hideTimer = setTimeout(() => el.classList.remove('visible'), 3000)
500
564
  }
565
+
566
+ // Render markdown in chat bubbles
567
+ function renderMarkdown() {
568
+ if (typeof marked === 'undefined') return
569
+ document.querySelectorAll('.markdown-body[data-markdown-b64]').forEach(el => {
570
+ const b64 = el.getAttribute('data-markdown-b64')
571
+ if (b64 && !el.hasAttribute('data-rendered')) {
572
+ // Decode base64 to get original text with preserved newlines
573
+ const text = atob(b64)
574
+ el.innerHTML = marked.parse(text)
575
+ el.setAttribute('data-rendered', 'true')
576
+ }
577
+ })
578
+ }
579
+
580
+ // Initial render
581
+ renderMarkdown()
582
+
583
+ // Re-render after HTMX swaps new content
584
+ document.addEventListener('htmx:afterSwap', () => {
585
+ renderMarkdown()
586
+ })
501
587
  </script>
502
588
  </body>
503
589
  </html>`;
@@ -609,10 +695,19 @@ export async function communicatorWeb(coworker, options) {
609
695
  app.get("/ping", (_req, res) => {
610
696
  res.status(204).end();
611
697
  });
612
- app.listen(port, host, () => {
698
+ const server = app.listen(port, host, () => {
613
699
  console.log(`Communicator running at http://${host}:${port}`);
614
700
  console.log(`Press Ctrl+C to stop.`);
615
701
  });
702
+ server.on('error', (err) => {
703
+ if (err.code === 'EADDRINUSE') {
704
+ console.error(`Error: Port ${port} is already in use. Is another instance running?`);
705
+ }
706
+ else {
707
+ console.error(`Error: ${err.message}`);
708
+ }
709
+ process.exit(1);
710
+ });
616
711
  // Keep process alive
617
712
  await new Promise(() => { });
618
713
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-office",
3
- "version": "0.0.13",
3
+ "version": "0.0.14",
4
4
  "description": "An office for your AI agents",
5
5
  "type": "module",
6
6
  "license": "MIT",