Skip to content
Merged
Show file tree
Hide file tree
Changes from 146 commits
Commits
Show all changes
148 commits
Select commit Hold shift + click to select a range
4011a4b
add tailwind
ken-zlai Sep 24, 2024
f46c568
format code w/ prettier
ken-zlai Sep 24, 2024
6a05ff7
add shadcn
ken-zlai Sep 24, 2024
2a9aad1
add shadcn button
ken-zlai Sep 24, 2024
f96c4fe
use button on home page for simple counter
ken-zlai Sep 24, 2024
570ba5d
npm run format
ken-zlai Sep 24, 2024
2b2bddc
modify typescript rules to ignore specific errors in shadcn components
ken-zlai Sep 24, 2024
53e759e
configure tailwind typography
ken-zlai Sep 24, 2024
8dab5f7
Merge branch 'ken-websvc' into ken-frontend
ken-zlai Sep 25, 2024
71895c7
basic dark mode config
ken-zlai Sep 25, 2024
d0b55ea
ignore warnings from node_modules folder
ken-zlai Sep 25, 2024
1e11bc2
configure testing stuff
ken-zlai Sep 25, 2024
3205caa
readme
ken-zlai Sep 25, 2024
afb9aea
move tests
ken-zlai Sep 25, 2024
05bce39
dark mode should be applied in the app.html
ken-zlai Sep 25, 2024
1c31f4a
create a reusable echart component
ken-zlai Sep 26, 2024
8f5bd92
basic heatmap to start testing
ken-zlai Sep 26, 2024
201dc8e
install echarts
ken-zlai Sep 26, 2024
251a052
fix type issues
ken-zlai Sep 26, 2024
a77da82
add sheet component
ken-zlai Sep 26, 2024
6d3a1e9
use resizeobserver on my echart
ken-zlai Sep 27, 2024
7462ed4
refactor echart for clarity
ken-zlai Sep 27, 2024
b2270f6
use proper type
ken-zlai Sep 27, 2024
cf8c8e6
svelte 5 state
ken-zlai Sep 27, 2024
ea4ba7b
create SplitView.svelte
ken-zlai Sep 27, 2024
db32508
use SplitView to show heatmap/sidebar
ken-zlai Sep 27, 2024
f8060b1
Merge branch 'piyush--apis' into ken-frontend
ken-zlai Sep 30, 2024
fffd0ed
add table component
ken-zlai Sep 30, 2024
ea0cd3c
index to export table elements
ken-zlai Sep 30, 2024
fd3702b
add basic api
ken-zlai Sep 30, 2024
7eb1bea
type for model
ken-zlai Sep 30, 2024
00f3807
format code
ken-zlai Sep 30, 2024
dc76a03
add ModelsResponse
ken-zlai Sep 30, 2024
998956a
render a basic table with models data
ken-zlai Sep 30, 2024
7ab1d5d
change route structure
ken-zlai Sep 30, 2024
79268df
add preview of timeseries data
ken-zlai Sep 30, 2024
8e9327c
add avatar
ken-zlai Sep 30, 2024
1855d2e
zipline logo
ken-zlai Sep 30, 2024
3d76751
Wire up Play frontend + server in docker setup
piyush-zlai Sep 30, 2024
c3f728a
create a layout for the app w/ top and left bar
ken-zlai Sep 30, 2024
578ed5b
move model code to model route
ken-zlai Sep 30, 2024
a284edb
add loading indication on nav
ken-zlai Sep 30, 2024
114511f
Merge branch 'main' into ken-frontend
ken-zlai Sep 30, 2024
7e7d697
Fix based on coder rabbit suggestions
piyush-zlai Sep 30, 2024
557555b
Address feedback
piyush-zlai Oct 1, 2024
5691a3c
Merge remote-tracking branch 'origin/piyush/docker-play' into ken-fro…
ken-zlai Oct 1, 2024
34da3a4
configure frontend to build using nodejs
ken-zlai Oct 1, 2024
811cb65
create a custom build for docker to use that env file
ken-zlai Oct 1, 2024
6b27bd3
add a quick coming soon to the / route
ken-zlai Oct 1, 2024
115faa3
zipline logo routes to / route
ken-zlai Oct 1, 2024
9e63629
quick header for timeseries data
ken-zlai Oct 1, 2024
c7cb4a4
Merge branch 'main' into ken-frontend
ken-zlai Oct 1, 2024
cf2e418
remove old frontend stuff from build.sbt
ken-zlai Oct 1, 2024
463eefe
update docker readme
ken-zlai Oct 1, 2024
5ade424
format
ken-zlai Oct 1, 2024
b26c672
add link to the heatmap preview
ken-zlai Oct 1, 2024
6a75611
add instructions for how to start the backend server first for dev work
ken-zlai Oct 2, 2024
f84133a
add tests for core api functionality
ken-zlai Oct 2, 2024
6c52f28
.env files were ignored in the .gitignore - remove this
ken-zlai Oct 2, 2024
4750136
cleaner way to run docker build
ken-zlai Oct 2, 2024
045858f
Merge branch 'ken-frontend' into frontend-tests
ken-zlai Oct 2, 2024
eb11d7b
create tests that verify backend types for model
ken-zlai Oct 3, 2024
a6b1a47
remove comments
ken-zlai Oct 3, 2024
1f7426a
Merge branch 'main' into frontend-tests
ken-zlai Oct 3, 2024
5eb7ccb
delete old test file
ken-zlai Oct 3, 2024
2c58662
delete old model file
ken-zlai Oct 3, 2024
83f97d6
fix import
ken-zlai Oct 3, 2024
735cd00
layout has left nav and main content for children
ken-zlai Oct 3, 2024
d67ecdb
add breadcrumb component
ken-zlai Oct 3, 2024
b1bfe04
basic breadcrumb implementation
ken-zlai Oct 3, 2024
42da51f
use slash for breadcrumb separator
ken-zlai Oct 3, 2024
7b038a3
format
ken-zlai Oct 3, 2024
ee70ba8
componentize stuff for reusability
ken-zlai Oct 3, 2024
3a5186f
add command and dialog component
ken-zlai Oct 3, 2024
3acadbb
swap colors of navbar and content
ken-zlai Oct 3, 2024
1e22b0c
match the sidebar tabs a bit more to the figma
ken-zlai Oct 3, 2024
1990a3e
basic search bar/dialog
ken-zlai Oct 3, 2024
8cf2523
create functions for our api calls
ken-zlai Oct 3, 2024
62fe84b
api code to search
ken-zlai Oct 3, 2024
ef2d709
basic search implementation
ken-zlai Oct 4, 2024
875e178
create a basic coming soon page and show it on groupbys and joins page
ken-zlai Oct 4, 2024
c933ce1
add badge component
ken-zlai Oct 4, 2024
25d40ba
show more content in the table
ken-zlai Oct 4, 2024
d4df9cd
update package-lock.json versions
ken-zlai Oct 4, 2024
1f564c9
inject env variable from node server
ken-zlai Oct 4, 2024
229889d
add back the .env to gitignore until they are needed
ken-zlai Oct 4, 2024
ef91a4c
cors config for local dev and node docker dev
ken-zlai Oct 4, 2024
c71555a
remove the --force
ken-zlai Oct 4, 2024
10624d1
Merge branch 'cors-env-fix' into frontend-8/4-milestone
ken-zlai Oct 4, 2024
050c6e4
Update compose.yaml
ken-zlai Oct 4, 2024
3bcef16
Merge branch 'cors-env-fix' into frontend-8/4-milestone
ken-zlai Oct 4, 2024
6b72d52
fix test
ken-zlai Oct 4, 2024
c21731d
Merge branch 'cors-env-fix' into frontend-8/4-milestone
ken-zlai Oct 4, 2024
435dd43
note about echarts
ken-zlai Oct 4, 2024
b9b1c7c
route to proper link on click on search result
ken-zlai Oct 4, 2024
9086595
fix data not updating
ken-zlai Oct 4, 2024
38210d2
close dialog on click of button
ken-zlai Oct 4, 2024
def0089
change to onclick
ken-zlai Oct 4, 2024
02e5cd1
make full search result button clickable
ken-zlai Oct 4, 2024
e6db76d
Merge branch 'main' into frontend-8/4-milestone
ken-zlai Oct 7, 2024
8e7a261
cleanup debounce on component destroy
ken-zlai Oct 8, 2024
d1a4d9e
Merge remote-tracking branch 'origin/frontend-8/4-milestone' into fro…
ken-zlai Oct 8, 2024
d67b964
Merge branch 'main' into frontend-8/4-milestone
ken-zlai Oct 8, 2024
9f5c1bf
Merge branch 'main' into frontend-8/4-milestone
ken-zlai Oct 8, 2024
c3894a0
create a model performance chart
ken-zlai Oct 8, 2024
040e81d
use dark mode in echarts
ken-zlai Oct 9, 2024
df1c9c2
add select component
ken-zlai Oct 9, 2024
b51a0ec
date selector to change graphs
ken-zlai Oct 9, 2024
d6e0bbf
fix ts issues
ken-zlai Oct 9, 2024
5a39284
add collabsible component
ken-zlai Oct 9, 2024
f82084d
make model performance collabsible
ken-zlai Oct 9, 2024
30dff77
add tabs component
ken-zlai Oct 9, 2024
eb2eaaa
add a section for feature monitoring
ken-zlai Oct 9, 2024
d1511d2
create reusable collabsible component
ken-zlai Oct 9, 2024
4e7a057
format code
ken-zlai Oct 9, 2024
4cdc767
fix value type
ken-zlai Oct 9, 2024
4c7cdb7
use svelte 5 concepts
ken-zlai Oct 9, 2024
1ec77f1
make breadcrumbs fixed at top
ken-zlai Oct 9, 2024
aa04f19
observability page title
ken-zlai Oct 9, 2024
01c4f5c
mock for performance vs drift
ken-zlai Oct 10, 2024
f31fbf7
remove placeholder
ken-zlai Oct 10, 2024
cdfe18b
add dropdown menu
ken-zlai Oct 10, 2024
a5e1fe4
cleaner ux for logged in user in nav bar
ken-zlai Oct 10, 2024
b089ebc
connect the two charts
ken-zlai Oct 10, 2024
7566666
use chart instance here
ken-zlai Oct 10, 2024
f644fec
chart for each groupby w/ sync
ken-zlai Oct 11, 2024
6f7704c
chart axis from 0-1
ken-zlai Oct 11, 2024
87df29d
highlight the line that was hovered in the groupby charts
ken-zlai Oct 11, 2024
e8ed826
rename stuff
ken-zlai Oct 11, 2024
171656c
dont show smooth lines
ken-zlai Oct 11, 2024
a75c82b
update favicon
ken-zlai Oct 11, 2024
b04e046
add scroll area
ken-zlai Oct 11, 2024
a832d43
open sheet when point is clicked
ken-zlai Oct 11, 2024
8c7c403
add logic to show all points that are overlapping
ken-zlai Oct 11, 2024
e9a1906
scroll on sheet content
ken-zlai Oct 11, 2024
5e36233
use scroll area
ken-zlai Oct 11, 2024
7eacd77
put each groupby chart in a collabsible section
ken-zlai Oct 11, 2024
ffe177c
click and drag chart area to zoom
ken-zlai Oct 14, 2024
40ead78
create a derived store for all charts
ken-zlai Oct 14, 2024
bf4d972
only show reset zoom if chart is zoomed
ken-zlai Oct 14, 2024
b7a3465
change cursor to pointer if they move the mouse over a data point
ken-zlai Oct 14, 2024
67a779c
no animation for right sheet
ken-zlai Oct 14, 2024
a252dd1
scale dots to click on
ken-zlai Oct 14, 2024
0c185ea
ensure chart zoom works after changing date range
ken-zlai Oct 14, 2024
5d57400
Merge branch 'main' into frontend-8/14-milestone
ken-zlai Oct 14, 2024
163c505
remove redundant slash
ken-zlai Oct 14, 2024
228a3ba
use lodash merge
ken-zlai Oct 14, 2024
5582156
Merge branch 'main' into frontend-8/14-milestone
ken-zlai Oct 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 26 additions & 1 deletion frontend/src/lib/api/api.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import type { ModelsResponse, TimeSeriesResponse } from '$lib/types/Model/Model';
import type {
JoinTimeSeriesResponse,
ModelsResponse,
TimeSeriesResponse
} from '$lib/types/Model/Model';
import { error } from '@sveltejs/kit';
import { browser } from '$app/environment';

