feat: add Umami analytics with tagged events
Add Umami web analytics tracking script to the layout head, and
instrument all interactive elements with event tracking:
Declarative (data-umami-event attributes):
- Category selector: select_category
- Style selector: select_style
- Intensity slider: adjust_intensity
- Prompt toggle: toggle_prompt (with open/close action)
Programmatic (umami.track with metadata):
- convert_click: { style, intensity }
- convert_success: { style, intensity, model }
- convert_error: { style, intensity, error }
- copy_result: { style }
This commit is contained in:
5
src/app.d.ts
vendored
5
src/app.d.ts
vendored
@@ -8,6 +8,11 @@ declare global {
|
||||
// interface PageState {}
|
||||
// interface Platform {}
|
||||
}
|
||||
|
||||
// Umami analytics tracker (injected by script tag)
|
||||
const umami: {
|
||||
track(event: string, data?: Record<string, string | number | boolean>): void;
|
||||
};
|
||||
}
|
||||
|
||||
export {};
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
<svelte:head>
|
||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>✨</text></svg>" />
|
||||
<script defer src="https://wa.santhoshj.com/script.js" data-website-id="1004961a-2b1f-4c45-95a6-78059acac472"></script>
|
||||
</svelte:head>
|
||||
|
||||
{@render children()}
|
||||
@@ -78,6 +78,10 @@
|
||||
async function handleConvert() {
|
||||
if (!canConvert) return;
|
||||
|
||||
if (typeof umami !== 'undefined') {
|
||||
umami.track('convert_click', { style: selectedStyleId, intensity });
|
||||
}
|
||||
|
||||
loading = true;
|
||||
error = '';
|
||||
outputText = '';
|
||||
@@ -108,14 +112,26 @@
|
||||
systemPrompt = result.systemPrompt;
|
||||
userMessage = result.userMessage;
|
||||
modelLabel = result.modelLabel;
|
||||
|
||||
if (typeof umami !== 'undefined') {
|
||||
umami.track('convert_success', { style: selectedStyleId, intensity, model: result.modelLabel });
|
||||
}
|
||||
} catch (err) {
|
||||
error = err instanceof Error ? err.message : 'Something went wrong';
|
||||
|
||||
if (typeof umami !== 'undefined') {
|
||||
umami.track('convert_error', { style: selectedStyleId, intensity, error });
|
||||
}
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function handleCopy() {
|
||||
if (typeof umami !== 'undefined') {
|
||||
umami.track('copy_result', { style: selectedStyleId });
|
||||
}
|
||||
|
||||
try {
|
||||
await navigator.clipboard.writeText(outputText);
|
||||
copied = true;
|
||||
@@ -157,6 +173,7 @@
|
||||
bind:value={selectedCategoryId}
|
||||
onchange={onCategoryChange}
|
||||
disabled={loading}
|
||||
data-umami-event="select_category"
|
||||
>
|
||||
<option value="">Choose a category...</option>
|
||||
{#each categories as cat}
|
||||
@@ -167,7 +184,7 @@
|
||||
|
||||
<div class="form-group">
|
||||
<label for="style">Style</label>
|
||||
<select id="style" bind:value={selectedStyleId} disabled={loading || !selectedCategoryId}>
|
||||
<select id="style" bind:value={selectedStyleId} disabled={loading || !selectedCategoryId} data-umami-event="select_style">
|
||||
{#if !selectedCategoryId}
|
||||
<option value="">Select a category first...</option>
|
||||
{:else if availableStyles.length === 0}
|
||||
@@ -196,6 +213,7 @@
|
||||
step="1"
|
||||
bind:value={intensity}
|
||||
disabled={loading}
|
||||
data-umami-event="adjust_intensity"
|
||||
/>
|
||||
<span class="slider-end">Maximum</span>
|
||||
</div>
|
||||
@@ -240,7 +258,7 @@
|
||||
</div>
|
||||
|
||||
<div class="prompt-section">
|
||||
<button class="prompt-toggle" onclick={() => (showPrompt = !showPrompt)}>
|
||||
<button class="prompt-toggle" onclick={() => (showPrompt = !showPrompt)} data-umami-event="toggle_prompt" data-umami-event-action={showPrompt ? 'close' : 'open'}>
|
||||
{showPrompt ? '▼' : '▶'} Show prompt
|
||||
</button>
|
||||
{#if showPrompt}
|
||||
|
||||
Reference in New Issue
Block a user