@signal9/era-ui 2.2.0 → 2.4.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 (189) hide show
  1. package/dist/ai/actions/action.svelte +41 -0
  2. package/dist/ai/actions/action.svelte.d.ts +12 -0
  3. package/dist/ai/actions/actions.svelte +15 -0
  4. package/dist/ai/actions/actions.svelte.d.ts +8 -0
  5. package/dist/ai/actions/index.d.ts +2 -0
  6. package/dist/ai/actions/index.js +2 -0
  7. package/dist/ai/artifact/artifact-actions.svelte +7 -0
  8. package/dist/ai/artifact/artifact-actions.svelte.d.ts +8 -0
  9. package/dist/ai/artifact/artifact-close.svelte +9 -0
  10. package/dist/ai/artifact/artifact-close.svelte.d.ts +6 -0
  11. package/dist/ai/artifact/artifact-content.svelte +7 -0
  12. package/dist/ai/artifact/artifact-content.svelte.d.ts +8 -0
  13. package/dist/ai/artifact/artifact-description.svelte +7 -0
  14. package/dist/ai/artifact/artifact-description.svelte.d.ts +8 -0
  15. package/dist/ai/artifact/artifact-header.svelte +16 -0
  16. package/dist/ai/artifact/artifact-header.svelte.d.ts +8 -0
  17. package/dist/ai/artifact/artifact-title.svelte +7 -0
  18. package/dist/ai/artifact/artifact-title.svelte.d.ts +8 -0
  19. package/dist/ai/artifact/artifact.svelte +16 -0
  20. package/dist/ai/artifact/artifact.svelte.d.ts +8 -0
  21. package/dist/ai/artifact/index.d.ts +8 -0
  22. package/dist/ai/artifact/index.js +8 -0
  23. package/dist/ai/chain-of-thought/chain-of-thought-step.svelte +39 -0
  24. package/dist/ai/chain-of-thought/chain-of-thought-step.svelte.d.ts +11 -0
  25. package/dist/ai/chain-of-thought/chain-of-thought.svelte +30 -0
  26. package/dist/ai/chain-of-thought/chain-of-thought.svelte.d.ts +11 -0
  27. package/dist/ai/chain-of-thought/index.d.ts +3 -0
  28. package/dist/ai/chain-of-thought/index.js +3 -0
  29. package/dist/ai/checkpoint.svelte +27 -0
  30. package/dist/ai/checkpoint.svelte.d.ts +10 -0
  31. package/dist/ai/confirmation/confirmation-actions.svelte +9 -0
  32. package/dist/ai/confirmation/confirmation-actions.svelte.d.ts +8 -0
  33. package/dist/ai/confirmation/confirmation.svelte +36 -0
  34. package/dist/ai/confirmation/confirmation.svelte.d.ts +11 -0
  35. package/dist/ai/confirmation/index.d.ts +2 -0
  36. package/dist/ai/confirmation/index.js +2 -0
  37. package/dist/ai/context/context.svelte +49 -0
  38. package/dist/ai/context/context.svelte.d.ts +9 -0
  39. package/dist/ai/context/index.d.ts +1 -0
  40. package/dist/ai/context/index.js +1 -0
  41. package/dist/ai/conversation/context.d.ts +3 -0
  42. package/dist/ai/conversation/context.js +12 -0
  43. package/dist/ai/conversation/conversation-content.svelte +32 -0
  44. package/dist/ai/conversation/conversation-content.svelte.d.ts +8 -0
  45. package/dist/ai/conversation/conversation-empty-state.svelte +32 -0
  46. package/dist/ai/conversation/conversation-empty-state.svelte.d.ts +11 -0
  47. package/dist/ai/conversation/conversation-scroll-button.svelte +27 -0
  48. package/dist/ai/conversation/conversation-scroll-button.svelte.d.ts +6 -0
  49. package/dist/ai/conversation/conversation.svelte +21 -0
  50. package/dist/ai/conversation/conversation.svelte.d.ts +8 -0
  51. package/dist/ai/conversation/index.d.ts +5 -0
  52. package/dist/ai/conversation/index.js +5 -0
  53. package/dist/ai/copy-button.svelte +42 -0
  54. package/dist/ai/copy-button.svelte.d.ts +8 -0
  55. package/dist/ai/image.svelte +19 -0
  56. package/dist/ai/image.svelte.d.ts +8 -0
  57. package/dist/ai/index.d.ts +29 -0
  58. package/dist/ai/index.js +31 -0
  59. package/dist/ai/inline-citation/index.d.ts +1 -0
  60. package/dist/ai/inline-citation/index.js +1 -0
  61. package/dist/ai/inline-citation/inline-citation.svelte +66 -0
  62. package/dist/ai/inline-citation/inline-citation.svelte.d.ts +11 -0
  63. package/dist/ai/loader.svelte +8 -0
  64. package/dist/ai/loader.svelte.d.ts +6 -0
  65. package/dist/ai/message/branch.svelte.d.ts +10 -0
  66. package/dist/ai/message/branch.svelte.js +28 -0
  67. package/dist/ai/message/context.d.ts +6 -0
  68. package/dist/ai/message/context.js +1 -0
  69. package/dist/ai/message/index.d.ts +8 -0
  70. package/dist/ai/message/index.js +7 -0
  71. package/dist/ai/message/message-branch-content.svelte +24 -0
  72. package/dist/ai/message/message-branch-content.svelte.d.ts +10 -0
  73. package/dist/ai/message/message-branch-selector.svelte +25 -0
  74. package/dist/ai/message/message-branch-selector.svelte.d.ts +6 -0
  75. package/dist/ai/message/message-branch.svelte +25 -0
  76. package/dist/ai/message/message-branch.svelte.d.ts +9 -0
  77. package/dist/ai/message/message-content.svelte +24 -0
  78. package/dist/ai/message/message-content.svelte.d.ts +8 -0
  79. package/dist/ai/message/message-toolbar.svelte +23 -0
  80. package/dist/ai/message/message-toolbar.svelte.d.ts +8 -0
  81. package/dist/ai/message/message.svelte +27 -0
  82. package/dist/ai/message/message.svelte.d.ts +10 -0
  83. package/dist/ai/model-selector/index.d.ts +1 -0
  84. package/dist/ai/model-selector/index.js +1 -0
  85. package/dist/ai/model-selector/model-selector.svelte +61 -0
  86. package/dist/ai/model-selector/model-selector.svelte.d.ts +15 -0
  87. package/dist/ai/open-in-chat/index.d.ts +1 -0
  88. package/dist/ai/open-in-chat/index.js +1 -0
  89. package/dist/ai/open-in-chat/open-in-chat.svelte +41 -0
  90. package/dist/ai/open-in-chat/open-in-chat.svelte.d.ts +32 -0
  91. package/dist/ai/plan/index.d.ts +3 -0
  92. package/dist/ai/plan/index.js +3 -0
  93. package/dist/ai/plan/plan-content.svelte +13 -0
  94. package/dist/ai/plan/plan-content.svelte.d.ts +8 -0
  95. package/dist/ai/plan/plan-header.svelte +22 -0
  96. package/dist/ai/plan/plan-header.svelte.d.ts +9 -0
  97. package/dist/ai/plan/plan.svelte +23 -0
  98. package/dist/ai/plan/plan.svelte.d.ts +10 -0
  99. package/dist/ai/prompt-input/attachments.svelte.d.ts +27 -0
  100. package/dist/ai/prompt-input/attachments.svelte.js +60 -0
  101. package/dist/ai/prompt-input/index.d.ts +7 -0
  102. package/dist/ai/prompt-input/index.js +7 -0
  103. package/dist/ai/prompt-input/prompt-input-add-attachments.svelte +12 -0
  104. package/dist/ai/prompt-input/prompt-input-add-attachments.svelte.d.ts +6 -0
  105. package/dist/ai/prompt-input/prompt-input-attachments.svelte +40 -0
  106. package/dist/ai/prompt-input/prompt-input-attachments.svelte.d.ts +18 -0
  107. package/dist/ai/prompt-input/prompt-input-submit.svelte +49 -0
  108. package/dist/ai/prompt-input/prompt-input-submit.svelte.d.ts +10 -0
  109. package/dist/ai/prompt-input/prompt-input-textarea.svelte +57 -0
  110. package/dist/ai/prompt-input/prompt-input-textarea.svelte.d.ts +7 -0
  111. package/dist/ai/prompt-input/prompt-input-toolbar.svelte +15 -0
  112. package/dist/ai/prompt-input/prompt-input-toolbar.svelte.d.ts +8 -0
  113. package/dist/ai/prompt-input/prompt-input.svelte +98 -0
  114. package/dist/ai/prompt-input/prompt-input.svelte.d.ts +17 -0
  115. package/dist/ai/queue/index.d.ts +2 -0
  116. package/dist/ai/queue/index.js +2 -0
  117. package/dist/ai/queue/queue-item.svelte +26 -0
  118. package/dist/ai/queue/queue-item.svelte.d.ts +9 -0
  119. package/dist/ai/queue/queue.svelte +14 -0
  120. package/dist/ai/queue/queue.svelte.d.ts +8 -0
  121. package/dist/ai/reasoning/index.d.ts +3 -0
  122. package/dist/ai/reasoning/index.js +3 -0
  123. package/dist/ai/reasoning/reasoning-trigger.svelte +22 -0
  124. package/dist/ai/reasoning/reasoning-trigger.svelte.d.ts +9 -0
  125. package/dist/ai/reasoning/reasoning.svelte +52 -0
  126. package/dist/ai/reasoning/reasoning.svelte.d.ts +13 -0
  127. package/dist/ai/response/index.d.ts +2 -0
  128. package/dist/ai/response/index.js +2 -0
  129. package/dist/ai/response/repair.d.ts +7 -0
  130. package/dist/ai/response/repair.js +26 -0
  131. package/dist/ai/response/response.svelte +112 -0
  132. package/dist/ai/response/response.svelte.d.ts +10 -0
  133. package/dist/ai/shimmer.svelte +15 -0
  134. package/dist/ai/shimmer.svelte.d.ts +9 -0
  135. package/dist/ai/sources/index.d.ts +4 -0
  136. package/dist/ai/sources/index.js +4 -0
  137. package/dist/ai/sources/source.svelte +16 -0
  138. package/dist/ai/sources/source.svelte.d.ts +7 -0
  139. package/dist/ai/sources/sources-trigger.svelte +12 -0
  140. package/dist/ai/sources/sources-trigger.svelte.d.ts +8 -0
  141. package/dist/ai/sources/sources.svelte +15 -0
  142. package/dist/ai/sources/sources.svelte.d.ts +9 -0
  143. package/dist/ai/suggestion/index.d.ts +2 -0
  144. package/dist/ai/suggestion/index.js +2 -0
  145. package/dist/ai/suggestion/suggestion.svelte +25 -0
  146. package/dist/ai/suggestion/suggestion.svelte.d.ts +10 -0
  147. package/dist/ai/suggestion/suggestions.svelte +18 -0
  148. package/dist/ai/suggestion/suggestions.svelte.d.ts +8 -0
  149. package/dist/ai/task/index.d.ts +5 -0
  150. package/dist/ai/task/index.js +5 -0
  151. package/dist/ai/task/task-content.svelte +14 -0
  152. package/dist/ai/task/task-content.svelte.d.ts +8 -0
  153. package/dist/ai/task/task-item-file.svelte +8 -0
  154. package/dist/ai/task/task-item-file.svelte.d.ts +7 -0
  155. package/dist/ai/task/task-item.svelte +8 -0
  156. package/dist/ai/task/task-item.svelte.d.ts +8 -0
  157. package/dist/ai/task/task-trigger.svelte +12 -0
  158. package/dist/ai/task/task-trigger.svelte.d.ts +8 -0
  159. package/dist/ai/task/task.svelte +17 -0
  160. package/dist/ai/task/task.svelte.d.ts +11 -0
  161. package/dist/ai/tool/index.d.ts +5 -0
  162. package/dist/ai/tool/index.js +5 -0
  163. package/dist/ai/tool/tool-header.svelte +20 -0
  164. package/dist/ai/tool/tool-header.svelte.d.ts +9 -0
  165. package/dist/ai/tool/tool-input.svelte +10 -0
  166. package/dist/ai/tool/tool-input.svelte.d.ts +6 -0
  167. package/dist/ai/tool/tool-output.svelte +20 -0
  168. package/dist/ai/tool/tool-output.svelte.d.ts +7 -0
  169. package/dist/ai/tool/tool.svelte +41 -0
  170. package/dist/ai/tool/tool.svelte.d.ts +12 -0
  171. package/dist/ai/web-preview/index.d.ts +1 -0
  172. package/dist/ai/web-preview/index.js +1 -0
  173. package/dist/ai/web-preview/web-preview.svelte +48 -0
  174. package/dist/ai/web-preview/web-preview.svelte.d.ts +8 -0
  175. package/dist/ai/workflow/canvas.svelte +19 -0
  176. package/dist/ai/workflow/canvas.svelte.d.ts +11 -0
  177. package/dist/ai/workflow/index.d.ts +2 -0
  178. package/dist/ai/workflow/index.js +2 -0
  179. package/dist/ai/workflow/node.svelte +27 -0
  180. package/dist/ai/workflow/node.svelte.d.ts +9 -0
  181. package/dist/era-ui.css +1 -1
  182. package/dist/generated-docs/llm-shell.md +3 -0
  183. package/dist/generated-docs/llms-full.txt +8 -0
  184. package/dist/generated-docs/llms.txt +1 -1
  185. package/dist/generated-docs/manifest.json +8 -0
  186. package/dist/index.d.ts +1 -0
  187. package/dist/index.js +1 -0
  188. package/dist/ui/step/step-content.svelte +3 -3
  189. package/package.json +14 -1