Expand Down Expand Up @@ -52,3 +56,24 @@ export async function search(term: string, limit: number = 20): Promise<ModelsRe
});
return get(`search?${params.toString()}`);
}

export async function getJoinTimeseries(
joinId: string,
startTs: number,
endTs: number,
metricType: string = 'drift',
metrics: string = 'null',
offset: string = '10h',
algorithm: string = 'psi'
): Promise<JoinTimeSeriesResponse> {
const params = new URLSearchParams({
startTs: startTs.toString(),
endTs: endTs.toString(),
metricType,
metrics,
offset,
algorithm
});

return get(`join/${joinId}/timeseries?${params.toString()}`);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<script lang="ts">
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger
} from '$lib/components/ui/collapsible';
import type { Snippet } from 'svelte';
import { ChevronDown } from 'svelte-radix';

let {
children,
title,
open = $bindable(true)
}: { children: Snippet; title: string; open: boolean } = $props();
</script>

<Collapsible bind:open class="mt-4">
<CollapsibleTrigger class="flex items-center w-full">
<ChevronDown
class="w-5 h-5 mr-2 transition-transform duration-200 {open ? '' : 'rotate-180'}"
/>
<h2 class="text-xl">{title}</h2>
</CollapsibleTrigger>
<CollapsibleContent>
{@render children()}
</CollapsibleContent>
</Collapsible>
36 changes: 31 additions & 5 deletions frontend/src/lib/components/EChart/EChart.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,46 @@
import * as echarts from 'echarts';
import type { ECElementEvent, EChartOption } from 'echarts';
const { option }: { option: EChartOption } = $props();
let {
option,
chartInstance = $bindable(),
enableMousemove = false
}: {
option: EChartOption;
chartInstance: echarts.ECharts | null;
enableMousemove?: boolean;
} = $props();
const dispatch = createEventDispatcher();
let chartDiv: HTMLDivElement;
let chartInstance: echarts.ECharts;
let resizeObserver: ResizeObserver;
// todo this needs to change dynamically when we support light mode
const theme = $derived('dark');
const defaultOption: EChartOption = {
backgroundColor: 'transparent'
};
const mergedOption: EChartOption = $derived.by(() => {
return { ...defaultOption, ...option };
});
function initChart() {
if (!chartDiv) return;
chartInstance?.dispose();
chartInstance = echarts.init(chartDiv);
chartInstance.setOption(option);
chartInstance = echarts.init(chartDiv, theme);
chartInstance.setOption(mergedOption);
chartInstance.on('click', (params: ECElementEvent) => dispatch('click', params));
chartInstance.on('datazoom', (params: EChartOption.DataZoom) => dispatch('datazoom', params));
if (enableMousemove) {
chartInstance.on('mousemove', (params: ECElementEvent) => {
if (params.componentType === 'series') {
chartInstance?.getZr().setCursorStyle('pointer');
}
});
}
}
function handleResize() {
Expand All @@ -35,7 +61,7 @@
});
$effect(() => {
chartInstance?.setOption(option, true);
chartInstance?.setOption(mergedOption, true);
});
</script>

