@joewinke/jatui 0.1.22 → 0.1.24
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/CLAUDE.md +244 -0
- package/package.json +23 -10
- package/src/lib/actions/collapse.ts +0 -134
- package/src/lib/actions/railNav.ts +0 -473
- package/src/lib/components/AnnotationLayer.svelte +0 -108
- package/src/lib/components/AnnotationPanel.svelte +0 -319
- package/src/lib/components/AudioWaveform.svelte +0 -698
- package/src/lib/components/AvailabilityModal.svelte +0 -177
- package/src/lib/components/AvatarUpload.svelte +0 -234
- package/src/lib/components/Badge.svelte +0 -38
- package/src/lib/components/BookingForm.svelte +0 -278
- package/src/lib/components/BurndownChart.svelte +0 -778
- package/src/lib/components/Button.svelte +0 -81
- package/src/lib/components/CalendarPicker.svelte +0 -284
- package/src/lib/components/Card.svelte +0 -67
- package/src/lib/components/CharacterCounter.svelte +0 -82
- package/src/lib/components/ChipInput.svelte +0 -609
- package/src/lib/components/Collapse.svelte +0 -82
- package/src/lib/components/ColorSelector.svelte +0 -167
- package/src/lib/components/CommentThread.svelte +0 -773
- package/src/lib/components/ConfirmDialog.svelte +0 -348
- package/src/lib/components/ConfirmModal.svelte +0 -142
- package/src/lib/components/ContextMenu.svelte +0 -188
- package/src/lib/components/CountdownTimer.svelte +0 -94
- package/src/lib/components/DateRangePicker.svelte +0 -194
- package/src/lib/components/Drawer.svelte +0 -143
- package/src/lib/components/EmojiPicker.svelte +0 -159
- package/src/lib/components/EntityPreviewCard.svelte +0 -104
- package/src/lib/components/FileDropzone.svelte +0 -493
- package/src/lib/components/FilePicker.svelte +0 -488
- package/src/lib/components/FileThumbnail.svelte +0 -80
- package/src/lib/components/FilterDropdown.svelte +0 -203
- package/src/lib/components/FloatingActionBar.svelte +0 -354
- package/src/lib/components/GPSTracker.svelte +0 -202
- package/src/lib/components/HunkDiffView.svelte +0 -348
- package/src/lib/components/ImageLightbox.svelte +0 -274
- package/src/lib/components/ImageUpload.svelte +0 -147
- package/src/lib/components/InlineEdit.svelte +0 -291
- package/src/lib/components/InputDialog.svelte +0 -327
- package/src/lib/components/KeyboardShortcutsOverlay.svelte +0 -296
- package/src/lib/components/LazyImage.svelte +0 -123
- package/src/lib/components/LinkShortener.svelte +0 -274
- package/src/lib/components/LoadingSpinner.svelte +0 -106
- package/src/lib/components/LocationMap.svelte +0 -186
- package/src/lib/components/MapView.svelte +0 -341
- package/src/lib/components/MarkupEditor.svelte +0 -485
- package/src/lib/components/MarkupOverlay.svelte +0 -55
- package/src/lib/components/MediaWorkbench.svelte +0 -871
- package/src/lib/components/MilestoneCard.svelte +0 -98
- package/src/lib/components/MilestoneTimeline.svelte +0 -91
- package/src/lib/components/Modal.svelte +0 -244
- package/src/lib/components/PDFViewer.svelte +0 -105
- package/src/lib/components/PdfThumbnail.svelte +0 -63
- package/src/lib/components/PhoneInput.svelte +0 -212
- package/src/lib/components/ResizableDivider.svelte +0 -307
- package/src/lib/components/ResizablePanel.svelte +0 -305
- package/src/lib/components/SearchDropdown.svelte +0 -421
- package/src/lib/components/SelectInput.svelte +0 -237
- package/src/lib/components/SidebarUserFooter.svelte +0 -34
- package/src/lib/components/SignaturePad.svelte +0 -175
- package/src/lib/components/SmartImageEditor.svelte +0 -720
- package/src/lib/components/SortDropdown.svelte +0 -156
- package/src/lib/components/Sparkline.svelte +0 -116
- package/src/lib/components/SpeechForm.svelte +0 -114
- package/src/lib/components/StatusBadge.svelte +0 -157
- package/src/lib/components/TaskTypeIcon.svelte +0 -54
- package/src/lib/components/TextArea.svelte +0 -162
- package/src/lib/components/TextInput.svelte +0 -131
- package/src/lib/components/ThemeSelector.svelte +0 -206
- package/src/lib/components/TimeSlotPicker.svelte +0 -162
- package/src/lib/components/UserAvatar.svelte +0 -72
- package/src/lib/components/VariablePicker.svelte +0 -170
- package/src/lib/components/VoicePlayer.svelte +0 -422
- package/src/lib/components/linked-columns/LinkedColumns.svelte +0 -526
- package/src/lib/components/markup.ts +0 -287
- package/src/lib/components/messaging/Avatar.svelte +0 -81
- package/src/lib/components/messaging/ChannelInfoModal.svelte +0 -163
- package/src/lib/components/messaging/ChannelList.svelte +0 -107
- package/src/lib/components/messaging/ChannelMemberAvatarStack.svelte +0 -69
- package/src/lib/components/messaging/ChannelMembersModal.svelte +0 -182
- package/src/lib/components/messaging/CreateChannelModal.svelte +0 -190
- package/src/lib/components/messaging/DirectMessageList.svelte +0 -145
- package/src/lib/components/messaging/EmojiSelector.svelte +0 -261
- package/src/lib/components/messaging/MentionAutocomplete.svelte +0 -193
- package/src/lib/components/messaging/MessageAttachment.svelte +0 -273
- package/src/lib/components/messaging/MessageAttachmentUpload.svelte +0 -243
- package/src/lib/components/messaging/MessageInput.svelte +0 -452
- package/src/lib/components/messaging/MessageItem.svelte +0 -341
- package/src/lib/components/messaging/MessageThread.svelte +0 -306
- package/src/lib/components/messaging/NotificationSettingsModal.svelte +0 -234
- package/src/lib/components/messaging/QuotedMessageDisplay.svelte +0 -123
- package/src/lib/components/messaging/StartDMModal.svelte +0 -100
- package/src/lib/components/messaging/ThreadPanel.svelte +0 -153
- package/src/lib/components/pipeline/DESIGN.md +0 -361
- package/src/lib/components/pipeline/Pipeline.svelte +0 -391
- package/src/lib/components/pipeline/PipelineCard.svelte +0 -94
- package/src/lib/components/pipeline/PipelineColumn.svelte +0 -163
- package/src/lib/components/replay/ChapterTimeline.svelte +0 -326
- package/src/lib/components/session-nav/transcriptModel.ts +0 -352
- package/src/lib/data/emojis.ts +0 -1208
- package/src/lib/index.ts +0 -405
- package/src/lib/stores/confirmDialog.svelte.ts +0 -48
- package/src/lib/stores/inputDialog.svelte.ts +0 -51
- package/src/lib/styles/rail.css +0 -63
- package/src/lib/types/annotation.ts +0 -38
- package/src/lib/types/booking.ts +0 -143
- package/src/lib/types/comments.ts +0 -97
- package/src/lib/types/entityPreview.ts +0 -45
- package/src/lib/types/filePicker.ts +0 -16
- package/src/lib/types/googleMaps.d.ts +0 -51
- package/src/lib/types/maps.ts +0 -43
- package/src/lib/types/messaging.ts +0 -459
- package/src/lib/types/milestone.ts +0 -22
- package/src/lib/types/pipeline.ts +0 -150
- package/src/lib/types/smartImageEditor.ts +0 -39
- package/src/lib/types/templateVars.ts +0 -36
- package/src/lib/utils/currency.ts +0 -20
- package/src/lib/utils/daisyuiColors.ts +0 -243
- package/src/lib/utils/dateFormatters.ts +0 -196
- package/src/lib/utils/googleMapsLoader.ts +0 -84
- package/src/lib/utils/mentionParser.ts +0 -188
- package/src/lib/utils/phone.ts +0 -80
- package/src/lib/utils/phoneFormat.ts +0 -74
- package/src/lib/utils/taskUtils.ts +0 -75
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
/**
|
|
3
|
-
* SortDropdown Component - Reusable sort dropdown with optional filter input
|
|
4
|
-
*
|
|
5
|
-
* A standardized dropdown for sorting lists with:
|
|
6
|
-
* - Sort button showing current option icon, label, and direction indicator
|
|
7
|
-
* - Dropdown menu with configurable sort options
|
|
8
|
-
* - Optional filter input for search/filtering
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
type SortDirection = 'asc' | 'desc';
|
|
12
|
-
|
|
13
|
-
export interface SortOption {
|
|
14
|
-
value: string;
|
|
15
|
-
label: string;
|
|
16
|
-
icon: string;
|
|
17
|
-
defaultDir?: SortDirection;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
interface Props {
|
|
21
|
-
/** Sort options array (required) */
|
|
22
|
-
options: SortOption[];
|
|
23
|
-
/** Current sort value */
|
|
24
|
-
sortBy: string;
|
|
25
|
-
/** Current sort direction */
|
|
26
|
-
sortDir: SortDirection;
|
|
27
|
-
/** Current filter input value */
|
|
28
|
-
filterValue?: string;
|
|
29
|
-
/** Whether to show the filter input */
|
|
30
|
-
showFilter?: boolean;
|
|
31
|
-
/** Placeholder text for filter input */
|
|
32
|
-
filterPlaceholder?: string;
|
|
33
|
-
/** Button size variant */
|
|
34
|
-
size?: 'xs' | 'sm' | 'md';
|
|
35
|
-
/** Show label on small screens */
|
|
36
|
-
showLabelOnMobile?: boolean;
|
|
37
|
-
/** Callback when sort changes */
|
|
38
|
-
onSortChange?: (value: string, dir: SortDirection) => void;
|
|
39
|
-
/** Callback when filter changes */
|
|
40
|
-
onFilterChange?: (value: string) => void;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
let {
|
|
44
|
-
options,
|
|
45
|
-
sortBy,
|
|
46
|
-
sortDir,
|
|
47
|
-
filterValue = '',
|
|
48
|
-
showFilter = false,
|
|
49
|
-
filterPlaceholder = 'Filter...',
|
|
50
|
-
size = 'xs',
|
|
51
|
-
showLabelOnMobile = false,
|
|
52
|
-
onSortChange,
|
|
53
|
-
onFilterChange
|
|
54
|
-
}: Props = $props();
|
|
55
|
-
|
|
56
|
-
// Find current sort option
|
|
57
|
-
const currentOption = $derived(options.find(o => o.value === sortBy));
|
|
58
|
-
const currentIcon = $derived(currentOption?.icon || '');
|
|
59
|
-
const currentLabel = $derived(currentOption?.label || 'Sort');
|
|
60
|
-
|
|
61
|
-
// Handle sort option click
|
|
62
|
-
function handleSortClick(value: string) {
|
|
63
|
-
if (sortBy === value) {
|
|
64
|
-
onSortChange?.(value, sortDir === 'asc' ? 'desc' : 'asc');
|
|
65
|
-
} else {
|
|
66
|
-
const opt = options.find(o => o.value === value);
|
|
67
|
-
onSortChange?.(value, opt?.defaultDir ?? 'asc');
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Handle filter input change
|
|
72
|
-
function handleFilterInput(e: Event) {
|
|
73
|
-
const target = e.currentTarget as HTMLInputElement;
|
|
74
|
-
onFilterChange?.(target.value);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Size classes
|
|
78
|
-
const buttonSizeClass = $derived({
|
|
79
|
-
xs: 'btn-xs',
|
|
80
|
-
sm: 'btn-sm',
|
|
81
|
-
md: ''
|
|
82
|
-
}[size]);
|
|
83
|
-
|
|
84
|
-
const menuSizeClass = $derived({
|
|
85
|
-
xs: 'menu-xs',
|
|
86
|
-
sm: 'menu-sm',
|
|
87
|
-
md: ''
|
|
88
|
-
}[size]);
|
|
89
|
-
|
|
90
|
-
const inputSizeClass = $derived({
|
|
91
|
-
xs: 'input-xs',
|
|
92
|
-
sm: 'input-sm',
|
|
93
|
-
md: ''
|
|
94
|
-
}[size]);
|
|
95
|
-
|
|
96
|
-
const inputWidthClass = $derived({
|
|
97
|
-
xs: 'w-20 focus:w-32',
|
|
98
|
-
sm: 'w-24 focus:w-36',
|
|
99
|
-
md: 'w-32 focus:w-44'
|
|
100
|
-
}[size]);
|
|
101
|
-
|
|
102
|
-
import { fly } from 'svelte/transition';
|
|
103
|
-
import { cubicOut } from 'svelte/easing';
|
|
104
|
-
</script>
|
|
105
|
-
|
|
106
|
-
<div class="flex items-center gap-1">
|
|
107
|
-
<!-- Sort dropdown -->
|
|
108
|
-
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
109
|
-
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
110
|
-
<div class="dropdown dropdown-end flex-shrink-0" onclick={(e) => e.stopPropagation()}>
|
|
111
|
-
<button
|
|
112
|
-
tabindex="0"
|
|
113
|
-
class="btn {buttonSizeClass} btn-ghost gap-1 text-[0.75rem] opacity-70 hover:opacity-100"
|
|
114
|
-
title="Sort"
|
|
115
|
-
>
|
|
116
|
-
<span>{currentIcon}</span>
|
|
117
|
-
<span class="{showLabelOnMobile ? '' : 'hidden sm:inline'}">{currentLabel}</span>
|
|
118
|
-
<span class="text-[9px]">{sortDir === 'asc' ? '▲' : '▼'}</span>
|
|
119
|
-
</button>
|
|
120
|
-
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
|
|
121
|
-
<ul tabindex="0" class="dropdown-content menu {menuSizeClass} bg-base-200 rounded-box z-40 w-36 p-1 border border-base-300">
|
|
122
|
-
{#each options as opt (opt.value)}
|
|
123
|
-
<li>
|
|
124
|
-
<button
|
|
125
|
-
class="flex items-center gap-2 {sortBy === opt.value ? 'active' : ''}"
|
|
126
|
-
onclick={() => handleSortClick(opt.value)}
|
|
127
|
-
>
|
|
128
|
-
<span>{opt.icon}</span>
|
|
129
|
-
<span class="flex-1">{opt.label}</span>
|
|
130
|
-
{#if sortBy === opt.value}
|
|
131
|
-
<span class="text-[9px] opacity-70">{sortDir === 'asc' ? '▲' : '▼'}</span>
|
|
132
|
-
{/if}
|
|
133
|
-
</button>
|
|
134
|
-
</li>
|
|
135
|
-
{/each}
|
|
136
|
-
</ul>
|
|
137
|
-
</div>
|
|
138
|
-
|
|
139
|
-
<!-- Filter input (optional) -->
|
|
140
|
-
{#if showFilter}
|
|
141
|
-
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
142
|
-
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
143
|
-
<div class="flex-shrink-0" onclick={(e) => e.stopPropagation()}
|
|
144
|
-
in:fly={{ x: -8, duration: 180, easing: cubicOut }}
|
|
145
|
-
out:fly={{ x: -4, duration: 120, easing: cubicOut }}
|
|
146
|
-
>
|
|
147
|
-
<input
|
|
148
|
-
type="text"
|
|
149
|
-
placeholder={filterPlaceholder}
|
|
150
|
-
value={filterValue}
|
|
151
|
-
oninput={handleFilterInput}
|
|
152
|
-
class="input {inputSizeClass} input-bordered {inputWidthClass} transition-all duration-200 bg-base-200/50"
|
|
153
|
-
/>
|
|
154
|
-
</div>
|
|
155
|
-
{/if}
|
|
156
|
-
</div>
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
/**
|
|
3
|
-
* Sparkline — Lightweight D3 bar chart for trend visualization.
|
|
4
|
-
* Animated entrance with staggered bars, configurable color and opacity gradient.
|
|
5
|
-
*
|
|
6
|
-
* Requires `d3` as a peer dependency.
|
|
7
|
-
*/
|
|
8
|
-
import * as d3 from 'd3';
|
|
9
|
-
|
|
10
|
-
let {
|
|
11
|
-
values,
|
|
12
|
-
width = 120,
|
|
13
|
-
height = 32,
|
|
14
|
-
color = 'oklch(var(--p))',
|
|
15
|
-
fillOpacity = 0.15,
|
|
16
|
-
labels,
|
|
17
|
-
}: {
|
|
18
|
-
values: number[];
|
|
19
|
-
width?: number | 'auto';
|
|
20
|
-
height?: number;
|
|
21
|
-
color?: string;
|
|
22
|
-
fillOpacity?: number;
|
|
23
|
-
/** Optional tooltip label per bar. If provided, shown as SVG title on hover. */
|
|
24
|
-
labels?: string[];
|
|
25
|
-
} = $props();
|
|
26
|
-
|
|
27
|
-
let el: HTMLDivElement | undefined = $state();
|
|
28
|
-
let animated = false;
|
|
29
|
-
|
|
30
|
-
function getEffectiveWidth(): number {
|
|
31
|
-
if (width === 'auto' && el) return el.clientWidth;
|
|
32
|
-
if (typeof width === 'number') return width;
|
|
33
|
-
return 120;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
$effect(() => {
|
|
37
|
-
if (!el || values.length < 2) return;
|
|
38
|
-
|
|
39
|
-
const w = getEffectiveWidth();
|
|
40
|
-
if (w <= 0) return;
|
|
41
|
-
|
|
42
|
-
d3.select(el).select('svg').remove();
|
|
43
|
-
|
|
44
|
-
const pad = 1;
|
|
45
|
-
const innerW = w - pad * 2;
|
|
46
|
-
const innerH = height - pad * 2;
|
|
47
|
-
|
|
48
|
-
if (innerW <= 0 || innerH <= 0) return;
|
|
49
|
-
|
|
50
|
-
const svg = d3
|
|
51
|
-
.select(el)
|
|
52
|
-
.append('svg')
|
|
53
|
-
.attr('width', w)
|
|
54
|
-
.attr('height', height);
|
|
55
|
-
|
|
56
|
-
const g = svg.append('g').attr('transform', `translate(${pad},${pad})`);
|
|
57
|
-
|
|
58
|
-
const x = d3
|
|
59
|
-
.scaleBand<number>()
|
|
60
|
-
.domain(values.map((_, i) => i))
|
|
61
|
-
.range([0, innerW])
|
|
62
|
-
.padding(0.15);
|
|
63
|
-
|
|
64
|
-
const yMin = d3.min(values)! * 0.95;
|
|
65
|
-
const yMax = d3.max(values)! * 1.05;
|
|
66
|
-
const y = d3.scaleLinear().domain([yMin, yMax]).range([innerH, 0]);
|
|
67
|
-
|
|
68
|
-
const n = Math.max(1, values.length - 1);
|
|
69
|
-
const shouldAnimate = !animated;
|
|
70
|
-
animated = true;
|
|
71
|
-
|
|
72
|
-
const bars = g
|
|
73
|
-
.selectAll('.bar')
|
|
74
|
-
.data(values)
|
|
75
|
-
.join('rect')
|
|
76
|
-
.attr('x', (_, i) => x(i)!)
|
|
77
|
-
.attr('width', x.bandwidth())
|
|
78
|
-
.attr('rx', Math.min(1, x.bandwidth() / 4))
|
|
79
|
-
.style('fill', color)
|
|
80
|
-
.style('fill-opacity', (_, i) => fillOpacity + (1 - fillOpacity) * (i / n));
|
|
81
|
-
|
|
82
|
-
bars.each(function (d, i) {
|
|
83
|
-
d3.select(this).selectAll('title').remove();
|
|
84
|
-
const label = labels && labels[i] !== undefined ? labels[i] : String(d);
|
|
85
|
-
d3.select(this).append('title').text(label);
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
if (shouldAnimate) {
|
|
89
|
-
bars
|
|
90
|
-
.attr('y', innerH)
|
|
91
|
-
.attr('height', 0)
|
|
92
|
-
.transition()
|
|
93
|
-
.duration(400)
|
|
94
|
-
.delay((_, i) => i * 15)
|
|
95
|
-
.ease(d3.easeCubicOut)
|
|
96
|
-
.attr('y', (d) => y(d))
|
|
97
|
-
.attr('height', (d) => innerH - y(d));
|
|
98
|
-
} else {
|
|
99
|
-
bars.attr('y', (d) => y(d)).attr('height', (d) => innerH - y(d));
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
</script>
|
|
103
|
-
|
|
104
|
-
{#if values.length >= 2}
|
|
105
|
-
<div
|
|
106
|
-
bind:this={el}
|
|
107
|
-
style="{width === 'auto' ? 'width: 100%' : `width: ${width}px`}; height: {height}px"
|
|
108
|
-
></div>
|
|
109
|
-
{:else}
|
|
110
|
-
<div
|
|
111
|
-
class="flex items-center justify-center text-base-content/30 text-xs"
|
|
112
|
-
style="{width === 'auto' ? 'width: 100%' : `width: ${width}px`}; height: {height}px"
|
|
113
|
-
>
|
|
114
|
-
--
|
|
115
|
-
</div>
|
|
116
|
-
{/if}
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
/**
|
|
3
|
-
* SpeechForm — Text-to-speech generation form with voice selection, character limit, and audio preview.
|
|
4
|
-
*
|
|
5
|
-
* Voice options are configurable via the `voiceOptions` prop. Includes a textarea with character counter,
|
|
6
|
-
* voice selector dropdown, optional audio preview player, and submit/cancel buttons.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
let {
|
|
10
|
-
text = $bindable(""),
|
|
11
|
-
voice = $bindable("en-US-female-27"),
|
|
12
|
-
previewUrl = null,
|
|
13
|
-
isCreating = false,
|
|
14
|
-
maxLength = 1000,
|
|
15
|
-
voiceOptions = [
|
|
16
|
-
{ id: "en-US-female-27", label: "US Female (Clear)" },
|
|
17
|
-
{ id: "en-US-male-27", label: "US Male (Clear)" },
|
|
18
|
-
{ id: "en-GB-female-11", label: "UK Female" },
|
|
19
|
-
{ id: "en-GB-male-10", label: "UK Male" },
|
|
20
|
-
{ id: "en-US-female-24", label: "US Female (Warm)" },
|
|
21
|
-
{ id: "en-US-male-24", label: "US Male (Deep)" },
|
|
22
|
-
],
|
|
23
|
-
infoText = "The system will convert your text to natural-sounding speech which you can download and use.",
|
|
24
|
-
onSubmit,
|
|
25
|
-
onCancel,
|
|
26
|
-
} = $props<{
|
|
27
|
-
text?: string
|
|
28
|
-
voice?: string
|
|
29
|
-
previewUrl?: string | null
|
|
30
|
-
isCreating?: boolean
|
|
31
|
-
maxLength?: number
|
|
32
|
-
voiceOptions?: { id: string; label: string }[]
|
|
33
|
-
infoText?: string
|
|
34
|
-
onSubmit: () => void
|
|
35
|
-
onCancel: () => void
|
|
36
|
-
}>()
|
|
37
|
-
|
|
38
|
-
let remaining = $derived(maxLength - (text?.length || 0))
|
|
39
|
-
|
|
40
|
-
$effect(() => {
|
|
41
|
-
return () => {
|
|
42
|
-
if (previewUrl) {
|
|
43
|
-
URL.revokeObjectURL(previewUrl)
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
})
|
|
47
|
-
</script>
|
|
48
|
-
|
|
49
|
-
<div class="space-y-4">
|
|
50
|
-
<div class="form-control">
|
|
51
|
-
<label class="label" for="speech-text-input">
|
|
52
|
-
<span class="label-text font-medium">Text to Convert</span>
|
|
53
|
-
<span class="label-text-alt">{remaining} characters left</span>
|
|
54
|
-
</label>
|
|
55
|
-
<textarea
|
|
56
|
-
id="speech-text-input"
|
|
57
|
-
bind:value={text}
|
|
58
|
-
placeholder="Enter the text you want to convert to speech..."
|
|
59
|
-
class="textarea min-h-32"
|
|
60
|
-
maxlength={maxLength}
|
|
61
|
-
disabled={isCreating}
|
|
62
|
-
></textarea>
|
|
63
|
-
</div>
|
|
64
|
-
|
|
65
|
-
<div class="form-control">
|
|
66
|
-
<label class="label" for="speech-voice-select">
|
|
67
|
-
<span class="label-text font-medium">Voice</span>
|
|
68
|
-
</label>
|
|
69
|
-
<select id="speech-voice-select" bind:value={voice} class="select" disabled={isCreating}>
|
|
70
|
-
{#each voiceOptions as option}
|
|
71
|
-
<option value={option.id}>{option.label}</option>
|
|
72
|
-
{/each}
|
|
73
|
-
</select>
|
|
74
|
-
</div>
|
|
75
|
-
|
|
76
|
-
{#if previewUrl}
|
|
77
|
-
<div class="form-control">
|
|
78
|
-
<div class="label">
|
|
79
|
-
<span class="label-text font-medium">Preview</span>
|
|
80
|
-
</div>
|
|
81
|
-
<audio src={previewUrl} controls class="w-full" aria-label="Generated speech preview">
|
|
82
|
-
Your browser does not support the audio element.
|
|
83
|
-
</audio>
|
|
84
|
-
</div>
|
|
85
|
-
{/if}
|
|
86
|
-
|
|
87
|
-
{#if infoText}
|
|
88
|
-
<div class="flex items-center gap-2 text-info bg-info/10 p-3 rounded">
|
|
89
|
-
<!-- Info icon -->
|
|
90
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/></svg>
|
|
91
|
-
<p class="text-sm">{infoText}</p>
|
|
92
|
-
</div>
|
|
93
|
-
{/if}
|
|
94
|
-
</div>
|
|
95
|
-
|
|
96
|
-
<div class="flex justify-end gap-2 mt-6">
|
|
97
|
-
<button class="btn btn-ghost" onclick={onCancel} disabled={isCreating}>
|
|
98
|
-
Cancel
|
|
99
|
-
</button>
|
|
100
|
-
<button
|
|
101
|
-
class="btn btn-primary gap-2"
|
|
102
|
-
onclick={onSubmit}
|
|
103
|
-
disabled={isCreating || !text}
|
|
104
|
-
>
|
|
105
|
-
{#if isCreating}
|
|
106
|
-
<span class="loading loading-spinner loading-sm"></span>
|
|
107
|
-
Converting...
|
|
108
|
-
{:else}
|
|
109
|
-
<!-- Mic icon -->
|
|
110
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><line x1="12" x2="12" y1="19" y2="22"/></svg>
|
|
111
|
-
Generate Speech
|
|
112
|
-
{/if}
|
|
113
|
-
</button>
|
|
114
|
-
</div>
|
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
<script module lang="ts">
|
|
2
|
-
export type StatusValue =
|
|
3
|
-
| 'active'
|
|
4
|
-
| 'inactive'
|
|
5
|
-
| 'pending'
|
|
6
|
-
| 'completed'
|
|
7
|
-
| 'cancelled'
|
|
8
|
-
| 'draft'
|
|
9
|
-
| 'scheduled'
|
|
10
|
-
| 'in-progress'
|
|
11
|
-
| 'overdue'
|
|
12
|
-
| 'paid'
|
|
13
|
-
| 'unpaid'
|
|
14
|
-
| 'partial'
|
|
15
|
-
| 'assigned'
|
|
16
|
-
| 'available';
|
|
17
|
-
</script>
|
|
18
|
-
|
|
19
|
-
<script lang="ts">
|
|
20
|
-
/**
|
|
21
|
-
* StatusBadge Component
|
|
22
|
-
*
|
|
23
|
-
* Predefined status badge with semantic colors, icons, and dot indicators.
|
|
24
|
-
*/
|
|
25
|
-
|
|
26
|
-
interface Props {
|
|
27
|
-
status: StatusValue;
|
|
28
|
-
size?: 'xs' | 'sm' | 'md' | 'lg';
|
|
29
|
-
showIcon?: boolean;
|
|
30
|
-
showDot?: boolean;
|
|
31
|
-
customText?: string;
|
|
32
|
-
clickable?: boolean;
|
|
33
|
-
outlined?: boolean;
|
|
34
|
-
class?: string;
|
|
35
|
-
ariaLabel?: string;
|
|
36
|
-
onclick?: (event: { event: MouseEvent; status: string }) => void;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
let {
|
|
40
|
-
status,
|
|
41
|
-
size = 'md',
|
|
42
|
-
showIcon = false,
|
|
43
|
-
showDot = false,
|
|
44
|
-
customText = '',
|
|
45
|
-
clickable = false,
|
|
46
|
-
outlined = false,
|
|
47
|
-
class: className = '',
|
|
48
|
-
ariaLabel = '',
|
|
49
|
-
onclick
|
|
50
|
-
}: Props = $props();
|
|
51
|
-
|
|
52
|
-
const statusConfig: Record<string, { text: string; variant: string; icon: string }> = {
|
|
53
|
-
active: { text: 'Active', variant: 'success', icon: '✓' },
|
|
54
|
-
inactive: { text: 'Inactive', variant: 'error', icon: '✗' },
|
|
55
|
-
pending: { text: 'Pending', variant: 'warning', icon: '' },
|
|
56
|
-
completed: { text: 'Completed', variant: 'success', icon: '✓' },
|
|
57
|
-
cancelled: { text: 'Cancelled', variant: 'error', icon: '✗' },
|
|
58
|
-
draft: { text: 'Draft', variant: 'default', icon: '' },
|
|
59
|
-
scheduled: { text: 'Scheduled', variant: 'info', icon: '' },
|
|
60
|
-
'in-progress': { text: 'In Progress', variant: 'primary', icon: '' },
|
|
61
|
-
overdue: { text: 'Overdue', variant: 'error', icon: '' },
|
|
62
|
-
paid: { text: 'Paid', variant: 'success', icon: '✓' },
|
|
63
|
-
unpaid: { text: 'Unpaid', variant: 'warning', icon: '' },
|
|
64
|
-
partial: { text: 'Partial', variant: 'warning', icon: '' },
|
|
65
|
-
assigned: { text: 'Assigned', variant: 'success', icon: '✓' },
|
|
66
|
-
available: { text: 'Available', variant: 'info', icon: '' }
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
const config = $derived(statusConfig[status] || { text: status || 'Unknown', variant: 'default', icon: '?' });
|
|
70
|
-
const displayText = $derived(customText || config.text);
|
|
71
|
-
|
|
72
|
-
const variantMap: Record<string, string> = {
|
|
73
|
-
default: 'badge-ghost',
|
|
74
|
-
primary: 'badge-primary',
|
|
75
|
-
secondary: 'badge-secondary',
|
|
76
|
-
accent: 'badge-accent',
|
|
77
|
-
success: 'badge-success',
|
|
78
|
-
warning: 'badge-warning',
|
|
79
|
-
error: 'badge-error',
|
|
80
|
-
info: 'badge-info'
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
const outlinedVariantMap: Record<string, string> = {
|
|
84
|
-
default: 'badge-outline',
|
|
85
|
-
primary: 'badge-primary badge-outline',
|
|
86
|
-
secondary: 'badge-secondary badge-outline',
|
|
87
|
-
accent: 'badge-accent badge-outline',
|
|
88
|
-
success: 'badge-success badge-outline',
|
|
89
|
-
warning: 'badge-warning badge-outline',
|
|
90
|
-
error: 'badge-error badge-outline',
|
|
91
|
-
info: 'badge-info badge-outline'
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
const variantClass = $derived(outlined ? outlinedVariantMap[config.variant] : variantMap[config.variant]);
|
|
95
|
-
const sizeClass = $derived({ xs: 'badge-xs', sm: 'badge-sm', md: 'badge-md', lg: 'badge-lg' }[size]);
|
|
96
|
-
|
|
97
|
-
const dotColor = $derived(
|
|
98
|
-
{
|
|
99
|
-
default: 'bg-base-content',
|
|
100
|
-
primary: 'bg-primary',
|
|
101
|
-
secondary: 'bg-secondary',
|
|
102
|
-
accent: 'bg-accent',
|
|
103
|
-
success: 'bg-success',
|
|
104
|
-
warning: 'bg-warning',
|
|
105
|
-
error: 'bg-error',
|
|
106
|
-
info: 'bg-info'
|
|
107
|
-
}[config.variant]
|
|
108
|
-
);
|
|
109
|
-
|
|
110
|
-
const badgeClass = $derived(
|
|
111
|
-
['badge', variantClass, sizeClass, clickable ? 'cursor-pointer hover:opacity-80' : '', className]
|
|
112
|
-
.filter(Boolean)
|
|
113
|
-
.join(' ')
|
|
114
|
-
);
|
|
115
|
-
|
|
116
|
-
function handleClick(event: MouseEvent) {
|
|
117
|
-
if (clickable && onclick) {
|
|
118
|
-
onclick({ event, status });
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
function handleKeyDown(event: KeyboardEvent) {
|
|
123
|
-
if (clickable && onclick && (event.key === 'Enter' || event.key === ' ')) {
|
|
124
|
-
event.preventDefault();
|
|
125
|
-
onclick({ event: event as any, status });
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
</script>
|
|
129
|
-
|
|
130
|
-
{#if clickable}
|
|
131
|
-
<span
|
|
132
|
-
class={badgeClass}
|
|
133
|
-
role="button"
|
|
134
|
-
tabindex="0"
|
|
135
|
-
aria-label={ariaLabel || `Status: ${displayText}`}
|
|
136
|
-
onclick={handleClick}
|
|
137
|
-
onkeydown={handleKeyDown}
|
|
138
|
-
>
|
|
139
|
-
{#if showDot}
|
|
140
|
-
<span class="w-2 h-2 rounded-full {dotColor} mr-1.5 {status === 'active' ? 'animate-pulse' : ''}"></span>
|
|
141
|
-
{/if}
|
|
142
|
-
{#if showIcon}
|
|
143
|
-
<span class="mr-1">{config.icon}</span>
|
|
144
|
-
{/if}
|
|
145
|
-
{displayText}
|
|
146
|
-
</span>
|
|
147
|
-
{:else}
|
|
148
|
-
<span class={badgeClass} aria-label={ariaLabel || `Status: ${displayText}`}>
|
|
149
|
-
{#if showDot}
|
|
150
|
-
<span class="w-2 h-2 rounded-full {dotColor} mr-1.5 {status === 'active' ? 'animate-pulse' : ''}"></span>
|
|
151
|
-
{/if}
|
|
152
|
-
{#if showIcon}
|
|
153
|
-
<span class="mr-1">{config.icon}</span>
|
|
154
|
-
{/if}
|
|
155
|
-
{displayText}
|
|
156
|
-
</span>
|
|
157
|
-
{/if}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
/**
|
|
3
|
-
* Stroke icon for a task issue_type value.
|
|
4
|
-
* Replaces the emoji returned by `typeIcon()`.
|
|
5
|
-
*
|
|
6
|
-
* Usage:
|
|
7
|
-
* <TaskTypeIcon type={task.issue_type} />
|
|
8
|
-
* <TaskTypeIcon type="bug" size={14} class="text-base-content/60" />
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
interface Props {
|
|
12
|
-
type: string | null | undefined
|
|
13
|
-
size?: number
|
|
14
|
-
class?: string
|
|
15
|
-
title?: string
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
let { type, size = 14, class: className = "", title }: Props = $props()
|
|
19
|
-
|
|
20
|
-
// Path data per type. Single-path lucide-style strokes so they inherit
|
|
21
|
-
// currentColor and size cleanly inside dense tables.
|
|
22
|
-
const paths: Record<string, string> = {
|
|
23
|
-
// Bug: body + antennae/legs
|
|
24
|
-
bug: "M12 4v2M8 6l1 2M16 6l-1 2M6 12H4M20 12h-2M6 16l-2 1M18 16l2 1M9 9h6v5a3 3 0 11-6 0V9z",
|
|
25
|
-
// Feature (spark): 4-point star
|
|
26
|
-
feature: "M12 3v4M12 17v4M3 12h4M17 12h4M5.5 5.5l2.5 2.5M16 16l2.5 2.5M5.5 18.5L8 16M16 8l2.5-2.5",
|
|
27
|
-
// Task: checklist
|
|
28
|
-
task: "M9 4h9a1 1 0 011 1v14a1 1 0 01-1 1H6a1 1 0 01-1-1V5a1 1 0 011-1h1M9 3h6v2H9zM8.5 11.5l1.5 1.5 3-3M8.5 16.5l1.5 1.5 3-3",
|
|
29
|
-
// Epic: mountain peaks
|
|
30
|
-
epic: "M3 19l5-8 4 6 3-4 6 6H3z",
|
|
31
|
-
// Default: document
|
|
32
|
-
document: "M13 3H6a1 1 0 00-1 1v16a1 1 0 001 1h12a1 1 0 001-1V9l-6-6zM13 3v6h6",
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const d = $derived(paths[type ?? ""] ?? paths.document)
|
|
36
|
-
const label = $derived(title ?? (type ? `${type[0].toUpperCase()}${type.slice(1)}` : "Document"))
|
|
37
|
-
</script>
|
|
38
|
-
|
|
39
|
-
<svg
|
|
40
|
-
viewBox="0 0 24 24"
|
|
41
|
-
fill="none"
|
|
42
|
-
stroke="currentColor"
|
|
43
|
-
stroke-width="2"
|
|
44
|
-
stroke-linecap="round"
|
|
45
|
-
stroke-linejoin="round"
|
|
46
|
-
width={size}
|
|
47
|
-
height={size}
|
|
48
|
-
class={className}
|
|
49
|
-
role="img"
|
|
50
|
-
aria-label={label}
|
|
51
|
-
>
|
|
52
|
-
<title>{label}</title>
|
|
53
|
-
<path d={d} />
|
|
54
|
-
</svg>
|