@@ -0,0 +1,98 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import type { HTMLFormAttributes } from 'svelte/elements';
4
+ import { cn } from '../../utils/index.js';
5
+ import {
6
+ Attachments,
7
+ setAttachmentsContext,
8
+ type PromptMessage
9
+ } from './attachments.svelte.js';
10
+
11
+ let {
12
+ accept = '',
13
+ maxFiles,
14
+ globalDrop = false,
15
+ clearOnSubmit = true,
16
+ onSubmit,
17
+ onError,
18
+ children,
19
+ class: className,
20
+ ...restProps
21
+ }: Omit<HTMLFormAttributes, 'onsubmit'> & {
22
+ /** Comma-separated accept patterns for attachments (`image/*,application/pdf`). */
23
+ accept?: string;
24
+ maxFiles?: number;
25
+ /** Accept file drops anywhere on the page, not just the form. */
26
+ globalDrop?: boolean;
27
+ clearOnSubmit?: boolean;
28
+ onSubmit?: (message: PromptMessage, event: SubmitEvent) => void | Promise<void>;
29
+ onError?: (code: 'accept' | 'max_files', file: File) => void;
30
+ children?: Snippet;
31
+ } = $props();
32
+
33
+ const attachments = setAttachmentsContext(new Attachments());
34
+ $effect(() => {
35
+ attachments.accept = accept;
36
+ attachments.maxFiles = maxFiles ?? Infinity;
37
+ attachments.onError = onError;
38
+ });
39
+
40
+ let form = $state<HTMLFormElement | null>(null);
41
+
42
+ async function handleSubmit(e: SubmitEvent) {
43
+ e.preventDefault();
44
+ const textarea = form?.querySelector<HTMLTextAreaElement>('textarea[name="message"]');
45
+ const text = textarea?.value.trim() ?? '';
46
+ if (!text && attachments.items.length === 0) return;
47
+ // A throwing onSubmit keeps the draft — retry-friendly.
48
+ await onSubmit?.({ text, attachments: [...attachments.items] }, e);
49
+ if (clearOnSubmit) {
50
+ if (textarea) textarea.value = '';
51
+ textarea?.dispatchEvent(new Event('input', { bubbles: true }));
52
+ attachments.clear();
53
+ }
54
+ }
55
+
56
+ function handleDrop(e: DragEvent) {
57
+ if (!e.dataTransfer?.files.length) return;
58
+ e.preventDefault();
59
+ attachments.add(e.dataTransfer.files);
60
+ }
61
+
62
+ $effect(() => {
63
+ if (!globalDrop) return;
64
+ const over = (e: DragEvent) => e.preventDefault();
65
+ document.addEventListener('dragover', over);
66
+ document.addEventListener('drop', handleDrop);
67
+ return () => {
68
+ document.removeEventListener('dragover', over);
69
+ document.removeEventListener('drop', handleDrop);
70
+ };
71
+ });
72
+ </script>
73
+
74
+ <form
75
+ bind:this={form}
76
+ onsubmit={handleSubmit}
77
+ ondragover={(e) => e.preventDefault()}
78
+ ondrop={handleDrop}
79
+ class={cn(
80
+ 'flex w-full flex-col rounded-(--era-rd-lg) bg-(--era-surface-bg-elevated) p-(--era-gap) shadow-(--era-shadow-well)',
81
+ className
82
+ )}
83
+ {...restProps}
84
+ >
85
+ <input
86
+ bind:this={attachments.fileInput}
87
+ type="file"
88
+ {accept}
89
+ multiple={maxFiles !== 1}
90
+ hidden
91
+ onchange={(e) => {
92
+ const input = e.currentTarget;
93
+ if (input.files) attachments.add(input.files);
94
+ input.value = '';
95
+ }}
96
+ />
97
+ {@render children?.()}
98
+ </form>
@@ -0,0 +1,17 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { HTMLFormAttributes } from 'svelte/elements';
3
+ import { type PromptMessage } from './attachments.svelte.js';
4
+ type $$ComponentProps = Omit<HTMLFormAttributes, 'onsubmit'> & {
5
+ /** Comma-separated accept patterns for attachments (`image/*,application/pdf`). */
6
+ accept?: string;
7
+ maxFiles?: number;
8
+ /** Accept file drops anywhere on the page, not just the form. */
9
+ globalDrop?: boolean;
10
+ clearOnSubmit?: boolean;
11
+ onSubmit?: (message: PromptMessage, event: SubmitEvent) => void | Promise<void>;
12
+ onError?: (code: 'accept' | 'max_files', file: File) => void;
13
+ children?: Snippet;
14
+ };
15
+ declare const PromptInput: import("svelte").Component<$$ComponentProps, {}, "">;
16
+ type PromptInput = ReturnType<typeof PromptInput>;
17
+ export default PromptInput;
@@ -0,0 +1,2 @@
1
+ export { default as Root } from './queue.svelte';
2
+ export { default as Item } from './queue-item.svelte';
@@ -0,0 +1,2 @@
1
+ export { default as Root } from './queue.svelte';
2
+ export { default as Item } from './queue-item.svelte';
@@ -0,0 +1,26 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import { cn } from '../../utils/index.js';
4
+
5
+ let {
6
+ completed = false,
7
+ children,
8
+ class: className
9
+ }: { completed?: boolean; children?: Snippet; class?: string } = $props();
10
+ </script>
11
+
12
+ <li
13
+ class={cn(
14
+ 'flex min-w-0 items-center gap-(--era-gap) rounded-(--era-rd-md) px-(--era-inset-md) py-(--era-gap) text-body hover:bg-(--era-highlight)',
15
+ completed ? 'text-muted line-through' : 'text-fg',
16
+ className
17
+ )}
18
+ >
19
+ <span
20
+ class={cn(
21
+ 'size-2 shrink-0 rounded-full',
22
+ completed ? 'bg-success' : 'border border-divider bg-transparent'
23
+ )}
24
+ ></span>
25
+ <span class="min-w-0 flex-1 truncate">{@render children?.()}</span>
26
+ </li>
@@ -0,0 +1,9 @@
1
+ import type { Snippet } from 'svelte';
2
+ type $$ComponentProps = {
3
+ completed?: boolean;
4
+ children?: Snippet;
5
+ class?: string;
6
+ };
7
+ declare const QueueItem: import("svelte").Component<$$ComponentProps, {}, "">;
8
+ type QueueItem = ReturnType<typeof QueueItem>;
9
+ export default QueueItem;
@@ -0,0 +1,14 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import { cn } from '../../utils/index.js';
4
+ let { children, class: className }: { children?: Snippet; class?: string } = $props();
5
+ </script>
6
+
7
+ <ul
8
+ class={cn(
9
+ 'flex max-h-48 flex-col gap-(--era-gap) overflow-y-auto rounded-(--era-rd-lg) bg-(--era-surface-bg) p-(--era-pad-sm) shadow-(--era-shadow)',
10
+ className
11
+ )}
12
+ >
13
+ {@render children?.()}
14
+ </ul>
@@ -0,0 +1,8 @@
1
+ import type { Snippet } from 'svelte';
2
+ type $$ComponentProps = {
3
+ children?: Snippet;
4
+ class?: string;
5
+ };
6
+ declare const Queue: import("svelte").Component<$$ComponentProps, {}, "">;
7
+ type Queue = ReturnType<typeof Queue>;
8
+ export default Queue;
@@ -0,0 +1,3 @@
1
+ export { default as Root } from './reasoning.svelte';
2
+ export { default as Trigger } from './reasoning-trigger.svelte';
3
+ export { Content } from '../../ui/step/index.js';
@@ -0,0 +1,3 @@
1
+ export { default as Root } from './reasoning.svelte';
2
+ export { default as Trigger } from './reasoning-trigger.svelte';
3
+ export { Content } from '../../ui/step/index.js';
@@ -0,0 +1,22 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import Brain from '@lucide/svelte/icons/brain';
4
+ import * as Step from '../../ui/step/index.js';
5
+
6
+ let {
7
+ streaming = false,
8
+ duration = 0,
9
+ children
10
+ }: { streaming?: boolean; duration?: number; children?: Snippet } = $props();
11
+ </script>
12
+
13
+ <Step.Summary>
14
+ <Brain class="size-(--era-h-xs) shrink-0" aria-hidden="true" />
15
+ {#if children}
16
+ {@render children()}
17
+ {:else if streaming || duration === 0}
18
+ Thinking…
19
+ {:else}
20
+ Thought for {duration} second{duration === 1 ? '' : 's'}
21
+ {/if}
22
+ </Step.Summary>
@@ -0,0 +1,9 @@
1
+ import type { Snippet } from 'svelte';
2
+ type $$ComponentProps = {
3
+ streaming?: boolean;
4
+ duration?: number;
5
+ children?: Snippet;
6
+ };
7
+ declare const ReasoningTrigger: import("svelte").Component<$$ComponentProps, {}, "">;
8
+ type ReasoningTrigger = ReturnType<typeof ReasoningTrigger>;
9
+ export default ReasoningTrigger;
@@ -0,0 +1,52 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import * as Step from '../../ui/step/index.js';
4
+ import { cn } from '../../utils/index.js';
5
+
6
+ let {
7
+ streaming = false,
8
+ open = $bindable(false),
9
+ duration = $bindable(0),
10
+ children,
11
+ class: className
12
+ }: {
13
+ /** True while thinking tokens arrive. */
14
+ streaming?: boolean;
15
+ open?: boolean;
16
+ /** Seconds spent thinking — measured here, readable via bind. */
17
+ duration?: number;
18
+ children?: Snippet;
19
+ class?: string;
20
+ } = $props();
21
+
22
+ // The ChatGPT reasoning choreography: auto-open when thinking starts,
23
+ // measure the duration, auto-close 1s after it ends — once, and never if
24
+ // the user toggled it themselves.
25
+ let userTouched = false;
26
+ let autoClosed = false;
27
+ let startedAt = 0;
28
+ $effect(() => {
29
+ if (streaming) {
30
+ startedAt = Date.now();
31
+ if (!userTouched) open = true;
32
+ } else if (startedAt) {
33
+ duration = Math.max(1, Math.ceil((Date.now() - startedAt) / 1000));
34
+ if (!userTouched && !autoClosed) {
35
+ autoClosed = true;
36
+ const t = setTimeout(() => {
37
+ if (!userTouched) open = false;
38
+ }, 1000);
39
+ return () => clearTimeout(t);
40
+ }
41
+ }
42
+ });
43
+ </script>
44
+
45
+ <Step.Root
46
+ status={streaming ? 'running' : 'done'}
47
+ bind:open
48
+ onOpenChange={() => (userTouched = true)}
49
+ class={cn(className)}
50
+ >
51
+ {@render children?.()}
52
+ </Step.Root>
@@ -0,0 +1,13 @@
1
+ import type { Snippet } from 'svelte';
2
+ type $$ComponentProps = {
3
+ /** True while thinking tokens arrive. */
4
+ streaming?: boolean;
5
+ open?: boolean;
6
+ /** Seconds spent thinking — measured here, readable via bind. */
7
+ duration?: number;
8
+ children?: Snippet;
9
+ class?: string;
10
+ };
11
+ declare const Reasoning: import("svelte").Component<$$ComponentProps, {}, "duration" | "open">;
12
+ type Reasoning = ReturnType<typeof Reasoning>;
13
+ export default Reasoning;
@@ -0,0 +1,2 @@
1
+ export { default as Response } from './response.svelte';
2
+ export { repairStreamingMarkdown } from './repair.js';
@@ -0,0 +1,2 @@
1
+ export { default as Response } from './response.svelte';
2
+ export { repairStreamingMarkdown } from './repair.js';
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Mini-remend: repair the incomplete tail of streaming markdown so the parser
3
+ * never renders half-open syntax (the flash of literal ** mid-stream).
4
+ * Closes unbalanced code fences, inline code, bold and italic runs on the
5
+ * LAST line only — earlier lines are already complete blocks.
6
+ */
7
+ export declare function repairStreamingMarkdown(md: string): string;
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Mini-remend: repair the incomplete tail of streaming markdown so the parser
3
+ * never renders half-open syntax (the flash of literal ** mid-stream).
4
+ * Closes unbalanced code fences, inline code, bold and italic runs on the
5
+ * LAST line only — earlier lines are already complete blocks.
6
+ */
7
+ export function repairStreamingMarkdown(md) {
8
+ let out = md;
9
+ // Unclosed fenced code block: append a virtual closing fence.
10
+ const fences = (out.match(/^```/gm) ?? []).length;
11
+ if (fences % 2 === 1)
12
+ out += '\n```';
13
+ else {
14
+ const tail = out.slice(out.lastIndexOf('\n') + 1);
15
+ if ((tail.match(/(?<!`)`(?!`)/g) ?? []).length % 2 === 1)
16
+ out += '`';
17
+ if ((tail.match(/\*\*/g) ?? []).length % 2 === 1)
18
+ out += '**';
19
+ else {
20
+ const singles = (tail.replace(/\*\*/g, '').match(/\*/g) ?? []).length;
21
+ if (singles % 2 === 1)
22
+ out += '*';
23
+ }
24
+ }
25
+ return out;
26
+ }
@@ -0,0 +1,112 @@
1
+ <script lang="ts">
2
+ import { marked, type Token } from 'marked';
3
+ import DOMPurify from 'dompurify';
4
+ import { CodeBlock } from '../../ui/code-block/index.js';
5
+ import { cn } from '../../utils/index.js';
6
+ import { repairStreamingMarkdown } from './repair.js';
7
+
8
+ let {
9
+ content,
10
+ streaming = false,
11
+ class: className
12
+ }: {
13
+ /** Markdown source — pass the accumulated text on every stream flush. */
14
+ content: string;
15
+ /** Adds the pulsing stream-head cursor and tail repair. */
16
+ streaming?: boolean;
17
+ class?: string;
18
+ } = $props();
19
+
20
+ // Re-lex the full source per flush, but render per BLOCK keyed by index —
21
+ // completed blocks keep their DOM (and their CodeBlock highlights); only
22
+ // the tail block re-renders as it grows (the streamdown memoization trick).
23
+ const blocks = $derived.by((): Token[] =>
24
+ marked.lexer(streaming ? repairStreamingMarkdown(content) : content)
25
+ );
26
+
27
+ function html(token: Token): string {
28
+ const raw = marked.parser([token] as Token[] & { links: Record<string, unknown> });
29
+ return typeof window === 'undefined' ? raw : DOMPurify.sanitize(raw);
30
+ }
31
+ </script>
32
+
33
+ <!-- eslint-disable svelte/no-at-html-tags -- parser output is DOMPurify-sanitized -->
34
+ <!-- eslint-disable better-tailwindcss/no-unknown-classes -- era-response* are defined in this component's <style> -->
35
+ <div
36
+ class={cn(
37
+ 'era-response flex min-w-0 flex-col gap-(--era-gap) text-body leading-relaxed text-fg',
38
+ streaming && 'era-response-streaming',
39
+ className
40
+ )}
41
+ >
42
+ {#each blocks as block, i (i)}
43
+ {#if block.type === 'code'}
44
+ <CodeBlock code={block.text} />
45
+ {:else if block.type !== 'space'}
46
+ <div class="era-response-block min-w-0">{@html html(block)}</div>
47
+ {/if}
48
+ {/each}
49
+ </div>
50
+
51
+ <style>
52
+ /* Markdown element rhythm, scoped — consumers get prose without a plugin. */
53
+ .era-response :global(:is(h1, h2, h3, h4)) {
54
+ color: var(--color-bright);
55
+ font-weight: 500;
56
+ }
57
+ .era-response :global(a) {
58
+ color: var(--color-primary);
59
+ text-decoration: underline;
60
+ text-underline-offset: 2px;
61
+ }
62
+ .era-response :global(ul) {
63
+ list-style: disc;
64
+ padding-inline-start: 1.25em;
65
+ }
66
+ .era-response :global(ol) {
67
+ list-style: decimal;
68
+ padding-inline-start: 1.25em;
69
+ }
70
+ .era-response :global(li + li) {
71
+ margin-top: 0.25em;
72
+ }
73
+ .era-response :global(blockquote) {
74
+ border-inline-start: 2px solid var(--color-border);
75
+ padding-inline-start: var(--era-pad-sm);
76
+ color: var(--color-muted);
77
+ }
78
+ .era-response :global(code) {
79
+ font-family: var(--font-mono, monospace);
80
+ background: var(--color-2);
81
+ border-radius: var(--era-rd-xxs);
82
+ padding-inline: 0.25em;
83
+ }
84
+ .era-response :global(strong) {
85
+ color: var(--color-bright);
86
+ }
87
+ .era-response :global(table) {
88
+ border-collapse: collapse;
89
+ }
90
+ .era-response :global(:is(th, td)) {
91
+ border: 1px solid var(--color-border);
92
+ padding: calc(var(--era-gap) / 2) var(--era-gap);
93
+ text-align: left;
94
+ }
95
+
96
+ /* Stream-head cursor: a pulsing dot after the last block's last element.
97
+ A pseudo-element, so it survives block re-renders without JS placement;
98
+ pulse timing derives from the motion axis (static dot at instant). */
99
+ .era-response-streaming :global(.era-response-block:last-child > :last-child)::after {
100
+ content: '●';
101
+ display: inline-block;
102
+ margin-inline-start: 0.25em;
103
+ font-size: 0.6em;
104
+ vertical-align: baseline;
105
+ animation: era-cursor calc(var(--era-duration) * 6) steps(2, start) infinite;
106
+ }
107
+ @keyframes -global-era-cursor {
108
+ 50% {
109
+ opacity: 0;
110
+ }
111
+ }
112
+ </style>
@@ -0,0 +1,10 @@
1
+ type $$ComponentProps = {
2
+ /** Markdown source — pass the accumulated text on every stream flush. */
3
+ content: string;
4
+ /** Adds the pulsing stream-head cursor and tail repair. */
5
+ streaming?: boolean;
6
+ class?: string;
7
+ };
8
+ declare const Response: import("svelte").Component<$$ComponentProps, {}, "">;
9
+ type Response = ReturnType<typeof Response>;
10
+ export default Response;
@@ -0,0 +1,15 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import { cn } from '../utils/index.js';
4
+
5
+ let {
6
+ as = 'p',
7
+ children,
8
+ class: className
9
+ }: { as?: string; children?: Snippet; class?: string } = $props();
10
+ </script>
11
+
12
+ <!-- The era-shimmer utility already derives its sweep from the motion axis. -->
13
+ <svelte:element this={as} class={cn('era-shimmer text-body', className)}>
14
+ {@render children?.()}
15
+ </svelte:element>
@@ -0,0 +1,9 @@
1
+ import type { Snippet } from 'svelte';
2
+ type $$ComponentProps = {
3
+ as?: string;
4
+ children?: Snippet;
5
+ class?: string;
6
+ };
7
+ declare const Shimmer: import("svelte").Component<$$ComponentProps, {}, "">;
8
+ type Shimmer = ReturnType<typeof Shimmer>;
9
+ export default Shimmer;
@@ -0,0 +1,4 @@
1
+ export { default as Root } from './sources.svelte';
2
+ export { default as Trigger } from './sources-trigger.svelte';
3
+ export { default as Source } from './source.svelte';
4
+ export { Content } from '../../ui/step/index.js';
@@ -0,0 +1,4 @@
1
+ export { default as Root } from './sources.svelte';
2
+ export { default as Trigger } from './sources-trigger.svelte';
3
+ export { default as Source } from './source.svelte';
4
+ export { Content } from '../../ui/step/index.js';
@@ -0,0 +1,16 @@
1
+ <script lang="ts">
2
+ import BookOpen from '@lucide/svelte/icons/book-open';
3
+
4
+ let { href, title }: { href: string; title?: string } = $props();
5
+ </script>
6
+
7
+ <!-- eslint-disable svelte/no-navigation-without-resolve -- external source URLs -->
8
+ <a
9
+ {href}
10
+ target="_blank"
11
+ rel="noreferrer"
12
+ class="flex items-center gap-(--era-gap) text-body text-fg hover:text-bright hover:underline"
13
+ >
14
+ <BookOpen class="size-(--era-h-xs) shrink-0 text-muted" aria-hidden="true" />
15
+ <span class="truncate">{title ?? href}</span>
16
+ </a>
@@ -0,0 +1,7 @@
1
+ type $$ComponentProps = {
2
+ href: string;
3
+ title?: string;
4
+ };
5
+ declare const Source: import("svelte").Component<$$ComponentProps, {}, "">;
6
+ type Source = ReturnType<typeof Source>;
7
+ export default Source;
@@ -0,0 +1,12 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import BookMarked from '@lucide/svelte/icons/book-marked';
4
+ import * as Step from '../../ui/step/index.js';
5
+
6
+ let { count, children }: { count?: number; children?: Snippet } = $props();
7
+ </script>
8
+
9
+ <Step.Summary>
10
+ <BookMarked class="size-(--era-h-xs) shrink-0" aria-hidden="true" />
11
+ {#if children}{@render children()}{:else}Used {count} source{count === 1 ? '' : 's'}{/if}
12
+ </Step.Summary>
@@ -0,0 +1,8 @@
1
+ import type { Snippet } from 'svelte';
2
+ type $$ComponentProps = {
3
+ count?: number;
4
+ children?: Snippet;
5
+ };
6
+ declare const SourcesTrigger: import("svelte").Component<$$ComponentProps, {}, "">;
7
+ type SourcesTrigger = ReturnType<typeof SourcesTrigger>;
8
+ export default SourcesTrigger;
@@ -0,0 +1,15 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import * as Step from '../../ui/step/index.js';
4
+ import { cn } from '../../utils/index.js';
5
+
6
+ let {
7
+ open = $bindable(false),
8
+ children,
9
+ class: className
10
+ }: { open?: boolean; children?: Snippet; class?: string } = $props();
11
+ </script>
12
+
13
+ <Step.Root status="done" bind:open class={cn(className)}>
14
+ {@render children?.()}
15
+ </Step.Root>
@@ -0,0 +1,9 @@
1
+ import type { Snippet } from 'svelte';
2
+ type $$ComponentProps = {
3
+ open?: boolean;
4
+ children?: Snippet;
5
+ class?: string;
6
+ };
7
+ declare const Sources: import("svelte").Component<$$ComponentProps, {}, "open">;
8
+ type Sources = ReturnType<typeof Sources>;
9
+ export default Sources;
@@ -0,0 +1,2 @@
1
+ export { default as Root } from './suggestions.svelte';
2
+ export { default as Suggestion } from './suggestion.svelte';
@@ -0,0 +1,2 @@
1
+ export { default as Root } from './suggestions.svelte';
2
+ export { default as Suggestion } from './suggestion.svelte';
@@ -0,0 +1,25 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import { Button } from '../../ui/button/index.js';
4
+ import { cn } from '../../utils/index.js';
5
+
6
+ let {
7
+ suggestion,
8
+ onSelect,
9
+ children,
10
+ class: className
11
+ }: {
12
+ suggestion: string;
13
+ onSelect?: (suggestion: string) => void;
14
+ children?: Snippet;
15
+ class?: string;
16
+ } = $props();
17
+ </script>
18
+
19
+ <Button
20
+ size="sm"
21
+ class={cn('shrink-0 rounded-full', className)}
22
+ onclick={() => onSelect?.(suggestion)}
23
+ >
24
+ {#if children}{@render children()}{:else}{suggestion}{/if}
25
+ </Button>
@@ -0,0 +1,10 @@
1
+ import type { Snippet } from 'svelte';
2
+ type $$ComponentProps = {
3
+ suggestion: string;
4
+ onSelect?: (suggestion: string) => void;
5
+ children?: Snippet;
6
+ class?: string;
7
+ };
8
+ declare const Suggestion: import("svelte").Component<$$ComponentProps, {}, "">;
9
+ type Suggestion = ReturnType<typeof Suggestion>;
10
+ export default Suggestion;
@@ -0,0 +1,18 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import type { HTMLAttributes } from 'svelte/elements';
4
+ import { cn } from '../../utils/index.js';
5
+
6
+ let {
7
+ children,
8
+ class: className,
9
+ ...restProps
10
+ }: HTMLAttributes<HTMLDivElement> & { children?: Snippet } = $props();
11
+ </script>
12
+
13
+ <div
14
+ class={cn('scrollbar-none flex w-full flex-nowrap items-center gap-(--era-gap) overflow-x-auto', className)}
15
+ {...restProps}
16
+ >
17
+ {@render children?.()}
18
+ </div>
@@ -0,0 +1,8 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { HTMLAttributes } from 'svelte/elements';
3
+ type $$ComponentProps = HTMLAttributes<HTMLDivElement> & {
4
+ children?: Snippet;
5
+ };
6
+ declare const Suggestions: import("svelte").Component<$$ComponentProps, {}, "">;
7
+ type Suggestions = ReturnType<typeof Suggestions>;
8
+ export default Suggestions;