Expand Down
32 changes: 25 additions & 7 deletions frontend/src/lib/components/NavigationBar/NavigationBar.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@
import type { Model } from '$lib/types/Model/Model';
import debounce from 'lodash/debounce';
import { onDestroy } from 'svelte';
import {
DropdownMenu,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownMenuItem
} from '$lib/components/ui/dropdown-menu/';
import { ChevronDown } from 'svelte-radix';
type Props = {
navItems: { label: string; href: string }[];
Expand Down Expand Up @@ -73,13 +80,24 @@
{/each}
</ul>
<div class="flex items-center mt-auto">
<span class="mr-2">{user.name}</span>
<Avatar>
<AvatarImage src={user.avatar} alt={user.name} />
<AvatarFallback>
<Person />
</AvatarFallback>
</Avatar>
<DropdownMenu>
<DropdownMenuTrigger>
<Button variant="outline" class="flex items-center cursor-pointer">
<Avatar class="h-4 w-4">
<AvatarImage src={user.avatar} alt={user.name} />
<AvatarFallback>
<Person />
</AvatarFallback>
</Avatar>
<span class="ml-3">{user.name}</span>
<ChevronDown class="w-4 h-4 ml-3" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem onclick={() => alert('Settings clicked')}>Settings</DropdownMenuItem>
<DropdownMenuItem onclick={() => alert('Sign out clicked')}>Sign out</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
</nav>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<script lang="ts">
import { Collapsible as CollapsiblePrimitive } from 'bits-ui';
import { slide } from 'svelte/transition';
type $$Props = CollapsiblePrimitive.ContentProps;

export let transition: $$Props['transition'] = slide;
export let transitionConfig: $$Props['transitionConfig'] = {
duration: 150
};
</script>

<CollapsiblePrimitive.Content {transition} {transitionConfig} {...$$restProps}>
<slot />
</CollapsiblePrimitive.Content>
15 changes: 15 additions & 0 deletions frontend/src/lib/components/ui/collapsible/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Collapsible as CollapsiblePrimitive } from 'bits-ui';
import Content from './collapsible-content.svelte';

