@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.
- package/dist/ai/actions/action.svelte +41 -0
- package/dist/ai/actions/action.svelte.d.ts +12 -0
- package/dist/ai/actions/actions.svelte +15 -0
- package/dist/ai/actions/actions.svelte.d.ts +8 -0
- package/dist/ai/actions/index.d.ts +2 -0
- package/dist/ai/actions/index.js +2 -0
- package/dist/ai/artifact/artifact-actions.svelte +7 -0
- package/dist/ai/artifact/artifact-actions.svelte.d.ts +8 -0
- package/dist/ai/artifact/artifact-close.svelte +9 -0
- package/dist/ai/artifact/artifact-close.svelte.d.ts +6 -0
- package/dist/ai/artifact/artifact-content.svelte +7 -0
- package/dist/ai/artifact/artifact-content.svelte.d.ts +8 -0
- package/dist/ai/artifact/artifact-description.svelte +7 -0
- package/dist/ai/artifact/artifact-description.svelte.d.ts +8 -0
- package/dist/ai/artifact/artifact-header.svelte +16 -0
- package/dist/ai/artifact/artifact-header.svelte.d.ts +8 -0
- package/dist/ai/artifact/artifact-title.svelte +7 -0
- package/dist/ai/artifact/artifact-title.svelte.d.ts +8 -0
- package/dist/ai/artifact/artifact.svelte +16 -0
- package/dist/ai/artifact/artifact.svelte.d.ts +8 -0
- package/dist/ai/artifact/index.d.ts +8 -0
- package/dist/ai/artifact/index.js +8 -0
- package/dist/ai/chain-of-thought/chain-of-thought-step.svelte +39 -0
- package/dist/ai/chain-of-thought/chain-of-thought-step.svelte.d.ts +11 -0
- package/dist/ai/chain-of-thought/chain-of-thought.svelte +30 -0
- package/dist/ai/chain-of-thought/chain-of-thought.svelte.d.ts +11 -0
- package/dist/ai/chain-of-thought/index.d.ts +3 -0
- package/dist/ai/chain-of-thought/index.js +3 -0
- package/dist/ai/checkpoint.svelte +27 -0
- package/dist/ai/checkpoint.svelte.d.ts +10 -0
- package/dist/ai/confirmation/confirmation-actions.svelte +9 -0
- package/dist/ai/confirmation/confirmation-actions.svelte.d.ts +8 -0
- package/dist/ai/confirmation/confirmation.svelte +36 -0
- package/dist/ai/confirmation/confirmation.svelte.d.ts +11 -0
- package/dist/ai/confirmation/index.d.ts +2 -0
- package/dist/ai/confirmation/index.js +2 -0
- package/dist/ai/context/context.svelte +49 -0
- package/dist/ai/context/context.svelte.d.ts +9 -0
- package/dist/ai/context/index.d.ts +1 -0
- package/dist/ai/context/index.js +1 -0
- package/dist/ai/conversation/context.d.ts +3 -0
- package/dist/ai/conversation/context.js +12 -0
- package/dist/ai/conversation/conversation-content.svelte +32 -0
- package/dist/ai/conversation/conversation-content.svelte.d.ts +8 -0
- package/dist/ai/conversation/conversation-empty-state.svelte +32 -0
- package/dist/ai/conversation/conversation-empty-state.svelte.d.ts +11 -0
- package/dist/ai/conversation/conversation-scroll-button.svelte +27 -0
- package/dist/ai/conversation/conversation-scroll-button.svelte.d.ts +6 -0
- package/dist/ai/conversation/conversation.svelte +21 -0
- package/dist/ai/conversation/conversation.svelte.d.ts +8 -0
- package/dist/ai/conversation/index.d.ts +5 -0
- package/dist/ai/conversation/index.js +5 -0
- package/dist/ai/copy-button.svelte +42 -0
- package/dist/ai/copy-button.svelte.d.ts +8 -0
- package/dist/ai/image.svelte +19 -0
- package/dist/ai/image.svelte.d.ts +8 -0
- package/dist/ai/index.d.ts +29 -0
- package/dist/ai/index.js +31 -0
- package/dist/ai/inline-citation/index.d.ts +1 -0
- package/dist/ai/inline-citation/index.js +1 -0
- package/dist/ai/inline-citation/inline-citation.svelte +66 -0
- package/dist/ai/inline-citation/inline-citation.svelte.d.ts +11 -0
- package/dist/ai/loader.svelte +8 -0
- package/dist/ai/loader.svelte.d.ts +6 -0
- package/dist/ai/message/branch.svelte.d.ts +10 -0
- package/dist/ai/message/branch.svelte.js +28 -0
- package/dist/ai/message/context.d.ts +6 -0
- package/dist/ai/message/context.js +1 -0
- package/dist/ai/message/index.d.ts +8 -0
- package/dist/ai/message/index.js +7 -0
- package/dist/ai/message/message-branch-content.svelte +24 -0
- package/dist/ai/message/message-branch-content.svelte.d.ts +10 -0
- package/dist/ai/message/message-branch-selector.svelte +25 -0
- package/dist/ai/message/message-branch-selector.svelte.d.ts +6 -0
- package/dist/ai/message/message-branch.svelte +25 -0
- package/dist/ai/message/message-branch.svelte.d.ts +9 -0
- package/dist/ai/message/message-content.svelte +24 -0
- package/dist/ai/message/message-content.svelte.d.ts +8 -0
- package/dist/ai/message/message-toolbar.svelte +23 -0
- package/dist/ai/message/message-toolbar.svelte.d.ts +8 -0
- package/dist/ai/message/message.svelte +27 -0
- package/dist/ai/message/message.svelte.d.ts +10 -0
- package/dist/ai/model-selector/index.d.ts +1 -0
- package/dist/ai/model-selector/index.js +1 -0
- package/dist/ai/model-selector/model-selector.svelte +61 -0
- package/dist/ai/model-selector/model-selector.svelte.d.ts +15 -0
- package/dist/ai/open-in-chat/index.d.ts +1 -0
- package/dist/ai/open-in-chat/index.js +1 -0
- package/dist/ai/open-in-chat/open-in-chat.svelte +41 -0
- package/dist/ai/open-in-chat/open-in-chat.svelte.d.ts +32 -0
- package/dist/ai/plan/index.d.ts +3 -0
- package/dist/ai/plan/index.js +3 -0
- package/dist/ai/plan/plan-content.svelte +13 -0
- package/dist/ai/plan/plan-content.svelte.d.ts +8 -0
- package/dist/ai/plan/plan-header.svelte +22 -0
- package/dist/ai/plan/plan-header.svelte.d.ts +9 -0
- package/dist/ai/plan/plan.svelte +23 -0
- package/dist/ai/plan/plan.svelte.d.ts +10 -0
- package/dist/ai/prompt-input/attachments.svelte.d.ts +27 -0
- package/dist/ai/prompt-input/attachments.svelte.js +60 -0
- package/dist/ai/prompt-input/index.d.ts +7 -0
- package/dist/ai/prompt-input/index.js +7 -0
- package/dist/ai/prompt-input/prompt-input-add-attachments.svelte +12 -0
- package/dist/ai/prompt-input/prompt-input-add-attachments.svelte.d.ts +6 -0
- package/dist/ai/prompt-input/prompt-input-attachments.svelte +40 -0
- package/dist/ai/prompt-input/prompt-input-attachments.svelte.d.ts +18 -0
- package/dist/ai/prompt-input/prompt-input-submit.svelte +49 -0
- package/dist/ai/prompt-input/prompt-input-submit.svelte.d.ts +10 -0
- package/dist/ai/prompt-input/prompt-input-textarea.svelte +57 -0
- package/dist/ai/prompt-input/prompt-input-textarea.svelte.d.ts +7 -0
- package/dist/ai/prompt-input/prompt-input-toolbar.svelte +15 -0
- package/dist/ai/prompt-input/prompt-input-toolbar.svelte.d.ts +8 -0
- package/dist/ai/prompt-input/prompt-input.svelte +98 -0
- package/dist/ai/prompt-input/prompt-input.svelte.d.ts +17 -0
- package/dist/ai/queue/index.d.ts +2 -0
- package/dist/ai/queue/index.js +2 -0
- package/dist/ai/queue/queue-item.svelte +26 -0
- package/dist/ai/queue/queue-item.svelte.d.ts +9 -0
- package/dist/ai/queue/queue.svelte +14 -0
- package/dist/ai/queue/queue.svelte.d.ts +8 -0
- package/dist/ai/reasoning/index.d.ts +3 -0
- package/dist/ai/reasoning/index.js +3 -0
- package/dist/ai/reasoning/reasoning-trigger.svelte +22 -0
- package/dist/ai/reasoning/reasoning-trigger.svelte.d.ts +9 -0
- package/dist/ai/reasoning/reasoning.svelte +52 -0
- package/dist/ai/reasoning/reasoning.svelte.d.ts +13 -0
- package/dist/ai/response/index.d.ts +2 -0
- package/dist/ai/response/index.js +2 -0
- package/dist/ai/response/repair.d.ts +7 -0
- package/dist/ai/response/repair.js +26 -0
- package/dist/ai/response/response.svelte +112 -0
- package/dist/ai/response/response.svelte.d.ts +10 -0
- package/dist/ai/shimmer.svelte +15 -0
- package/dist/ai/shimmer.svelte.d.ts +9 -0
- package/dist/ai/sources/index.d.ts +4 -0
- package/dist/ai/sources/index.js +4 -0
- package/dist/ai/sources/source.svelte +16 -0
- package/dist/ai/sources/source.svelte.d.ts +7 -0
- package/dist/ai/sources/sources-trigger.svelte +12 -0
- package/dist/ai/sources/sources-trigger.svelte.d.ts +8 -0
- package/dist/ai/sources/sources.svelte +15 -0
- package/dist/ai/sources/sources.svelte.d.ts +9 -0
- package/dist/ai/suggestion/index.d.ts +2 -0
- package/dist/ai/suggestion/index.js +2 -0
- package/dist/ai/suggestion/suggestion.svelte +25 -0
- package/dist/ai/suggestion/suggestion.svelte.d.ts +10 -0
- package/dist/ai/suggestion/suggestions.svelte +18 -0
- package/dist/ai/suggestion/suggestions.svelte.d.ts +8 -0
- package/dist/ai/task/index.d.ts +5 -0
- package/dist/ai/task/index.js +5 -0
- package/dist/ai/task/task-content.svelte +14 -0
- package/dist/ai/task/task-content.svelte.d.ts +8 -0
- package/dist/ai/task/task-item-file.svelte +8 -0
- package/dist/ai/task/task-item-file.svelte.d.ts +7 -0
- package/dist/ai/task/task-item.svelte +8 -0
- package/dist/ai/task/task-item.svelte.d.ts +8 -0
- package/dist/ai/task/task-trigger.svelte +12 -0
- package/dist/ai/task/task-trigger.svelte.d.ts +8 -0
- package/dist/ai/task/task.svelte +17 -0
- package/dist/ai/task/task.svelte.d.ts +11 -0
- package/dist/ai/tool/index.d.ts +5 -0
- package/dist/ai/tool/index.js +5 -0
- package/dist/ai/tool/tool-header.svelte +20 -0
- package/dist/ai/tool/tool-header.svelte.d.ts +9 -0
- package/dist/ai/tool/tool-input.svelte +10 -0
- package/dist/ai/tool/tool-input.svelte.d.ts +6 -0
- package/dist/ai/tool/tool-output.svelte +20 -0
- package/dist/ai/tool/tool-output.svelte.d.ts +7 -0
- package/dist/ai/tool/tool.svelte +41 -0
- package/dist/ai/tool/tool.svelte.d.ts +12 -0
- package/dist/ai/web-preview/index.d.ts +1 -0
- package/dist/ai/web-preview/index.js +1 -0
- package/dist/ai/web-preview/web-preview.svelte +48 -0
- package/dist/ai/web-preview/web-preview.svelte.d.ts +8 -0
- package/dist/ai/workflow/canvas.svelte +19 -0
- package/dist/ai/workflow/canvas.svelte.d.ts +11 -0
- package/dist/ai/workflow/index.d.ts +2 -0
- package/dist/ai/workflow/index.js +2 -0
- package/dist/ai/workflow/node.svelte +27 -0
- package/dist/ai/workflow/node.svelte.d.ts +9 -0
- package/dist/era-ui.css +1 -1
- package/dist/generated-docs/llm-shell.md +3 -0
- package/dist/generated-docs/llms-full.txt +8 -0
- package/dist/generated-docs/llms.txt +1 -1
- package/dist/generated-docs/manifest.json +8 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/ui/step/step-content.svelte +3 -3
- 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,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,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,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,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,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,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;
|