@happyvertical/smrt-chat 0.30.0 → 0.31.1

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 (31) hide show
  1. package/dist/chunks/{ChatService-Dpzc1Pa5.js → ChatService-ByEPKa30.js} +2 -2
  2. package/dist/chunks/{ChatService-Dpzc1Pa5.js.map → ChatService-ByEPKa30.js.map} +1 -1
  3. package/dist/index.js +1 -1
  4. package/dist/internal/agent-runtime.js +1 -1
  5. package/dist/manifest.json +2 -2
  6. package/dist/services/ChatService.d.ts.map +1 -1
  7. package/dist/smrt-knowledge.json +4 -4
  8. package/dist/svelte/components/agent/AgentChat.svelte +6 -0
  9. package/dist/svelte/components/agent/ToolCallDisplay.svelte +4 -19
  10. package/dist/svelte/components/agent/ToolCallDisplay.svelte.d.ts.map +1 -1
  11. package/dist/svelte/components/agent/__tests__/ToolCallDisplay.test.js +44 -0
  12. package/dist/svelte/components/dialogs/RoomCreateDialog.svelte +101 -158
  13. package/dist/svelte/components/dialogs/RoomCreateDialog.svelte.d.ts.map +1 -1
  14. package/dist/svelte/components/layout/ChatLayout.svelte +11 -2
  15. package/dist/svelte/components/layout/ChatLayout.svelte.d.ts +2 -0
  16. package/dist/svelte/components/layout/ChatLayout.svelte.d.ts.map +1 -1
  17. package/dist/svelte/components/layout/MemberList.svelte +5 -3
  18. package/dist/svelte/components/layout/MemberList.svelte.d.ts.map +1 -1
  19. package/dist/svelte/components/layout/__tests__/ChatLayout.test.js +67 -0
  20. package/dist/svelte/components/messages/MessageItem.svelte +115 -1
  21. package/dist/svelte/components/messages/MessageItem.svelte.d.ts.map +1 -1
  22. package/dist/svelte/components/messages/__tests__/MessageItem.test.js +108 -0
  23. package/dist/svelte/components/shared/MentionAutocomplete.svelte +5 -1
  24. package/dist/svelte/components/shared/MentionAutocomplete.svelte.d.ts +2 -0
  25. package/dist/svelte/components/shared/MentionAutocomplete.svelte.d.ts.map +1 -1
  26. package/dist/svelte/components/shared/UserPresence.svelte +3 -3
  27. package/dist/svelte/components/shared/__tests__/MentionAutocomplete.test.js +66 -0
  28. package/dist/svelte/i18n.messages.d.ts +1 -0
  29. package/dist/svelte/i18n.messages.d.ts.map +1 -1
  30. package/dist/svelte/i18n.messages.js +1 -0
  31. package/package.json +8 -8
@@ -56,6 +56,16 @@ const formattedTime = $derived.by(() => {
56
56
  });
57
57
  });
58
58
 