const Root = CollapsiblePrimitive.Root;
const Trigger = CollapsiblePrimitive.Trigger;

export {
Root,
Content,
Trigger,
//
Root as Collapsible,
Content as CollapsibleContent,
Trigger as CollapsibleTrigger
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
import Check from "svelte-radix/Check.svelte";
import { cn } from "$lib/utils.js";

type $$Props = DropdownMenuPrimitive.CheckboxItemProps;
type $$Events = DropdownMenuPrimitive.CheckboxItemEvents;

let className: $$Props["class"] = undefined;
export let checked: $$Props["checked"] = undefined;
export { className as class };
</script>

<DropdownMenuPrimitive.CheckboxItem
bind:checked
class={cn(
"data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className
)}
{...$$restProps}
on:click
on:keydown
on:focusin
on:focusout
on:pointerdown
on:pointerleave
on:pointermove
>
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<DropdownMenuPrimitive.CheckboxIndicator>
<Check class="h-4 w-4" />
</DropdownMenuPrimitive.CheckboxIndicator>
</span>
<slot />
</DropdownMenuPrimitive.CheckboxItem>
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
import { cn, flyAndScale } from "$lib/utils.js";

type $$Props = DropdownMenuPrimitive.ContentProps;

let className: $$Props["class"] = undefined;
export let sideOffset: $$Props["sideOffset"] = 4;
export let transition: $$Props["transition"] = flyAndScale;
export let transitionConfig: $$Props["transitionConfig"] = undefined;
export { className as class };
</script>

<DropdownMenuPrimitive.Content
{transition}
{transitionConfig}
{sideOffset}
class={cn(
"bg-popover text-popover-foreground z-50 min-w-[8rem] rounded-md border p-1 shadow-md focus:outline-none",
className
)}
{...$$restProps}
on:keydown
>
<slot />
</DropdownMenuPrimitive.Content>
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
import { cn } from "$lib/utils.js";

type $$Props = DropdownMenuPrimitive.ItemProps & {
inset?: boolean;
};
type $$Events = DropdownMenuPrimitive.ItemEvents;

let className: $$Props["class"] = undefined;
export let inset: $$Props["inset"] = undefined;
export { className as class };
</script>

<DropdownMenuPrimitive.Item
class={cn(
"data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
inset && "pl-8",
className
)}
on:click
on:keydown
on:focusin
on:focusout
on:pointerdown
on:pointerleave
on:pointermove
{...$$restProps}
>
<slot />
</DropdownMenuPrimitive.Item>
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
import { cn } from "$lib/utils.js";

type $$Props = DropdownMenuPrimitive.LabelProps & {
inset?: boolean;
};

let className: $$Props["class"] = undefined;
export let inset: $$Props["inset"] = undefined;
export { className as class };
</script>

<DropdownMenuPrimitive.Label
class={cn("px-2 py-1.5 text-sm font-semibold", inset && "pl-8", className)}
{...$$restProps}
>
<slot />
</DropdownMenuPrimitive.Label>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";

type $$Props = DropdownMenuPrimitive.RadioGroupProps;

export let value: $$Props["value"] = undefined;
</script>

<DropdownMenuPrimitive.RadioGroup {...$$restProps} bind:value>
<slot />
</DropdownMenuPrimitive.RadioGroup>
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
import DotFilled from "svelte-radix/DotFilled.svelte";
import { cn } from "$lib/utils.js";
type $$Props = DropdownMenuPrimitive.RadioItemProps;
type $$Events = DropdownMenuPrimitive.RadioItemEvents;
let className: $$Props["class"] = undefined;
export let value: DropdownMenuPrimitive.RadioItemProps["value"];
export { className as class };
</script>

<DropdownMenuPrimitive.RadioItem
class={cn(
"data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className
)}
{value}
{...$$restProps}
on:click
on:keydown
on:focusin
on:focusout
on:pointerdown
on:pointerleave
on:pointermove
>
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<DropdownMenuPrimitive.RadioIndicator>
<DotFilled class="h-4 w-4 fill-current" />
</DropdownMenuPrimitive.RadioIndicator>
</span>
<slot />
</DropdownMenuPrimitive.RadioItem>
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
import { cn } from "$lib/utils.js";

type $$Props = DropdownMenuPrimitive.SeparatorProps;

let className: $$Props["class"] = undefined;
export { className as class };
</script>

<DropdownMenuPrimitive.Separator
class={cn("bg-muted -mx-1 my-1 h-px", className)}
{...$$restProps}
/>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<script lang="ts">
import type { HTMLAttributes } from "svelte/elements";
import { cn } from "$lib/utils.js";

type $$Props = HTMLAttributes<HTMLSpanElement>;

let className: $$Props["class"] = undefined;
export { className as class };
</script>

<span class={cn("ml-auto text-xs tracking-widest opacity-60", className)} {...$$restProps}>
<slot />
</span>
Loading