@svadmin/ui 0.65.1 → 0.67.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/app.css +0 -51
- package/dist/app.theme.css +0 -51
- package/dist/components/AdminApp.svelte +5 -2
- package/dist/components/AdminApp.svelte.d.ts +3 -0
- package/dist/components/CommandPalette.svelte +3 -1
- package/dist/components/Layout.svelte +20 -12
- package/dist/components/Layout.svelte.d.ts +6 -0
- package/dist/components/MetricStrip.svelte +149 -0
- package/dist/components/MetricStrip.svelte.d.ts +32 -0
- package/dist/components/ui/badge/badge.svelte +5 -0
- package/dist/components/ui/badge/badge.svelte.d.ts +15 -0
- package/dist/components/ui/table/table-cell.svelte +14 -2
- package/dist/components/ui/table/table-cell.svelte.d.ts +4 -1
- package/dist/components/ui/table/table-head.svelte +14 -2
- package/dist/components/ui/table/table-head.svelte.d.ts +4 -1
- package/dist/components/ui/table/table-row.svelte +1 -1
- package/dist/index.d.ts +3 -7
- package/dist/index.js +1 -7
- package/package.json +3 -14
- package/dist/components/AICommandBar.svelte +0 -228
- package/dist/components/AICommandBar.svelte.d.ts +0 -6
- package/dist/components/ChatDialog.svelte +0 -947
- package/dist/components/ChatDialog.svelte.d.ts +0 -24
- package/dist/components/CopilotPanel.svelte +0 -204
- package/dist/components/CopilotPanel.svelte.d.ts +0 -6
- package/dist/components/InsightCard.svelte +0 -139
- package/dist/components/InsightCard.svelte.d.ts +0 -11
- package/dist/components/MarkdownRenderer.svelte +0 -245
- package/dist/components/MarkdownRenderer.svelte.d.ts +0 -11
- package/dist/components/SmartSuggest.svelte +0 -207
- package/dist/components/SmartSuggest.svelte.d.ts +0 -19
- package/dist/components/VoiceInput.svelte +0 -133
- package/dist/components/VoiceInput.svelte.d.ts +0 -15
- package/dist/markdown-deps.d.ts +0 -21
|
@@ -1,207 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import { Input } from './ui/input/index.js';
|
|
3
|
-
import { captureAdminContext } from '@svadmin/core';
|
|
4
|
-
import type { ChatProvider } from '@svadmin/core';
|
|
5
|
-
|
|
6
|
-
interface Props {
|
|
7
|
-
/** The bound value of the input */
|
|
8
|
-
value: string;
|
|
9
|
-
/** The chat provider to generate suggestions */
|
|
10
|
-
provider?: ChatProvider;
|
|
11
|
-
/** Context given to the AI to help it predict the text */
|
|
12
|
-
context?: string;
|
|
13
|
-
/** Standard input props */
|
|
14
|
-
placeholder?: string;
|
|
15
|
-
class?: string;
|
|
16
|
-
id?: string;
|
|
17
|
-
disabled?: boolean;
|
|
18
|
-
name?: string;
|
|
19
|
-
type?: string;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
let {
|
|
23
|
-
value = $bindable(),
|
|
24
|
-
provider,
|
|
25
|
-
context = '',
|
|
26
|
-
placeholder = '',
|
|
27
|
-
class: className = '',
|
|
28
|
-
id,
|
|
29
|
-
disabled = false,
|
|
30
|
-
name,
|
|
31
|
-
type = 'text'
|
|
32
|
-
}: Props = $props();
|
|
33
|
-
const adminContext = captureAdminContext();
|
|
34
|
-
|
|
35
|
-
let suggestion = $state('');
|
|
36
|
-
let isPredicting = $state(false);
|
|
37
|
-
let abortController: AbortController | null = null;
|
|
38
|
-
let predictTimer: ReturnType<typeof setTimeout> | null = null;
|
|
39
|
-
let requestEpoch = 0;
|
|
40
|
-
let observedValue = value;
|
|
41
|
-
|
|
42
|
-
// The ghost text shows the suggestion, but only the part *after* what the user has typed
|
|
43
|
-
// It only displays if the suggestion actually starts with the current value
|
|
44
|
-
const ghostText = $derived.by(() => {
|
|
45
|
-
if (!suggestion || !value) return '';
|
|
46
|
-
const lowerVal = value.toLowerCase();
|
|
47
|
-
const lowerSugg = suggestion.toLowerCase();
|
|
48
|
-
|
|
49
|
-
if (lowerSugg.startsWith(lowerVal) && suggestion.length > value.length) {
|
|
50
|
-
// Preserve original casing from the suggestion but append only the suffix
|
|
51
|
-
return value + suggestion.slice(value.length);
|
|
52
|
-
}
|
|
53
|
-
return '';
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
const suffixText = $derived(ghostText ? ghostText.slice(value.length) : '');
|
|
57
|
-
const resolvedProvider = $derived(provider ?? adminContext.chatProvider);
|
|
58
|
-
|
|
59
|
-
$effect(() => {
|
|
60
|
-
void resolvedProvider;
|
|
61
|
-
void context;
|
|
62
|
-
void adminContext.tenantCacheKey?.__svadminTenant;
|
|
63
|
-
clearSuggestionScope();
|
|
64
|
-
|
|
65
|
-
return cancelPrediction;
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
$effect(() => {
|
|
69
|
-
const currentValue = value;
|
|
70
|
-
if (currentValue !== observedValue) {
|
|
71
|
-
observedValue = currentValue;
|
|
72
|
-
clearSuggestionScope();
|
|
73
|
-
}
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
function cancelPrediction() {
|
|
77
|
-
requestEpoch += 1;
|
|
78
|
-
abortController?.abort();
|
|
79
|
-
abortController = null;
|
|
80
|
-
if (predictTimer) clearTimeout(predictTimer);
|
|
81
|
-
predictTimer = null;
|
|
82
|
-
isPredicting = false;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function clearSuggestionScope() {
|
|
86
|
-
cancelPrediction();
|
|
87
|
-
suggestion = '';
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
function isCurrentPrediction(epoch: number, scopedValue: string) {
|
|
91
|
-
return epoch === requestEpoch && value === scopedValue;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
async function predict() {
|
|
95
|
-
predictTimer = null;
|
|
96
|
-
const scopedProvider = resolvedProvider;
|
|
97
|
-
const scopedValue = value;
|
|
98
|
-
const scopedContext = context;
|
|
99
|
-
if (!scopedProvider || !scopedValue.trim()) {
|
|
100
|
-
suggestion = '';
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
cancelPrediction();
|
|
105
|
-
const epoch = requestEpoch;
|
|
106
|
-
const controller = new AbortController();
|
|
107
|
-
abortController = controller;
|
|
108
|
-
isPredicting = true;
|
|
109
|
-
suggestion = '';
|
|
110
|
-
|
|
111
|
-
try {
|
|
112
|
-
const prompt = `You are an autocomplete engine. The user is typing: "${scopedValue}".
|
|
113
|
-
Context: ${scopedContext}.
|
|
114
|
-
Provide ONLY the most likely completion of the exact phrase the user is typing.
|
|
115
|
-
Include the user's input in your returned string.
|
|
116
|
-
Do not include any other text, Markdown formatting, quotes, or explanations.`;
|
|
117
|
-
|
|
118
|
-
const result = scopedProvider.sendMessage(
|
|
119
|
-
[{ id: 'prompt', role: 'user', content: prompt, timestamp: Date.now() }],
|
|
120
|
-
{ signal: controller.signal }
|
|
121
|
-
);
|
|
122
|
-
|
|
123
|
-
if (Symbol.asyncIterator in result) {
|
|
124
|
-
for await (const chunk of result as AsyncIterable<string>) {
|
|
125
|
-
if (!isCurrentPrediction(epoch, scopedValue)) return;
|
|
126
|
-
suggestion += chunk;
|
|
127
|
-
}
|
|
128
|
-
} else {
|
|
129
|
-
const response = await result;
|
|
130
|
-
if (isCurrentPrediction(epoch, scopedValue)) suggestion = response;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
if (isCurrentPrediction(epoch, scopedValue)) {
|
|
134
|
-
suggestion = suggestion.trim().replace(/^["']|["']$/g, '');
|
|
135
|
-
}
|
|
136
|
-
} catch (err: unknown) {
|
|
137
|
-
if (isCurrentPrediction(epoch, scopedValue) && err instanceof Error && err.name !== 'AbortError') {
|
|
138
|
-
suggestion = '';
|
|
139
|
-
}
|
|
140
|
-
} finally {
|
|
141
|
-
if (isCurrentPrediction(epoch, scopedValue)) {
|
|
142
|
-
isPredicting = false;
|
|
143
|
-
if (abortController === controller) abortController = null;
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
function handleInput(e: Event) {
|
|
149
|
-
const target = e.target as HTMLInputElement;
|
|
150
|
-
cancelPrediction();
|
|
151
|
-
observedValue = target.value;
|
|
152
|
-
value = target.value;
|
|
153
|
-
|
|
154
|
-
// Reset suggestion immediately if it no longer matches
|
|
155
|
-
if (suggestion && !suggestion.toLowerCase().startsWith(value.toLowerCase())) {
|
|
156
|
-
suggestion = '';
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
predictTimer = setTimeout(() => {
|
|
160
|
-
void predict();
|
|
161
|
-
}, 500);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
function handleKeyDown(e: KeyboardEvent) {
|
|
165
|
-
if (e.key === 'Tab' && suffixText) {
|
|
166
|
-
e.preventDefault();
|
|
167
|
-
cancelPrediction();
|
|
168
|
-
observedValue = ghostText;
|
|
169
|
-
value = ghostText;
|
|
170
|
-
suggestion = '';
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
</script>
|
|
175
|
-
|
|
176
|
-
<div class="relative w-full {className}">
|
|
177
|
-
<!-- Ghost Text Layer (positioned exactly beneath the actual text) -->
|
|
178
|
-
{#if suffixText}
|
|
179
|
-
<div
|
|
180
|
-
class="absolute inset-0 pointer-events-none flex items-center px-3 text-sm font-medium"
|
|
181
|
-
aria-hidden="true"
|
|
182
|
-
>
|
|
183
|
-
<span class="opacity-0">{value}</span>
|
|
184
|
-
<span class="text-muted-foreground/40">{suffixText}</span>
|
|
185
|
-
</div>
|
|
186
|
-
{/if}
|
|
187
|
-
|
|
188
|
-
<!-- Actual Input Layer -->
|
|
189
|
-
<Input
|
|
190
|
-
{id}
|
|
191
|
-
{name}
|
|
192
|
-
{type}
|
|
193
|
-
{disabled}
|
|
194
|
-
{placeholder}
|
|
195
|
-
value={value}
|
|
196
|
-
oninput={handleInput}
|
|
197
|
-
onkeydown={handleKeyDown}
|
|
198
|
-
class="bg-transparent relative z-10 w-full"
|
|
199
|
-
/>
|
|
200
|
-
|
|
201
|
-
<!-- Loading indicator -->
|
|
202
|
-
{#if isPredicting}
|
|
203
|
-
<div class="absolute right-3 top-1/2 -translate-y-1/2 pointer-events-none">
|
|
204
|
-
<div class="h-1.5 w-1.5 rounded-full bg-primary/50 animate-pulse"></div>
|
|
205
|
-
</div>
|
|
206
|
-
{/if}
|
|
207
|
-
</div>
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import type { ChatProvider } from '@svadmin/core';
|
|
2
|
-
interface Props {
|
|
3
|
-
/** The bound value of the input */
|
|
4
|
-
value: string;
|
|
5
|
-
/** The chat provider to generate suggestions */
|
|
6
|
-
provider?: ChatProvider;
|
|
7
|
-
/** Context given to the AI to help it predict the text */
|
|
8
|
-
context?: string;
|
|
9
|
-
/** Standard input props */
|
|
10
|
-
placeholder?: string;
|
|
11
|
-
class?: string;
|
|
12
|
-
id?: string;
|
|
13
|
-
disabled?: boolean;
|
|
14
|
-
name?: string;
|
|
15
|
-
type?: string;
|
|
16
|
-
}
|
|
17
|
-
declare const SmartSuggest: import("svelte").Component<Props, {}, "value">;
|
|
18
|
-
type SmartSuggest = ReturnType<typeof SmartSuggest>;
|
|
19
|
-
export default SmartSuggest;
|
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
/* eslint-disable no-undef */
|
|
3
|
-
import { Mic, MicOff, Loader2 } from '@lucide/svelte';
|
|
4
|
-
import TooltipButton from './TooltipButton.svelte';
|
|
5
|
-
|
|
6
|
-
// Web Speech API type declarations
|
|
7
|
-
interface SpeechRecognitionEvent extends Event {
|
|
8
|
-
results: SpeechRecognitionResultList;
|
|
9
|
-
}
|
|
10
|
-
interface SpeechRecognitionErrorEvent extends Event {
|
|
11
|
-
error: string;
|
|
12
|
-
}
|
|
13
|
-
interface SpeechRecognitionInstance extends EventTarget {
|
|
14
|
-
continuous: boolean;
|
|
15
|
-
interimResults: boolean;
|
|
16
|
-
lang: string;
|
|
17
|
-
onstart: (() => void) | null;
|
|
18
|
-
onresult: ((event: SpeechRecognitionEvent) => void) | null;
|
|
19
|
-
onerror: ((event: SpeechRecognitionErrorEvent) => void) | null;
|
|
20
|
-
onend: (() => void) | null;
|
|
21
|
-
start(): void;
|
|
22
|
-
stop(): void;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
interface Props {
|
|
26
|
-
/** Called when a speech recognition result is finalized */
|
|
27
|
-
onresult?: (text: string) => void;
|
|
28
|
-
/** Called when the engine starts listening */
|
|
29
|
-
onstart?: () => void;
|
|
30
|
-
/** Called when the engine stops listening */
|
|
31
|
-
onend?: () => void;
|
|
32
|
-
/** Standard button size */
|
|
33
|
-
size?: 'default' | 'sm' | 'lg' | 'icon' | 'icon-sm';
|
|
34
|
-
/** Custom class for the button */
|
|
35
|
-
class?: string;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
let {
|
|
39
|
-
onresult,
|
|
40
|
-
onstart,
|
|
41
|
-
onend,
|
|
42
|
-
size = 'icon',
|
|
43
|
-
class: className = ''
|
|
44
|
-
}: Props = $props();
|
|
45
|
-
|
|
46
|
-
let isListening = $state(false);
|
|
47
|
-
let notSupported = $state(false);
|
|
48
|
-
let recognition: SpeechRecognitionInstance | null = null;
|
|
49
|
-
|
|
50
|
-
// Initialize SpeechRecognition on mount
|
|
51
|
-
$effect(() => {
|
|
52
|
-
const SpeechRecognitionCtor = (
|
|
53
|
-
(globalThis as Record<string, unknown>).SpeechRecognition ||
|
|
54
|
-
(globalThis as Record<string, unknown>).webkitSpeechRecognition
|
|
55
|
-
) as (new () => SpeechRecognitionInstance) | undefined;
|
|
56
|
-
|
|
57
|
-
if (!SpeechRecognitionCtor) {
|
|
58
|
-
notSupported = true;
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
recognition = new SpeechRecognitionCtor();
|
|
63
|
-
recognition.continuous = false;
|
|
64
|
-
recognition.interimResults = false;
|
|
65
|
-
recognition.lang = navigator.language || 'en-US';
|
|
66
|
-
|
|
67
|
-
recognition.onstart = () => {
|
|
68
|
-
isListening = true;
|
|
69
|
-
if (onstart) onstart();
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
recognition.onresult = (event: SpeechRecognitionEvent) => {
|
|
73
|
-
const transcript = Array.from(event.results)
|
|
74
|
-
.map((result: SpeechRecognitionResult) => result[0])
|
|
75
|
-
.map((result: SpeechRecognitionAlternative) => result.transcript)
|
|
76
|
-
.join('');
|
|
77
|
-
|
|
78
|
-
if (onresult && transcript) {
|
|
79
|
-
onresult(transcript);
|
|
80
|
-
}
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
recognition.onerror = (event: SpeechRecognitionErrorEvent) => {
|
|
84
|
-
console.warn('SpeechRecognition error:', event.error);
|
|
85
|
-
isListening = false;
|
|
86
|
-
if (onend) onend();
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
recognition.onend = () => {
|
|
90
|
-
isListening = false;
|
|
91
|
-
if (onend) onend();
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
return () => {
|
|
95
|
-
if (recognition) {
|
|
96
|
-
isListening = false;
|
|
97
|
-
recognition.onend = () => {};
|
|
98
|
-
recognition.stop();
|
|
99
|
-
recognition.onstart = null;
|
|
100
|
-
recognition.onresult = null;
|
|
101
|
-
recognition.onerror = null;
|
|
102
|
-
recognition.onend = null;
|
|
103
|
-
}
|
|
104
|
-
};
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
function toggleListening(e?: MouseEvent) {
|
|
108
|
-
if (e) e.preventDefault();
|
|
109
|
-
if (!recognition) return;
|
|
110
|
-
|
|
111
|
-
if (isListening) {
|
|
112
|
-
recognition.stop();
|
|
113
|
-
} else {
|
|
114
|
-
recognition.start();
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
</script>
|
|
118
|
-
|
|
119
|
-
<TooltipButton
|
|
120
|
-
tooltip={notSupported ? "Voice input not supported in this browser" : (isListening ? "Stop listening" : "Start voice input")}
|
|
121
|
-
variant={isListening ? "destructive" : "ghost"}
|
|
122
|
-
{size}
|
|
123
|
-
class="{isListening ? 'animate-pulse' : ''} {className}"
|
|
124
|
-
onclick={toggleListening}
|
|
125
|
-
disabled={notSupported}
|
|
126
|
-
>
|
|
127
|
-
{#if isListening}
|
|
128
|
-
<Loader2 class="h-4 w-4 animate-spin -ml-1 mr-1.5 inline-block sm:hidden" />
|
|
129
|
-
<MicOff class="h-4 w-4" />
|
|
130
|
-
{:else}
|
|
131
|
-
<Mic class="h-4 w-4" />
|
|
132
|
-
{/if}
|
|
133
|
-
</TooltipButton>
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
interface Props {
|
|
2
|
-
/** Called when a speech recognition result is finalized */
|
|
3
|
-
onresult?: (text: string) => void;
|
|
4
|
-
/** Called when the engine starts listening */
|
|
5
|
-
onstart?: () => void;
|
|
6
|
-
/** Called when the engine stops listening */
|
|
7
|
-
onend?: () => void;
|
|
8
|
-
/** Standard button size */
|
|
9
|
-
size?: 'default' | 'sm' | 'lg' | 'icon' | 'icon-sm';
|
|
10
|
-
/** Custom class for the button */
|
|
11
|
-
class?: string;
|
|
12
|
-
}
|
|
13
|
-
declare const VoiceInput: import("svelte").Component<Props, {}, "">;
|
|
14
|
-
type VoiceInput = ReturnType<typeof VoiceInput>;
|
|
15
|
-
export default VoiceInput;
|
package/dist/markdown-deps.d.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
// Type declarations for MarkdownRenderer dependencies
|
|
2
|
-
declare module 'marked' {
|
|
3
|
-
export class Marked {
|
|
4
|
-
constructor(...extensions: unknown[]);
|
|
5
|
-
parse(src: string): string;
|
|
6
|
-
}
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
declare module 'marked-highlight' {
|
|
10
|
-
export function markedHighlight(options: {
|
|
11
|
-
langPrefix?: string;
|
|
12
|
-
highlight: (code: string, lang: string) => string;
|
|
13
|
-
}): unknown;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
declare module 'isomorphic-dompurify' {
|
|
17
|
-
const DOMPurify: {
|
|
18
|
-
sanitize(dirty: string, config?: Record<string, unknown>): string;
|
|
19
|
-
};
|
|
20
|
-
export default DOMPurify;
|
|
21
|
-
}
|