@meistrari/chat-nuxt 3.9.2 → 3.11.0

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 (106) hide show
  1. package/README.md +50 -440
  2. package/dist/module.d.mts +1 -1
  3. package/dist/module.json +1 -1
  4. package/dist/runtime/assets/css/markstream.css +1 -1
  5. package/dist/runtime/conversations/components/chat-conversation-creator-filter.vue +11 -67
  6. package/dist/runtime/conversations/components/chat-conversation-item.d.vue.ts +2 -1
  7. package/dist/runtime/conversations/components/chat-conversation-item.vue +29 -33
  8. package/dist/runtime/conversations/components/chat-conversation-item.vue.d.ts +2 -1
  9. package/dist/runtime/conversations/components/chat-conversation-list.d.vue.ts +0 -1
  10. package/dist/runtime/conversations/components/chat-conversation-list.vue +117 -158
  11. package/dist/runtime/conversations/components/chat-conversation-list.vue.d.ts +0 -1
  12. package/dist/runtime/conversations/components/chat-conversation-new-group-button.vue +34 -73
  13. package/dist/runtime/conversations/components/chat-conversation-search.vue +1 -21
  14. package/dist/runtime/conversations/components/chat-widget-history-list.d.vue.ts +12 -0
  15. package/dist/runtime/conversations/components/chat-widget-history-list.vue +27 -0
  16. package/dist/runtime/conversations/components/chat-widget-history-list.vue.d.ts +12 -0
  17. package/dist/runtime/conversations/composables/conversation-groups.d.ts +1 -0
  18. package/dist/runtime/conversations/composables/conversation-groups.js +2 -0
  19. package/dist/runtime/conversations/composables/conversation-item.js +4 -4
  20. package/dist/runtime/conversations/composables/conversation-list.js +14 -5
  21. package/dist/runtime/embed/components/chat-configuration-modal.d.vue.ts +3 -3
  22. package/dist/runtime/embed/components/chat-configuration-modal.vue.d.ts +3 -3
  23. package/dist/runtime/embed/components/chat-embed-inner.d.vue.ts +6 -3
  24. package/dist/runtime/embed/components/chat-embed-inner.vue +442 -174
  25. package/dist/runtime/embed/components/chat-embed-inner.vue.d.ts +6 -3
  26. package/dist/runtime/embed/components/chat-embed.d.vue.ts +31 -4
  27. package/dist/runtime/embed/components/chat-embed.vue +10 -4
  28. package/dist/runtime/embed/components/chat-embed.vue.d.ts +31 -4
  29. package/dist/runtime/embed/components/chat-topbar.vue +83 -85
  30. package/dist/runtime/embed/components/meistrari-chat-embed.d.vue.ts +5 -3
  31. package/dist/runtime/embed/components/meistrari-chat-embed.vue +118 -57
  32. package/dist/runtime/embed/components/meistrari-chat-embed.vue.d.ts +5 -3
  33. package/dist/runtime/embed/components/mobile/chat-mobile-drawer.d.vue.ts +0 -1
  34. package/dist/runtime/embed/components/mobile/chat-mobile-drawer.vue +2 -45
  35. package/dist/runtime/embed/components/mobile/chat-mobile-drawer.vue.d.ts +0 -1
  36. package/dist/runtime/embed/components/mobile/chat-mobile-home-suggestions.d.vue.ts +9 -5
  37. package/dist/runtime/embed/components/mobile/chat-mobile-home-suggestions.vue +14 -25
  38. package/dist/runtime/embed/components/mobile/chat-mobile-home-suggestions.vue.d.ts +9 -5
  39. package/dist/runtime/embed/components/mobile/chat-mobile-shell.d.vue.ts +4 -4
  40. package/dist/runtime/embed/components/mobile/chat-mobile-shell.vue +2 -4
  41. package/dist/runtime/embed/components/mobile/chat-mobile-shell.vue.d.ts +4 -4
  42. package/dist/runtime/embed/composables/use-agent-wake.d.ts +23 -0
  43. package/dist/runtime/embed/composables/use-agent-wake.js +182 -0
  44. package/dist/runtime/embed/feature-config.d.ts +7 -0
  45. package/dist/runtime/embed/feature-config.js +2 -1
  46. package/dist/runtime/embed/loading-messages.d.ts +16 -0
  47. package/dist/runtime/embed/loading-messages.js +10 -0
  48. package/dist/runtime/embed/types.d.ts +47 -8
  49. package/dist/runtime/files/icons.d.ts +1 -1
  50. package/dist/runtime/files/icons.js +1 -1
  51. package/dist/runtime/messages/components/chat-mention-dropdown.d.vue.ts +6 -1
  52. package/dist/runtime/messages/components/chat-mention-dropdown.vue +46 -22
  53. package/dist/runtime/messages/components/chat-mention-dropdown.vue.d.ts +6 -1
  54. package/dist/runtime/messages/components/chat-message-bubble.d.vue.ts +10 -1
  55. package/dist/runtime/messages/components/chat-message-bubble.vue +69 -76
  56. package/dist/runtime/messages/components/chat-message-bubble.vue.d.ts +10 -1
  57. package/dist/runtime/messages/components/chat-message-feedback.vue +25 -229
  58. package/dist/runtime/messages/components/chat-message-input.vue +176 -134
  59. package/dist/runtime/messages/components/chat-message-list.d.vue.ts +12 -1
  60. package/dist/runtime/messages/components/chat-message-list.vue +37 -24
  61. package/dist/runtime/messages/components/chat-message-list.vue.d.ts +12 -1
  62. package/dist/runtime/messages/components/chat-model-selector.vue +10 -22
  63. package/dist/runtime/messages/components/reasoning/chat-reasoning-message.vue +2 -3
  64. package/dist/runtime/messages/components/reasoning/chat-reasoning-thought.d.vue.ts +2 -1
  65. package/dist/runtime/messages/components/reasoning/chat-reasoning-thought.vue +69 -77
  66. package/dist/runtime/messages/components/reasoning/chat-reasoning-thought.vue.d.ts +2 -1
  67. package/dist/runtime/messages/components/reasoning/chat-thinking-indicator.vue +3 -9
  68. package/dist/runtime/messages/components/widgets/chat-bash-widget.vue +3 -12
  69. package/dist/runtime/messages/components/widgets/chat-doc-sdk-widget.vue +3 -12
  70. package/dist/runtime/messages/components/widgets/chat-generic-tool-widget.vue +3 -12
  71. package/dist/runtime/messages/components/widgets/chat-grep-widget.vue +7 -17
  72. package/dist/runtime/messages/components/widgets/chat-read-widget.vue +7 -17
  73. package/dist/runtime/messages/components/widgets/chat-tela-skill-widget.vue +4 -13
  74. package/dist/runtime/messages/components/widgets/chat-thinking-widget.vue +11 -22
  75. package/dist/runtime/messages/components/widgets/chat-web-search-widget.vue +13 -40
  76. package/dist/runtime/messages/components/widgets/chat-workstation-query-widget.vue +52 -65
  77. package/dist/runtime/messages/components/widgets/chat-write-widget.vue +8 -18
  78. package/dist/runtime/messages/composables/chat.d.ts +8 -2
  79. package/dist/runtime/messages/composables/chat.js +180 -25
  80. package/dist/runtime/messages/composables/mention-autocomplete.js +4 -0
  81. package/dist/runtime/messages/composables/use-direct-stream.d.ts +41 -0
  82. package/dist/runtime/messages/composables/use-direct-stream.js +263 -0
  83. package/dist/runtime/messages/message-state.js +1 -0
  84. package/dist/runtime/messages/reasoning.d.ts +7 -0
  85. package/dist/runtime/messages/reasoning.js +5 -0
  86. package/dist/runtime/public/files/generic.svg +2 -5
  87. package/dist/runtime/public/files/image.svg +2 -1
  88. package/dist/runtime/types/embed.d.ts +1 -1
  89. package/dist/runtime/types/schemas/chat/conversations.d.ts +4 -0
  90. package/dist/runtime/types/schemas/chat/conversations.js +3 -0
  91. package/dist/runtime/types/schemas/chat/messages.d.ts +43 -0
  92. package/dist/runtime/types/schemas/chat/messages.js +31 -0
  93. package/dist/runtime/types/schemas/chat/models.d.ts +3 -2
  94. package/dist/runtime/types/schemas/chat/models.js +2 -1
  95. package/dist/runtime/types/schemas/chat/ui.d.ts +10 -0
  96. package/dist/runtime/types/schemas/chat/ui.js +1 -0
  97. package/dist/runtime/types/schemas/integrations/tela-agent-api.d.ts +35 -0
  98. package/dist/runtime/types/schemas/integrations/tela-agent-api.js +17 -0
  99. package/dist/types.d.mts +1 -1
  100. package/package.json +5 -4
  101. package/dist/runtime/conversations/components/chat-conversation-new-group-popover-content.d.vue.ts +0 -23
  102. package/dist/runtime/conversations/components/chat-conversation-new-group-popover-content.vue +0 -75
  103. package/dist/runtime/conversations/components/chat-conversation-new-group-popover-content.vue.d.ts +0 -23
  104. package/dist/runtime/messages/components/reasoning/chat-reasoning-step-item.d.vue.ts +0 -37
  105. package/dist/runtime/messages/components/reasoning/chat-reasoning-step-item.vue +0 -125
  106. package/dist/runtime/messages/components/reasoning/chat-reasoning-step-item.vue.d.ts +0 -37