59
+ const hasActions = $derived(
60
+ Boolean(onreact || onreply || (isOwn && (onedit || ondelete))),
61
+ );
62
+
63
+ // Stable id linking the disclosure trigger (aria-controls) to the revealed
64
+ // action group. The actions are a labelled group of independently
65
+ // keyboard-operable buttons, not an ARIA menu, so the trigger is a plain
66
+ // disclosure toggle (aria-expanded) rather than aria-haspopup="menu".
67
+ const actionsId = $derived(`message-actions-${message.id}`);
68
+
59
69
  function handleReact(emoji: string) {
60
70
  onreact?.(message.id, emoji);
61
71
  showReactionPicker = false;
@@ -64,8 +74,40 @@ function handleReact(emoji: string) {
64
74
  function toggleReactionPicker() {
65
75
  showReactionPicker = !showReactionPicker;
66
76
  }
77
+
78
+ // The "more actions" button is the keyboard/touch-reachable entry point that
79
+ // opens the action bar (a11y blocker, T2 #1391). It opens rather than toggles
80
+ // so it stays robust against the focus-then-click ordering a tap/keyboard
81
+ // activation produces; the bar is dismissed by Escape, blur, or mouse-leave.
82
+ function openActions() {
83
+ showActions = true;
84
+ }
85
+
86
+ // Collapse the action bar (and any open picker) once focus leaves the row — the
87
+ // keyboard/touch counterpart to onmouseleave so the actions are not hover-only.
88
+ function handleFocusOut(event: FocusEvent) {
89
+ const next = event.relatedTarget as Node | null;
90
+ const row = event.currentTarget as HTMLElement;
91
+ if (next && row.contains(next)) return;
92
+ showActions = false;
93
+ showReactionPicker = false;
94
+ }
95
+
96
+ // Escape closes the action bar / picker when open. Bound on the window (gated
97
+ // on open state) rather than the noninteractive row element so Svelte's
98
+ // a11y_no_noninteractive_element_interactions rule stays satisfied.
99
+ function handleEscape(event: KeyboardEvent) {
100
+ if (event.key === 'Escape') {
101
+ showReactionPicker = false;
102
+ showActions = false;
103
+ }
104
+ }
67
105
  </script>
68
106
 
107
+ <svelte:window
108
+ onkeydown={showActions || showReactionPicker ? handleEscape : undefined}
109
+ />
110
+
69
111
  {#if message.messageType === 'system' || message.messageType === 'action'}
70
112
  <div class="message-item message-item--system">
71
113
  <MessageBubble content={message.content} isOwn={false} variant="system" />
@@ -78,6 +120,7 @@ function toggleReactionPicker() {
78
120
  aria-label={t(M['chat.message_item.message_from'], { name: message.senderName })}
79
121
  onmouseenter={() => showActions = true}
80
122
  onmouseleave={() => { showActions = false; showReactionPicker = false; }}
123
+ onfocusout={handleFocusOut}
81
124
  >
82
125
  {#if !isOwn}
83
126
  <div class="message-item__avatar">
@@ -151,8 +194,27 @@ function toggleReactionPicker() {
151
194
  </div>
152
195
  </div>
153
196
 
197
+ {#if hasActions}
198
+ <button
199
+ class="message-item__more-btn"
200
+ type="button"
201
+ onclick={openActions}
202
+ onfocus={openActions}
203
+ aria-label={t(M['chat.message_item.more_actions'])}
204
+ aria-expanded={showActions}
205
+ aria-controls={actionsId}
206
+ >
207
+ <svg viewBox="0 0 16 16" width="14" height="14" aria-hidden="true"><circle cx="3" cy="8" r="1.3" fill="currentColor" /><circle cx="8" cy="8" r="1.3" fill="currentColor" /><circle cx="13" cy="8" r="1.3" fill="currentColor" /></svg>
208
+ </button>
209
+ {/if}
210
+
154
211
  {#if showActions}
155
- <div class="message-item__actions">
212
+ <div
213
+ class="message-item__actions"
214
+ role="group"
215
+ id={actionsId}
216
+ aria-label={t(M['chat.message_item.more_actions'])}
217
+ >
156
218
  {#if onreact}
157
219
  <button
158
220
  class="message-item__action-btn"
@@ -379,6 +441,49 @@ function toggleReactionPicker() {
379
441
  font-style: italic;
380
442
  }
381
443
 
444
+ .message-item__more-btn {
445
+ display: flex;
446
+ align-items: center;
447
+ justify-content: center;
448
+ width: 26px;
449
+ height: 26px;
450
+ position: absolute;
451
+ top: 0;
452
+ right: var(--smrt-spacing-4, 1rem);
453
+ border: 1px solid var(--smrt-color-outline-variant, #c4c6cf);
454
+ border-radius: var(--smrt-radius-small, 0.25rem);
455
+ background: var(--smrt-color-surface, #ffffff);
456
+ color: var(--smrt-color-on-surface-variant, #43474e);
457
+ cursor: pointer;
458
+ opacity: 0;
459
+ transition: opacity var(--smrt-duration-short2, 150ms) var(--smrt-easing-standard, ease);
460
+ }
461
+
462
+ .message-item--own .message-item__more-btn {
463
+ right: auto;
464
+ left: var(--smrt-spacing-4, 1rem);
465
+ }
466
+
467
+ /* Reveal the keyboard/touch affordance on hover OR when focus is inside the
468
+ row, so it is never strictly hover-only (a11y blocker, T2 #1391). Always
469
+ visible to coarse pointers (touch) which cannot hover. */
470
+ .message-item:hover .message-item__more-btn,
471
+ .message-item:focus-within .message-item__more-btn {
472
+ opacity: 1;
473
+ }
474
+
475
+ .message-item__more-btn:focus-visible {
476
+ opacity: 1;
477
+ outline: 2px solid var(--smrt-color-primary, #005ac1);
478
+ outline-offset: 1px;
479
+ }
480
+
481
+ @media (hover: none) {
482
+ .message-item__more-btn {
483
+ opacity: 1;
484
+ }
485
+ }
486
+
382
487
  .message-item__actions {
383
488
  display: flex;
384
489
  align-items: center;
@@ -391,6 +496,7 @@ function toggleReactionPicker() {
391
496
  border-radius: var(--smrt-radius-small, 0.25rem);
392
497
  padding: var(--smrt-spacing-1, 4px);
393
498
  box-shadow: var(--smrt-elevation-1, 0 1px 3px rgba(0, 0, 0, 0.12));
499
+ z-index: 1;
394
500
  }
395
501
 
396
502
  .message-item--own .message-item__actions {
@@ -428,4 +534,12 @@ function toggleReactionPicker() {
428
534
  right: auto;
429
535
  left: var(--smrt-spacing-4, 1rem);
430
536
  }
537
+
538
+ @media (prefers-reduced-motion: reduce) {
539
+ .message-item__more-btn,
540
+ .message-item__action-btn,
541
+ .message-item__reaction {
542
+ transition: none;
543
+ }
544
+ }
431
545
  </style>
@@ -1 +1 @@
1
- {"version":3,"file":"MessageItem.svelte.d.ts","sourceRoot":"","sources":["../../../../src/svelte/components/messages/MessageItem.svelte.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAMtD,MAAM,WAAW,KAAK;IACpB,6BAA6B;IAC7B,OAAO,EAAE,eAAe,CAAC;IACzB,wDAAwD;IACxD,KAAK,EAAE,OAAO,CAAC;IACf,qBAAqB;IACrB,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,qBAAqB;IACrB,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,oBAAoB;IACpB,MAAM,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9B,sBAAsB;IACtB,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;CACjC;AAyJD,QAAA,MAAM,WAAW,2CAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"MessageItem.svelte.d.ts","sourceRoot":"","sources":["../../../../src/svelte/components/messages/MessageItem.svelte.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAMtD,MAAM,WAAW,KAAK;IACpB,6BAA6B;IAC7B,OAAO,EAAE,eAAe,CAAC;IACzB,wDAAwD;IACxD,KAAK,EAAE,OAAO,CAAC;IACf,qBAAqB;IACrB,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,qBAAqB;IACrB,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,oBAAoB;IACpB,MAAM,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9B,sBAAsB;IACtB,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;CACjC;AAuMD,QAAA,MAAM,WAAW,2CAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
@@ -0,0 +1,108 @@
1
+ // @vitest-environment jsdom
2
+ /**
3
+ * Component coverage for MessageItem via the shared S11 harness (#1416).
4
+ *
5
+ * Regression for the T2 #1391 a11y blocker: the per-message actions
6
+ * (reply/edit/delete/react) used to be revealed ONLY by mouseenter/mouseleave,
7
+ * so keyboard- and touch-only users could never reach them. There must now be a
8
+ * keyboard-reachable "more actions" button that toggles the action bar, and
9
+ * focusing into the row must reveal the actions.
10
+ */
11
+ import { expectNoA11yViolations, render, screen, userEvent, } from '@happyvertical/smrt-vitest/svelte';
12
+ import { describe, expect, it, vi } from 'vitest';
13
+ import MessageItem from '../MessageItem.svelte';
14
+ function makeMessage(overrides = {}) {
15
+ return {
16
+ id: 'm1',
17
+ roomId: 'room-1',
18
+ threadId: null,
19
+ senderProfileId: 'p1',
20
+ senderName: 'Ada',
21
+ senderAvatarUrl: undefined,
22
+ agentSessionId: null,
23
+ content: 'Hello there',
24
+ messageType: 'text',
25
+ role: 'user',
26
+ isEdited: false,
27
+ isDeleted: false,
28
+ replyTo: null,
29
+ reactions: [],
30
+ attachments: [],
31
+ toolCallData: null,
32
+ createdAt: new Date('2026-01-01T00:00:00Z'),
33
+ ...overrides,
34
+ };
35
+ }
36
+ describe('MessageItem', () => {
37
+ it('renders a keyboard-reachable "more actions" button when actions are available', () => {
38
+ render(MessageItem, {
39
+ props: {
40
+ message: makeMessage(),
41
+ isOwn: false,
42
+ onreply: vi.fn(),
43
+ onreact: vi.fn(),
44
+ },
45
+ });
46
+ const moreBtn = screen.getByRole('button', { name: 'Message actions' });
47
+ expect(moreBtn).toBeInTheDocument();
48
+ // A real <button> is inherently in the tab order (no tabindex="-1").
49
+ expect(moreBtn.getAttribute('tabindex')).not.toBe('-1');
50
+ });
51
+ it('reveals the action bar via the more-actions button without any mouse hover', async () => {
52
+ const onreply = vi.fn();
53
+ render(MessageItem, {
54
+ props: {
55
+ message: makeMessage(),
56
+ isOwn: false,
57
+ onreply,
58
+ onreact: vi.fn(),
59
+ },
60
+ });
61
+ // Actions are hidden until requested — no hover, no focus yet.
62
+ expect(screen.queryByRole('button', { name: 'Reply' })).toBeNull();
63
+ await userEvent.click(screen.getByRole('button', { name: 'Message actions' }));
64
+ // The action bar is now reachable; clicking Reply invokes the callback.
65
+ const replyBtn = await screen.findByRole('button', { name: 'Reply' });
66
+ await userEvent.click(replyBtn);
67
+ expect(onreply).toHaveBeenCalledWith('m1');
68
+ });
69
+ it("exposes edit/delete actions for the user's own message", async () => {
70
+ const onedit = vi.fn();
71
+ const ondelete = vi.fn();
72
+ render(MessageItem, {
73
+ props: {
74
+ message: makeMessage(),
75
+ isOwn: true,
76
+ onedit,
77
+ ondelete,
78
+ },
79
+ });
80
+ await userEvent.click(screen.getByRole('button', { name: 'Message actions' }));
81
+ await userEvent.click(await screen.findByRole('button', { name: 'Edit' }));
82
+ expect(onedit).toHaveBeenCalledWith('m1');
83
+ // The bar stays open while focus remains inside the row, so Delete is
84
+ // reachable in the same interaction.
85
+ await userEvent.click(screen.getByRole('button', { name: 'Delete' }));
86
+ expect(ondelete).toHaveBeenCalledWith('m1');
87
+ });
88
+ it('does not render the more-actions affordance when no action callbacks are provided', () => {
89
+ render(MessageItem, {
90
+ props: {
91
+ message: makeMessage(),
92
+ isOwn: false,
93
+ },
94
+ });
95
+ expect(screen.queryByRole('button', { name: 'Message actions' })).toBeNull();
96
+ });
97
+ it('is axe-clean with the action affordance present', async () => {
98
+ const { container } = render(MessageItem, {
99
+ props: {
100
+ message: makeMessage(),
101
+ isOwn: false,
102
+ onreply: vi.fn(),
103
+ onreact: vi.fn(),
104
+ },
105
+ });
106
+ await expectNoA11yViolations(container);
107
+ });
108
+ });
@@ -19,9 +19,11 @@ export interface Props {
19
19
  onselect: (id: string) => void;
20
20
  /** Whether the popup is visible */
21
21
  isVisible: boolean;
22
+ /** Dismiss the popup (e.g. Escape pressed) without selecting */
23
+ oncancel?: () => void;
22
24
  }
23
25
 
24
- const { query, suggestions, onselect, isVisible }: Props = $props();
26
+ const { query, suggestions, onselect, isVisible, oncancel }: Props = $props();
25
27
 
26
28
  let activeIndex = $state(0);
27
29
 
@@ -50,6 +52,8 @@ function handleKeydown(event: KeyboardEvent) {
50
52
  onselect(suggestions[activeIndex].id);
51
53
  break;
52
54
  case 'Escape':
55
+ event.preventDefault();
56
+ oncancel?.();
53
57
  break;
54
58
  }
55
59
  }
@@ -11,6 +11,8 @@ export interface Props {
11
11
  onselect: (id: string) => void;
12
12
  /** Whether the popup is visible */
13
13
  isVisible: boolean;
14
+ /** Dismiss the popup (e.g. Escape pressed) without selecting */
15
+ oncancel?: () => void;
14
16
  }
15
17
  declare const MentionAutocomplete: import("svelte").Component<Props, {}, "">;
16
18
  type MentionAutocomplete = ReturnType<typeof MentionAutocomplete>;
@@ -1 +1 @@
1
- {"version":3,"file":"MentionAutocomplete.svelte.d.ts","sourceRoot":"","sources":["../../../../src/svelte/components/shared/MentionAutocomplete.svelte.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,KAAK;IACpB,0CAA0C;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,2BAA2B;IAC3B,WAAW,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACrE,0BAA0B;IAC1B,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,mCAAmC;IACnC,SAAS,EAAE,OAAO,CAAC;CACpB;AA+ED,QAAA,MAAM,mBAAmB,2CAAwC,CAAC;AAClE,KAAK,mBAAmB,GAAG,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAClE,eAAe,mBAAmB,CAAC"}
1
+ {"version":3,"file":"MentionAutocomplete.svelte.d.ts","sourceRoot":"","sources":["../../../../src/svelte/components/shared/MentionAutocomplete.svelte.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,KAAK;IACpB,0CAA0C;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,2BAA2B;IAC3B,WAAW,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACrE,0BAA0B;IAC1B,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,mCAAmC;IACnC,SAAS,EAAE,OAAO,CAAC;IACnB,gEAAgE;IAChE,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;CACvB;AAiFD,QAAA,MAAM,mBAAmB,2CAAwC,CAAC;AAClE,KAAK,mBAAmB,GAAG,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAClE,eAAe,mBAAmB,CAAC"}
@@ -42,15 +42,15 @@ const labelText: Record<string, string> = {
42
42
  }
43
43
 
44
44
  .presence__dot.online {
45
- background: var(--smrt-color-success, #4caf50);
45
+ background: var(--smrt-color-success, #1e8e3e);
46
46
  }
47
47
 
48
48
  .presence__dot.away {
49
- background: var(--smrt-color-warning, #ff9800);
49
+ background: var(--smrt-color-warning, #f9ab00);
50
50
  }
51
51
 
52
52
  .presence__dot.dnd {
53
- background: var(--smrt-color-error, #f44336);
53
+ background: var(--smrt-color-error, #b3261e);
54
54
  }
55
55
 
56
56
  .presence__dot.offline {
@@ -0,0 +1,66 @@
1
+ // @vitest-environment jsdom
2
+ /**
3
+ * Component coverage for MentionAutocomplete via the shared S11 harness (#1416).
4
+ *
5
+ * Regression for T2 #1391: `case 'Escape': break;` was a no-op, so the
6
+ * suggestion popup could not be dismissed from the keyboard. Escape must now
7
+ * invoke the `oncancel` callback so a parent can close the popup.
8
+ */
9
+ import { expectNoA11yViolations, render, screen, userEvent, } from '@happyvertical/smrt-vitest/svelte';
10
+ import { describe, expect, it, vi } from 'vitest';
11
+ import MentionAutocomplete from '../MentionAutocomplete.svelte';
12
+ const SUGGESTIONS = [
13
+ { id: 'p1', name: 'Ada Lovelace' },
14
+ { id: 'p2', name: 'Alan Turing' },
15
+ ];
16
+ describe('MentionAutocomplete', () => {
17
+ it('invokes oncancel when Escape is pressed while visible', async () => {
18
+ const oncancel = vi.fn();
19
+ render(MentionAutocomplete, {
20
+ props: {
21
+ query: 'a',
22
+ suggestions: SUGGESTIONS,
23
+ onselect: vi.fn(),
24
+ isVisible: true,
25
+ oncancel,
26
+ },
27
+ });
28
+ await userEvent.keyboard('{Escape}');
29
+ expect(oncancel).toHaveBeenCalledTimes(1);
30
+ });
31
+ it('does not throw on Escape when no oncancel is provided', async () => {
32
+ render(MentionAutocomplete, {
33
+ props: {
34
+ query: 'a',
35
+ suggestions: SUGGESTIONS,
36
+ onselect: vi.fn(),
37
+ isVisible: true,
38
+ },
39
+ });
40
+ await expect(userEvent.keyboard('{Escape}')).resolves.not.toThrow();
41
+ });
42
+ it('selects the active suggestion on Enter', async () => {
43
+ const onselect = vi.fn();
44
+ render(MentionAutocomplete, {
45
+ props: {
46
+ query: 'a',
47
+ suggestions: SUGGESTIONS,
48
+ onselect,
49
+ isVisible: true,
50
+ },
51
+ });
52
+ await userEvent.keyboard('{Enter}');
53
+ expect(onselect).toHaveBeenCalledWith('p1');
54
+ });
55
+ it('is axe-clean', async () => {
56
+ const { container } = render(MentionAutocomplete, {
57
+ props: {
58
+ query: 'a',
59
+ suggestions: SUGGESTIONS,
60
+ onselect: vi.fn(),
61
+ isVisible: true,
62
+ },
63
+ });
64
+ await expectNoA11yViolations(container);
65
+ });
66
+ });
@@ -24,6 +24,7 @@ export declare const M: {
24
24
  readonly 'chat.message_item.reply': "chat.message_item.reply";
25
25
  readonly 'chat.message_item.edit': "chat.message_item.edit";
26
26
  readonly 'chat.message_item.delete': "chat.message_item.delete";
27
+ readonly 'chat.message_item.more_actions': "chat.message_item.more_actions";
27
28
  readonly 'chat.message_list.messages_label': "chat.message_list.messages_label";
28
29
  readonly 'chat.message_list.no_messages': "chat.message_list.no_messages";
29
30
  readonly 'chat.thread_panel.thread_label': "chat.thread_panel.thread_label";
@@ -1 +1 @@
1
- {"version":3,"file":"i18n.messages.d.ts","sourceRoot":"","sources":["../../src/svelte/i18n.messages.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuEZ,CAAC"}
1
+ {"version":3,"file":"i18n.messages.d.ts","sourceRoot":"","sources":["../../src/svelte/i18n.messages.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwEZ,CAAC"}
@@ -38,6 +38,7 @@ export const M = defineMessages({
38
38
  'chat.message_item.reply': 'Reply',
39
39
  'chat.message_item.edit': 'Edit',
40
40
  'chat.message_item.delete': 'Delete',
41
+ 'chat.message_item.more_actions': 'Message actions',
41
42
  // MessageList
42
43
  'chat.message_list.messages_label': 'Messages',
43
44
  'chat.message_list.no_messages': 'No messages yet',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@happyvertical/smrt-chat",
3
- "version": "0.30.0",
3
+ "version": "0.31.1",
4
4
  "description": "Chat rooms, DMs, threads, and agent conversations for the SMRT framework",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -55,10 +55,10 @@
55
55
  "access": "public"
56
56
  },
57
57
  "dependencies": {
58
- "@happyvertical/smrt-core": "0.30.0",
59
- "@happyvertical/smrt-types": "0.30.0",
60
- "@happyvertical/smrt-ui": "0.30.0",
61
- "@happyvertical/smrt-tenancy": "0.30.0"
58
+ "@happyvertical/smrt-ui": "0.31.1",
59
+ "@happyvertical/smrt-types": "0.31.1",
60
+ "@happyvertical/smrt-core": "0.31.1",
61
+ "@happyvertical/smrt-tenancy": "0.31.1"
62
62
  },
63
63
  "peerDependencies": {
64
64
  "svelte": "^5.18.0"
@@ -78,9 +78,9 @@
78
78
  "typescript": "^5.9.3",
79
79
  "vite": "^7.3.1",
80
80
  "vitest": "^4.0.17",
81
- "@happyvertical/smrt-agents": "0.30.0",
82
- "@happyvertical/smrt-profiles": "0.30.0",
83
- "@happyvertical/smrt-vitest": "0.30.0"
81
+ "@happyvertical/smrt-agents": "0.31.1",
82
+ "@happyvertical/smrt-profiles": "0.31.1",
83
+ "@happyvertical/smrt-vitest": "0.31.1"
84
84
  },
85
85
  "scripts": {
86
86
  "build": "vite build --mode library && svelte-package -i src/svelte -o dist/svelte --tsconfig tsconfig.svelte.json",