@@ -0,0 +1,182 @@
1
+ import { useChatApi } from "../../shared/composables/chat-api.js";
2
+ const WAKE_POLL_INTERVAL_MS = 1500;
3
+ const WAKE_BUDGET_MS = 3e4;
4
+ const WAKE_SETTLE_TIMEOUT_MS = 35e3;
5
+ export async function pollAgentWake(options) {
6
+ const now = options.now ?? Date.now;
7
+ const deadline = now() + (options.budgetMs ?? WAKE_BUDGET_MS);
8
+ const wait = options.wait ?? ((delayMs, signal) => new Promise((resolve, reject) => {
9
+ let timer;
10
+ const onAbort = () => {
11
+ if (timer)
12
+ clearTimeout(timer);
13
+ reject(new DOMException("Aborted", "AbortError"));
14
+ };
15
+ timer = setTimeout(() => {
16
+ signal?.removeEventListener("abort", onAbort);
17
+ resolve();
18
+ }, delayMs);
19
+ if (signal?.aborted)
20
+ onAbort();
21
+ else
22
+ signal?.addEventListener("abort", onAbort, { once: true });
23
+ }));
24
+ try {
25
+ while (!options.signal?.aborted && now() <= deadline) {
26
+ const response = await options.request();
27
+ options.onResponse?.(response);
28
+ if (response.status !== "waking")
29
+ return response;
30
+ const remaining = deadline - now();
31
+ if (remaining <= 0)
32
+ break;
33
+ await wait(Math.min(options.pollIntervalMs ?? WAKE_POLL_INTERVAL_MS, remaining), options.signal);
34
+ }
35
+ } catch (error) {
36
+ if (error instanceof DOMException && error.name === "AbortError")
37
+ throw error;
38
+ return { status: "unavailable" };
39
+ }
40
+ return { status: "unavailable" };
41
+ }
42
+ export function useAgentWake() {
43
+ const chatApi = useChatApi();
44
+ const snapshot = ref({ state: "idle" });
45
+ const state = computed(() => snapshot.value.state);
46
+ const sessionId = computed(() => snapshot.value.sessionId);
47
+ let generation = 0;
48
+ let abortController = null;
49
+ let activeWake = null;
50
+ function setSnapshot(next) {
51
+ snapshot.value = next;
52
+ }
53
+ function skip() {
54
+ generation++;
55
+ abortController?.abort();
56
+ abortController = null;
57
+ activeWake = null;
58
+ setSnapshot({
59
+ state: "idle",
60
+ ...snapshot.value.sessionId ? { sessionId: snapshot.value.sessionId } : {}
61
+ });
62
+ }
63
+ function consume() {
64
+ generation++;
65
+ abortController?.abort();
66
+ abortController = null;
67
+ activeWake = null;
68
+ setSnapshot({ state: "idle" });
69
+ }
70
+ async function wake(model, requestedSessionId) {
71
+ generation++;
72
+ const currentGeneration = generation;
73
+ abortController?.abort();
74
+ const controller = new AbortController();
75
+ abortController = controller;
76
+ let wakeSessionId = requestedSessionId ?? snapshot.value.sessionId;
77
+ setSnapshot({
78
+ state: "waking",
79
+ ...wakeSessionId ? { sessionId: wakeSessionId } : {},
80
+ ...model ? { model } : {}
81
+ });
82
+ if (import.meta.server) {
83
+ const unavailable = { status: "unavailable" };
84
+ setSnapshot({ state: unavailable.status });
85
+ return unavailable;
86
+ }
87
+ const run = async () => {
88
+ try {
89
+ const result = await pollAgentWake({
90
+ signal: controller.signal,
91
+ request: () => $fetch(
92
+ chatApi.path("/agent/wake"),
93
+ chatApi.withChatHeaders({
94
+ method: "POST",
95
+ signal: controller.signal,
96
+ body: {
97
+ ...wakeSessionId ? { sessionId: wakeSessionId } : {},
98
+ ...model ? { model } : {}
99
+ }
100
+ })
101
+ ),
102
+ onResponse: (response) => {
103
+ if (currentGeneration !== generation)
104
+ return;
105
+ wakeSessionId = response.sessionId ?? wakeSessionId;
106
+ setSnapshot({
107
+ state: response.status,
108
+ ...wakeSessionId ? { sessionId: wakeSessionId } : {},
109
+ ...model ? { model } : {}
110
+ });
111
+ }
112
+ });
113
+ if (currentGeneration === generation) {
114
+ setSnapshot({
115
+ state: result.status,
116
+ ...result.sessionId ? { sessionId: result.sessionId } : {},
117
+ ...model ? { model } : {}
118
+ });
119
+ }
120
+ return result;
121
+ } catch (error) {
122
+ if (!(error instanceof DOMException && error.name === "AbortError") && currentGeneration === generation)
123
+ setSnapshot({ state: "unavailable" });
124
+ return { status: "unavailable" };
125
+ } finally {
126
+ if (abortController === controller)
127
+ abortController = null;
128
+ }
129
+ };
130
+ const pending = run();
131
+ activeWake = pending;
132
+ try {
133
+ return await pending;
134
+ } finally {
135
+ if (activeWake === pending)
136
+ activeWake = null;
137
+ }
138
+ }
139
+ async function waitUntilSettled(signal) {
140
+ if (snapshot.value.state !== "waking" || !activeWake)
141
+ return snapshot.value;
142
+ const pending = activeWake;
143
+ await new Promise((resolve, reject) => {
144
+ let settled = false;
145
+ let timer;
146
+ let onAbort = () => {
147
+ };
148
+ const finish = (error) => {
149
+ if (settled)
150
+ return;
151
+ settled = true;
152
+ if (timer)
153
+ clearTimeout(timer);
154
+ signal?.removeEventListener("abort", onAbort);
155
+ if (error)
156
+ reject(error);
157
+ else
158
+ resolve();
159
+ };
160
+ onAbort = () => finish(new DOMException("Aborted", "AbortError"));
161
+ timer = setTimeout(() => finish(), WAKE_SETTLE_TIMEOUT_MS);
162
+ if (signal?.aborted)
163
+ onAbort();
164
+ else
165
+ signal?.addEventListener("abort", onAbort, { once: true });
166
+ pending.then(() => finish(), () => finish());
167
+ });
168
+ return snapshot.value;
169
+ }
170
+ onUnmounted(() => {
171
+ generation++;
172
+ abortController?.abort();
173
+ });
174
+ return {
175
+ state,
176
+ sessionId,
177
+ wake,
178
+ skip,
179
+ consume,
180
+ waitUntilSettled
181
+ };
182
+ }
@@ -1,11 +1,18 @@
1
+ /** Optional UI toggles the host passes per embed instance. */
1
2
  export type ChatFeatureConfig = {
3
+ /** Usage tab in the chat topbar, with per-conversation token and cost stats. */
2
4
  showUsageTab?: boolean;
5
+ /** Debug entry in the topbar overflow menu. */
3
6
  showDebugOption?: boolean;
7
+ /** "Stop generation" button while a response streams. */
4
8
  showCancelButton?: boolean;
9
+ /** Time to first token shown in assistant message metadata. Defaults to true. */
10
+ showTtft?: boolean;
5
11
  };
6
12
  export type ResolvedChatFeatureConfig = {
7
13
  showUsageTab: boolean;
8
14
  showDebugOption: boolean;
9
15
  showCancelButton: boolean;
16
+ showTtft: boolean;
10
17
  };
11
18
  export declare function resolveChatFeatures(features?: Partial<ChatFeatureConfig>): ResolvedChatFeatureConfig;
@@ -2,6 +2,7 @@ export function resolveChatFeatures(features) {
2
2
  return {
3
3
  showUsageTab: features?.showUsageTab ?? false,
4
4
  showDebugOption: features?.showDebugOption ?? false,
5
- showCancelButton: features?.showCancelButton ?? false
5
+ showCancelButton: features?.showCancelButton ?? false,
6
+ showTtft: features?.showTtft ?? true
6
7
  };
7
8
  }
@@ -1,4 +1,20 @@
1
1
  export declare const DEFAULT_CHAT_LOADING_MESSAGES: readonly ["Analisando sua pergunta", "Processando informações", "Consultando base de conhecimento", "Organizando ideias", "Elaborando resposta", "Refinando detalhes", "Verificando contexto", "Conectando conceitos", "Preparando resultado", "Quase lá", "Finalizando análise", "Estruturando resposta"];
2
+ /**
3
+ * Stand-in steps for a turn that has not produced its own reasoning yet. They
4
+ * accumulate, so the bubble reads as an agent working through the wait rather
5
+ * than as a loading placeholder. The user message is already delivered at this
6
+ * point, so the wait belongs to the answer.
7
+ */
8
+ export declare const CHAT_PREPARING_STEPS: readonly ["Preparando o ambiente de trabalho", "Analisando a mensagem"];
9
+ /**
10
+ * The first delay is measured from the send, every later one from the step
11
+ * before it — so the sequence paces itself instead of racing a shared clock.
12
+ * Late enough that a turn answering promptly never shows a stand-in, and
13
+ * jittered so it does not look like a fixed-timer artifact.
14
+ */
15
+ export declare const CHAT_PREPARING_STEP_DELAYS_MS: readonly [2500, 4000];
16
+ export declare const CHAT_PREPARING_STEP_JITTER_MS = 500;
17
+ export declare function nextChatPreparingStepDelay(stepIndex: number, random?: () => number): number;
2
18
  export type ChatLoadingMessageMode = 'ordered' | 'random';
3
19
  export type ChatLoadingMessagesInput = {
4
20
  loadingMessages?: readonly string[] | null;
@@ -12,6 +12,16 @@ export const DEFAULT_CHAT_LOADING_MESSAGES = [
12
12
  "Finalizando an\xE1lise",
13
13
  "Estruturando resposta"
14
14
  ];
15
+ export const CHAT_PREPARING_STEPS = [
16
+ "Preparando o ambiente de trabalho",
17
+ "Analisando a mensagem"
18
+ ];
19
+ export const CHAT_PREPARING_STEP_DELAYS_MS = [2500, 4e3];
20
+ export const CHAT_PREPARING_STEP_JITTER_MS = 500;
21
+ export function nextChatPreparingStepDelay(stepIndex, random = Math.random) {
22
+ const base = CHAT_PREPARING_STEP_DELAYS_MS[stepIndex] ?? CHAT_PREPARING_STEP_DELAYS_MS.at(-1);
23
+ return base + Math.floor(random() * CHAT_PREPARING_STEP_JITTER_MS);
24
+ }
15
25
  function normalizeMessages(messages) {
16
26
  if (!messages?.length)
17
27
  return [];
@@ -6,6 +6,13 @@ import type { ChatLoadingMessageMode } from './loading-messages.js';
6
6
  import type { ChatFeatureConfig } from './feature-config.js';
7
7
  import type { ChatCitationsConfig } from '../citations/types.js';
8
8
  import type { MessageFeedbackConfig, MessageFeedbackRating } from '../messages/composables/chat-feedback.js';
9
+ export type ChatEmbedVariant = 'page' | 'floating' | 'sidepanel';
10
+ export type ChatSuggestion = {
11
+ /** Short label rendered on the suggestion pill. */
12
+ title: string;
13
+ /** Full prompt written into the message input when the pill is clicked. */
14
+ prompt: string;
15
+ };
9
16
  export type ChatActor = {
10
17
  name?: string | null;
11
18
  email?: string | null;
@@ -35,9 +42,18 @@ export type ChatEmbedSharedProps = {
35
42
  conversationId?: string | null;
36
43
  /** Conversation id to open once on mount when `conversationId` is uncontrolled. */
37
44
  initialConversationId?: string | null;
38
- /** Technical scope that isolates conversation history within the current workspace and runtime. */
45
+ /**
46
+ * Technical scope that isolates conversation history within the current workspace and runtime.
47
+ * Scoped embeds only see conversations created with the same scope, and unscoped embeds only
48
+ * see unscoped ones, so adding a scope never mixes with existing workspace-level history.
49
+ * Values are trimmed, blank strings become `null`, and the server accepts 1-200 characters
50
+ * from `A-Z`, `a-z`, `0-9`, `.`, `_`, `:`, `/`, and `-`.
51
+ */
39
52
  conversationScope?: string | null;
40
- /** Initial creator filter for the sidebar conversation list. Defaults to all conversations. */
53
+ /**
54
+ * Initial creator filter for the sidebar conversation list: `'all'` (default) shows every
55
+ * conversation in scope, `'mine'` shows only those created by the current user.
56
+ */
41
57
  defaultConversationCreatorFilter?: ConversationCreatorFilter;
42
58
  /** Hide the conversation list and render only the active chat surface. */
43
59
  hideSidebar?: boolean;
@@ -46,14 +62,24 @@ export type ChatEmbedSharedProps = {
46
62
  /** Hide workspace settings actions while keeping the chat header and content unchanged. */
47
63
  hideSettings?: boolean;
48
64
  /**
49
- * Hide the empty-state folder onboarding block (PASTAS header, ghost rows, hint
50
- * text and inline create button). Folder creation stays available through the
51
- * new-group button in the sidebar header.
65
+ * Tighter spacing tuned for widget shells (floating/sidepanel). Hides the
66
+ * conversation topbar, since the widget header replaces it. Applied
67
+ * automatically by the `floating` and `sidepanel` variants.
68
+ */
69
+ compact?: boolean;
70
+ /**
71
+ * Suggestion pills rendered in the conversation empty state. Each pill shows
72
+ * its `title` and clicking it writes `prompt` into the message input. When
73
+ * omitted or empty no pills are rendered — the lib ships no defaults.
52
74
  */
53
- hideFolderHint?: boolean;
75
+ suggestions?: readonly ChatSuggestion[] | null;
54
76
  /** Custom pending-response messages shown while the assistant is working. */
55
77
  loadingMessages?: readonly string[] | null;
56
- /** How custom loading messages rotate. Defaults to ordered behavior when omitted. */
78
+ /**
79
+ * How custom loading messages rotate: `'ordered'` (default) cycles them in order,
80
+ * `'random'` picks at random. Only applies when `loadingMessages` has at least one
81
+ * non-empty message.
82
+ */
57
83
  loadingMessagesMode?: ChatLoadingMessageMode | null;
58
84
  /** Custom markdown component renderers keyed by markdown node or tag name. */
59
85
  customComponents?: Record<string, Component>;
@@ -103,4 +129,17 @@ export type TelaAgentChatEmbedProps = ChatEmbedSharedProps & {
103
129
  /** Optional UI feature toggles that are still supported in Tela agent mode. */
104
130
  features?: Partial<ChatFeatureConfig>;
105
131
  };
106
- export type MeistrariChatEmbedProps = DefaultChatEmbedProps | TelaAgentChatEmbedProps;
132
+ export type ChatEmbedVariantProps = {
133
+ /**
134
+ * Presentation shell: `'page'` (default) renders the full-page embed,
135
+ * `'floating'` renders a launcher pill with an anchored popover window, and
136
+ * `'sidepanel'` renders an always-open docked column. Widget variants render
137
+ * client-only and default to `hideSidebar`.
138
+ */
139
+ variant?: ChatEmbedVariant;
140
+ /** Floating variant only: start with the widget window open. */
141
+ defaultOpen?: boolean;
142
+ /** Floating variant only: label of the launcher pill. Defaults to `'Ask Tela'`. */
143
+ launcherLabel?: string;
144
+ };
145
+ export type MeistrariChatEmbedProps = (DefaultChatEmbedProps | TelaAgentChatEmbedProps) & ChatEmbedVariantProps;
@@ -1,4 +1,4 @@
1
1
  export declare const FILE_ICONS: Record<string, string>;
2
- export declare function getFileIcon(mimeType: string): string;
2
+ export declare function getFileIcon(mimeType?: string): string;
3
3
  export declare function getMimeIcon(mimeType?: string): string;
4
4
  export declare function getMimeLabel(mimeType?: string): string;
@@ -42,7 +42,7 @@ export const FILE_ICONS = {
42
42
  "unknown": "/chat-files/generic.svg"
43
43
  };
44
44
  export function getFileIcon(mimeType) {
45
- return FILE_ICONS[mimeType] ?? "/chat-files/generic.svg";
45
+ return FILE_ICONS[mimeType ?? "unknown"] ?? "/chat-files/generic.svg";
46
46
  }
47
47
  export function getMimeIcon(mimeType) {
48
48
  if (!mimeType)
@@ -1,9 +1,14 @@
1
1
  import type { MentionItem } from '../../types/schemas/chat/ui.js';
2
+ type MentionDropdownItem = MentionItem & {
3
+ iconUrl?: string;
4
+ };
2
5
  type __VLS_Props = {
3
- items: MentionItem[];
6
+ items: MentionDropdownItem[];
4
7
  highlightedIndex: number;
5
8
  trigger: '@' | '/';
6
9
  query: string;
10
+ /** Viewport rect of the trigger character ('@' / '/') the panel anchors to. */
11
+ caretRect?: DOMRect | null;
7
12
  };
8
13
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
9
14
  select: (item: MentionItem) => any;
@@ -3,10 +3,21 @@ const props = defineProps({
3
3
  items: { type: Array, required: true },
4
4
  highlightedIndex: { type: Number, required: true },
5
5
  trigger: { type: String, required: true },
6
- query: { type: String, required: true }
6
+ query: { type: String, required: true },
7
+ caretRect: { type: null, required: false }
7
8
  });
8
9
  const emit = defineEmits(["select"]);
9
10
  const listRef = ref(null);
11
+ const panelStyle = computed(() => {
12
+ const rect = props.caretRect;
13
+ if (!rect)
14
+ return {};
15
+ return {
16
+ position: "fixed",
17
+ left: `${Math.max(8, Math.min(rect.left, window.innerWidth - 416))}px`,
18
+ bottom: `${window.innerHeight - rect.top + 4}px`
19
+ };
20
+ });
10
21
  const sections = computed(() => {
11
22
  if (props.trigger === "@") {
12
23
  return [{
@@ -43,41 +54,54 @@ watch(() => props.highlightedIndex, () => {
43
54
  </script>
44
55
 
45
56
  <template>
57
+ <!-- Custom listbox (not a reka menu: it must stay open and keep focus in
58
+ the contenteditable while the user types). Surface and item styling
59
+ mirror TelaMenubarContent / TelaMenubarItem exactly. -->
46
60
  <div
47
- absolute bottom="100%" left-0 right-0 mb-4px
48
- z-10
49
- bg-white
50
- rounded-12px
51
- class="mention-dropdown custom-scrollbar"
52
- max-h-240px
53
- overflow-y-auto
61
+ :style="panelStyle"
62
+ class="mention-dropdown"
63
+ w-max min-w-244px max-w-400px
64
+ max-h-260px overflow-y-auto no-scrollbar
65
+ rounded-2xl
66
+ b-0.5px border-strong
67
+ bg
68
+ p-4px
54
69
  >
55
- <div v-if="items.length === 0" px-12px py-10px>
56
- <span text-13px text-gray-400>{{ emptyMessage }}</span>
70
+ <div v-if="items.length === 0" px-12px py-6px>
71
+ <span body-14-regular text-tertiary>{{ emptyMessage }}</span>
57
72
  </div>
58
73
 
59
- <div ref="listRef" pb-8px flex="~ col" gap-4px>
74
+ <div v-else ref="listRef" flex="~ col">
60
75
  <template v-for="section in sections" :key="section.title">
61
- <div px-12px pt-10px pb-0>
62
- <span text-11px font-500 text-gray-400 uppercase tracking-wide>{{ section.title }}</span>
76
+ <div px-12px pb-2px>
77
+ <span body-12-medium text-tertiary>{{ section.title }}</span>
63
78
  </div>
64
79
  <button
65
80
  v-for="entry in section.items"
66
81
  :key="entry.item.id"
82
+ type="button"
67
83
  :data-highlighted="entry.globalIndex === highlightedIndex"
68
84
  flex items-center gap-8px
69
- w-full
70
- h-48px
71
- px-12px
85
+ w-full min-w-0
86
+ rounded-10px
87
+ px-12px py-4px
72
88
  text-left
73
- transition-colors
74
- :class="entry.globalIndex === highlightedIndex ? 'bg-gray-200' : 'hover:bg-gray-50'"
89
+ cursor-pointer select-none
90
+ outline-none
91
+ :class="entry.globalIndex === highlightedIndex ? 'bg-muted' : 'hover:bg-muted'"
75
92
  @mousedown.prevent="emit('select', entry.item)"
76
93
  >
77
- <div :class="entry.item.icon" text-16px text-gray-500 shrink-0 />
94
+ <img
95
+ v-if="entry.item.iconUrl"
96
+ :src="entry.item.iconUrl"
97
+ alt=""
98
+ aria-hidden="true"
99
+ w-18px h-18px shrink-0
100
+ >
101
+ <TelaIcon v-else :name="entry.item.icon" size="18px" color="icon-secondary" shrink-0 />
78
102
  <div flex="~ col" min-w-0>
79
- <span text-13px text-gray-900 truncate>{{ entry.item.label }}</span>
80
- <span v-if="entry.item.description" text-11px text-gray-400 truncate>{{ entry.item.description }}</span>
103
+ <span body-12-medium text-primary truncate>{{ entry.item.label }}</span>
104
+ <span v-if="entry.item.description" body-10-regular leading-12px text-tertiary truncate>{{ entry.item.description }}</span>
81
105
  </div>
82
106
  </button>
83
107
  </template>
@@ -86,5 +110,5 @@ watch(() => props.highlightedIndex, () => {
86
110
  </template>
87
111
 
88
112
  <style scoped>
89
- .mention-dropdown{animation:mention-dropdown-in .25s ease-out;border:.5px solid #e5e7eb;box-shadow:0 3px 10px 0 hsla(0,0%,64%,.08),0 12px 20px 0 hsla(0,0%,64%,.06)}.custom-scrollbar{scrollbar-color:#d1d5db transparent;scrollbar-width:thin}.custom-scrollbar::-webkit-scrollbar{width:4px}.custom-scrollbar::-webkit-scrollbar-track{background:transparent}.custom-scrollbar::-webkit-scrollbar-thumb{background-color:#d1d5db;border-radius:3px}@keyframes mention-dropdown-in{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}
113
+ .mention-dropdown{bottom:100%;box-shadow:0 4px 16px 0 rgba(85,91,98,.14);margin-left:-4px;position:absolute;z-index:50}
90
114
  </style>
@@ -1,9 +1,14 @@
1
1
  import type { MentionItem } from '../../types/schemas/chat/ui.js';
2
+ type MentionDropdownItem = MentionItem & {
3
+ iconUrl?: string;
4
+ };
2
5
  type __VLS_Props = {
3
- items: MentionItem[];
6
+ items: MentionDropdownItem[];
4
7
  highlightedIndex: number;
5
8
  trigger: '@' | '/';
6
9
  query: string;
10
+ /** Viewport rect of the trigger character ('@' / '/') the panel anchors to. */
11
+ caretRect?: DOMRect | null;
7
12
  };
8
13
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
9
14
  select: (item: MentionItem) => any;
@@ -1,11 +1,20 @@
1
1
  import type { Message } from '../../types/schemas/chat/conversations.js';
2
2
  type __VLS_Props = {
3
3
  message: Message;
4
+ streamActive?: boolean;
5
+ /**
6
+ * Stand-in steps shown while this turn is still working towards its first
7
+ * output. They yield as soon as the run emits real reasoning or content.
8
+ */
9
+ preparingSteps?: readonly string[] | null;
10
+ showTtft?: boolean;
4
11
  };
5
12
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
6
13
  retry: (messageId: string) => any;
7
14
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
8
15
  onRetry?: ((messageId: string) => any) | undefined;
9
- }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
16
+ }>, {
17
+ showTtft: boolean;
18
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
10
19
  declare const _default: typeof __VLS_export;
11
20
  export default _default;