@joewinke/jatui 0.1.25 → 0.1.26
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/actions/collapse.d.ts +80 -0
- package/dist/actions/collapse.js +134 -0
- package/dist/actions/railNav.d.ts +167 -0
- package/dist/actions/railNav.js +351 -0
- package/dist/components/AnnotationLayer.svelte +108 -0
- package/dist/components/AnnotationLayer.svelte.d.ts +12 -0
- package/dist/components/AnnotationPanel.svelte +319 -0
- package/dist/components/AnnotationPanel.svelte.d.ts +13 -0
- package/dist/components/AudioWaveform.svelte +701 -0
- package/dist/components/AudioWaveform.svelte.d.ts +19 -0
- package/dist/components/AvailabilityModal.svelte +177 -0
- package/dist/components/AvailabilityModal.svelte.d.ts +14 -0
- package/dist/components/AvatarUpload.svelte +234 -0
- package/dist/components/AvatarUpload.svelte.d.ts +23 -0
- package/dist/components/Badge.svelte +38 -0
- package/dist/components/Badge.svelte.d.ts +14 -0
- package/dist/components/BookingForm.svelte +278 -0
- package/dist/components/BookingForm.svelte.d.ts +37 -0
- package/dist/components/BurndownChart.svelte +778 -0
- package/dist/components/BurndownChart.svelte.d.ts +17 -0
- package/dist/components/Button.svelte +81 -0
- package/dist/components/Button.svelte.d.ts +21 -0
- package/dist/components/CalendarPicker.svelte +284 -0
- package/dist/components/CalendarPicker.svelte.d.ts +22 -0
- package/dist/components/Card.svelte +67 -0
- package/dist/components/Card.svelte.d.ts +17 -0
- package/dist/components/CharacterCounter.svelte +82 -0
- package/dist/components/CharacterCounter.svelte.d.ts +10 -0
- package/dist/components/ChipInput.svelte +609 -0
- package/dist/components/ChipInput.svelte.d.ts +55 -0
- package/dist/components/Collapse.svelte +91 -0
- package/dist/components/Collapse.svelte.d.ts +18 -0
- package/dist/components/ColorSelector.svelte +167 -0
- package/dist/components/ColorSelector.svelte.d.ts +9 -0
- package/dist/components/CommentThread.svelte +773 -0
- package/dist/components/CommentThread.svelte.d.ts +32 -0
- package/dist/components/ConfirmDialog.svelte +348 -0
- package/dist/components/ConfirmDialog.svelte.d.ts +3 -0
- package/dist/components/ConfirmModal.svelte +142 -0
- package/dist/components/ConfirmModal.svelte.d.ts +15 -0
- package/dist/components/ContextMenu.svelte +188 -0
- package/dist/components/ContextMenu.svelte.d.ts +31 -0
- package/dist/components/CountdownTimer.svelte +94 -0
- package/dist/components/CountdownTimer.svelte.d.ts +10 -0
- package/dist/components/DateRangePicker.svelte +194 -0
- package/dist/components/DateRangePicker.svelte.d.ts +10 -0
- package/dist/components/Drawer.svelte +143 -0
- package/dist/components/Drawer.svelte.d.ts +33 -0
- package/dist/components/EmojiPicker.svelte +159 -0
- package/dist/components/EmojiPicker.svelte.d.ts +8 -0
- package/dist/components/EntityPreviewCard.svelte +104 -0
- package/dist/components/EntityPreviewCard.svelte.d.ts +9 -0
- package/dist/components/FileDropzone.svelte +493 -0
- package/dist/components/FileDropzone.svelte.d.ts +40 -0
- package/dist/components/FilePicker.svelte +488 -0
- package/dist/components/FilePicker.svelte.d.ts +20 -0
- package/dist/components/FileThumbnail.svelte +80 -0
- package/dist/components/FileThumbnail.svelte.d.ts +12 -0
- package/dist/components/FilterDropdown.svelte +203 -0
- package/dist/components/FilterDropdown.svelte.d.ts +48 -0
- package/dist/components/FloatingActionBar.svelte +354 -0
- package/dist/components/FloatingActionBar.svelte.d.ts +51 -0
- package/dist/components/GPSTracker.svelte +202 -0
- package/dist/components/GPSTracker.svelte.d.ts +34 -0
- package/dist/components/HunkDiffView.svelte +348 -0
- package/dist/components/HunkDiffView.svelte.d.ts +9 -0
- package/dist/components/ImageLightbox.svelte +274 -0
- package/dist/components/ImageLightbox.svelte.d.ts +25 -0
- package/dist/components/ImageUpload.svelte +147 -0
- package/dist/components/ImageUpload.svelte.d.ts +10 -0
- package/dist/components/InlineEdit.svelte +291 -0
- package/dist/components/InlineEdit.svelte.d.ts +45 -0
- package/dist/components/InputDialog.svelte +327 -0
- package/dist/components/InputDialog.svelte.d.ts +3 -0
- package/dist/components/KeyboardShortcutsOverlay.svelte +296 -0
- package/dist/components/KeyboardShortcutsOverlay.svelte.d.ts +42 -0
- package/dist/components/LazyImage.svelte +123 -0
- package/dist/components/LazyImage.svelte.d.ts +14 -0
- package/dist/components/LinkShortener.svelte +274 -0
- package/dist/components/LinkShortener.svelte.d.ts +27 -0
- package/dist/components/LoadingSpinner.svelte +106 -0
- package/dist/components/LoadingSpinner.svelte.d.ts +21 -0
- package/dist/components/LocationMap.svelte +186 -0
- package/dist/components/LocationMap.svelte.d.ts +11 -0
- package/dist/components/MapView.svelte +341 -0
- package/dist/components/MapView.svelte.d.ts +20 -0
- package/dist/components/MarkupEditor.svelte +485 -0
- package/dist/components/MarkupEditor.svelte.d.ts +10 -0
- package/dist/components/MarkupOverlay.svelte +55 -0
- package/dist/components/MarkupOverlay.svelte.d.ts +8 -0
- package/dist/components/MediaWorkbench.svelte +871 -0
- package/dist/components/MediaWorkbench.svelte.d.ts +9 -0
- package/dist/components/MilestoneCard.svelte +98 -0
- package/dist/components/MilestoneCard.svelte.d.ts +16 -0
- package/dist/components/MilestoneTimeline.svelte +91 -0
- package/dist/components/MilestoneTimeline.svelte.d.ts +15 -0
- package/dist/components/Modal.svelte +244 -0
- package/dist/components/Modal.svelte.d.ts +35 -0
- package/dist/components/PDFViewer.svelte +105 -0
- package/dist/components/PDFViewer.svelte.d.ts +9 -0
- package/dist/components/PdfThumbnail.svelte +63 -0
- package/dist/components/PdfThumbnail.svelte.d.ts +9 -0
- package/dist/components/PhoneInput.svelte +212 -0
- package/dist/components/PhoneInput.svelte.d.ts +25 -0
- package/dist/components/ResizableDivider.svelte +307 -0
- package/dist/components/ResizableDivider.svelte.d.ts +26 -0
- package/dist/components/ResizablePanel.svelte +305 -0
- package/dist/components/ResizablePanel.svelte.d.ts +18 -0
- package/dist/components/SearchDropdown.svelte +421 -0
- package/dist/components/SearchDropdown.svelte.d.ts +24 -0
- package/dist/components/SelectInput.svelte +237 -0
- package/dist/components/SelectInput.svelte.d.ts +26 -0
- package/dist/components/SidebarUserFooter.svelte +34 -0
- package/dist/components/SidebarUserFooter.svelte.d.ts +16 -0
- package/dist/components/SignaturePad.svelte +175 -0
- package/dist/components/SignaturePad.svelte.d.ts +17 -0
- package/dist/components/SmartImageEditor.svelte +720 -0
- package/dist/components/SmartImageEditor.svelte.d.ts +24 -0
- package/dist/components/SortDropdown.svelte +156 -0
- package/dist/components/SortDropdown.svelte.d.ts +40 -0
- package/dist/components/Sparkline.svelte +116 -0
- package/dist/components/Sparkline.svelte.d.ts +12 -0
- package/dist/components/SpeechForm.svelte +114 -0
- package/dist/components/SpeechForm.svelte.d.ts +17 -0
- package/dist/components/StatusBadge.svelte +157 -0
- package/dist/components/StatusBadge.svelte.d.ts +24 -0
- package/dist/components/TaskTypeIcon.svelte +54 -0
- package/dist/components/TaskTypeIcon.svelte.d.ts +17 -0
- package/dist/components/TextArea.svelte +162 -0
- package/dist/components/TextArea.svelte.d.ts +30 -0
- package/dist/components/TextInput.svelte +131 -0
- package/dist/components/TextInput.svelte.d.ts +29 -0
- package/dist/components/ThemeSelector.svelte +206 -0
- package/dist/components/ThemeSelector.svelte.d.ts +14 -0
- package/dist/components/TimeSlotPicker.svelte +162 -0
- package/dist/components/TimeSlotPicker.svelte.d.ts +18 -0
- package/dist/components/UserAvatar.svelte +72 -0
- package/dist/components/UserAvatar.svelte.d.ts +11 -0
- package/dist/components/VariablePicker.svelte +170 -0
- package/dist/components/VariablePicker.svelte.d.ts +40 -0
- package/dist/components/VoicePlayer.svelte +422 -0
- package/dist/components/VoicePlayer.svelte.d.ts +18 -0
- package/dist/components/linked-columns/LinkedColumns.svelte +526 -0
- package/dist/components/linked-columns/LinkedColumns.svelte.d.ts +130 -0
- package/dist/components/markup.d.ts +47 -0
- package/dist/components/markup.js +201 -0
- package/dist/components/messaging/Avatar.svelte +81 -0
- package/dist/components/messaging/Avatar.svelte.d.ts +11 -0
- package/dist/components/messaging/ChannelInfoModal.svelte +163 -0
- package/dist/components/messaging/ChannelInfoModal.svelte.d.ts +18 -0
- package/dist/components/messaging/ChannelList.svelte +107 -0
- package/dist/components/messaging/ChannelList.svelte.d.ts +11 -0
- package/dist/components/messaging/ChannelMemberAvatarStack.svelte +69 -0
- package/dist/components/messaging/ChannelMemberAvatarStack.svelte.d.ts +11 -0
- package/dist/components/messaging/ChannelMembersModal.svelte +182 -0
- package/dist/components/messaging/ChannelMembersModal.svelte.d.ts +16 -0
- package/dist/components/messaging/CreateChannelModal.svelte +190 -0
- package/dist/components/messaging/CreateChannelModal.svelte.d.ts +14 -0
- package/dist/components/messaging/DirectMessageList.svelte +145 -0
- package/dist/components/messaging/DirectMessageList.svelte.d.ts +12 -0
- package/dist/components/messaging/EmojiSelector.svelte +261 -0
- package/dist/components/messaging/EmojiSelector.svelte.d.ts +19 -0
- package/dist/components/messaging/MentionAutocomplete.svelte +193 -0
- package/dist/components/messaging/MentionAutocomplete.svelte.d.ts +17 -0
- package/dist/components/messaging/MessageAttachment.svelte +273 -0
- package/dist/components/messaging/MessageAttachment.svelte.d.ts +13 -0
- package/dist/components/messaging/MessageAttachmentUpload.svelte +243 -0
- package/dist/components/messaging/MessageAttachmentUpload.svelte.d.ts +19 -0
- package/dist/components/messaging/MessageInput.svelte +452 -0
- package/dist/components/messaging/MessageInput.svelte.d.ts +28 -0
- package/dist/components/messaging/MessageItem.svelte +341 -0
- package/dist/components/messaging/MessageItem.svelte.d.ts +41 -0
- package/dist/components/messaging/MessageThread.svelte +306 -0
- package/dist/components/messaging/MessageThread.svelte.d.ts +30 -0
- package/dist/components/messaging/NotificationSettingsModal.svelte +234 -0
- package/dist/components/messaging/NotificationSettingsModal.svelte.d.ts +13 -0
- package/dist/components/messaging/QuotedMessageDisplay.svelte +123 -0
- package/dist/components/messaging/QuotedMessageDisplay.svelte.d.ts +10 -0
- package/dist/components/messaging/StartDMModal.svelte +100 -0
- package/dist/components/messaging/StartDMModal.svelte.d.ts +14 -0
- package/dist/components/messaging/ThreadPanel.svelte +153 -0
- package/dist/components/messaging/ThreadPanel.svelte.d.ts +16 -0
- package/dist/components/pipeline/DESIGN.md +361 -0
- package/dist/components/pipeline/Pipeline.svelte +391 -0
- package/dist/components/pipeline/Pipeline.svelte.d.ts +28 -0
- package/dist/components/pipeline/PipelineCard.svelte +94 -0
- package/dist/components/pipeline/PipelineCard.svelte.d.ts +13 -0
- package/dist/components/pipeline/PipelineColumn.svelte +163 -0
- package/dist/components/pipeline/PipelineColumn.svelte.d.ts +30 -0
- package/dist/components/replay/ChapterTimeline.svelte +326 -0
- package/dist/components/replay/ChapterTimeline.svelte.d.ts +39 -0
- package/dist/components/session-nav/transcriptModel.d.ts +132 -0
- package/dist/components/session-nav/transcriptModel.js +285 -0
- package/dist/data/emojis.d.ts +18 -0
- package/dist/data/emojis.js +1196 -0
- package/dist/index.d.ts +142 -0
- package/dist/index.js +142 -0
- package/dist/stores/confirmDialog.svelte.d.ts +21 -0
- package/dist/stores/confirmDialog.svelte.js +31 -0
- package/dist/stores/inputDialog.svelte.d.ts +24 -0
- package/dist/stores/inputDialog.svelte.js +32 -0
- package/dist/styles/rail.css +63 -0
- package/dist/types/annotation.d.ts +32 -0
- package/dist/types/annotation.js +3 -0
- package/dist/types/booking.d.ts +115 -0
- package/dist/types/booking.js +24 -0
- package/dist/types/comments.d.ts +65 -0
- package/dist/types/comments.js +22 -0
- package/dist/types/entityPreview.d.ts +42 -0
- package/dist/types/entityPreview.js +10 -0
- package/dist/types/filePicker.d.ts +15 -0
- package/dist/types/filePicker.js +1 -0
- package/dist/types/googleMaps.d.ts +51 -0
- package/dist/types/maps.d.ts +43 -0
- package/dist/types/maps.js +4 -0
- package/dist/types/messaging.d.ts +282 -0
- package/dist/types/messaging.js +100 -0
- package/dist/types/milestone.d.ts +20 -0
- package/dist/types/milestone.js +7 -0
- package/dist/types/pipeline.d.ts +115 -0
- package/dist/types/pipeline.js +15 -0
- package/dist/types/smartImageEditor.d.ts +42 -0
- package/dist/types/smartImageEditor.js +1 -0
- package/dist/types/templateVars.d.ts +34 -0
- package/dist/types/templateVars.js +12 -0
- package/dist/utils/currency.d.ts +8 -0
- package/dist/utils/currency.js +17 -0
- package/dist/utils/daisyuiColors.d.ts +37 -0
- package/dist/utils/daisyuiColors.js +152 -0
- package/dist/utils/dateFormatters.d.ts +69 -0
- package/dist/utils/dateFormatters.js +199 -0
- package/dist/utils/googleMapsLoader.d.ts +19 -0
- package/dist/utils/googleMapsLoader.js +121 -0
- package/dist/utils/mentionParser.d.ts +67 -0
- package/dist/utils/mentionParser.js +132 -0
- package/dist/utils/phone.d.ts +13 -0
- package/dist/utils/phone.js +67 -0
- package/dist/utils/phoneFormat.d.ts +26 -0
- package/dist/utils/phoneFormat.js +69 -0
- package/dist/utils/taskUtils.d.ts +37 -0
- package/dist/utils/taskUtils.js +71 -0
- package/package.json +3 -2
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Annotation, AnnotationProfile, AnnotationCallbacks } from '../types/annotation'
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
annotations: Annotation[]
|
|
6
|
+
profiles: Record<string, AnnotationProfile>
|
|
7
|
+
activeAnnotationId?: string | null
|
|
8
|
+
currentUserId: string
|
|
9
|
+
callbacks: AnnotationCallbacks
|
|
10
|
+
onAnnotationSelect?: (annotation: Annotation | null) => void
|
|
11
|
+
onAnnotationsChanged?: () => void
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
let {
|
|
15
|
+
annotations = [],
|
|
16
|
+
profiles = {},
|
|
17
|
+
activeAnnotationId = null,
|
|
18
|
+
currentUserId,
|
|
19
|
+
callbacks,
|
|
20
|
+
onAnnotationSelect,
|
|
21
|
+
onAnnotationsChanged,
|
|
22
|
+
}: Props = $props()
|
|
23
|
+
|
|
24
|
+
let replyContent = $state("")
|
|
25
|
+
let isSubmitting = $state(false)
|
|
26
|
+
let showResolved = $state(false)
|
|
27
|
+
let editingId = $state<string | null>(null)
|
|
28
|
+
let editContent = $state("")
|
|
29
|
+
|
|
30
|
+
// Group annotations into threads
|
|
31
|
+
const threads = $derived.by(() => {
|
|
32
|
+
const roots = annotations.filter((a) => !a.parent_annotation_id)
|
|
33
|
+
return roots
|
|
34
|
+
.filter((r) => showResolved || !r.resolved)
|
|
35
|
+
.map((root) => ({
|
|
36
|
+
root,
|
|
37
|
+
replies: annotations
|
|
38
|
+
.filter((a) => a.parent_annotation_id === root.id)
|
|
39
|
+
.sort((a, b) => new Date(a.created_at).getTime() - new Date(b.created_at).getTime()),
|
|
40
|
+
}))
|
|
41
|
+
.sort((a, b) => new Date(b.root.created_at).getTime() - new Date(a.root.created_at).getTime())
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
const resolvedCount = $derived(
|
|
45
|
+
annotations.filter((a) => !a.parent_annotation_id && a.resolved).length,
|
|
46
|
+
)
|
|
47
|
+
const totalRoots = $derived(annotations.filter((a) => !a.parent_annotation_id).length)
|
|
48
|
+
|
|
49
|
+
function getAuthorName(userId: string): string {
|
|
50
|
+
return profiles[userId]?.full_name || "Unknown"
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function getInitials(userId: string): string {
|
|
54
|
+
const name = getAuthorName(userId)
|
|
55
|
+
return name
|
|
56
|
+
.split(" ")
|
|
57
|
+
.map((n) => n[0])
|
|
58
|
+
.join("")
|
|
59
|
+
.toUpperCase()
|
|
60
|
+
.slice(0, 2)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function formatTime(dateStr: string): string {
|
|
64
|
+
const date = new Date(dateStr)
|
|
65
|
+
const now = new Date()
|
|
66
|
+
const diffMs = now.getTime() - date.getTime()
|
|
67
|
+
const diffMins = Math.floor(diffMs / 60000)
|
|
68
|
+
if (diffMins < 1) return "just now"
|
|
69
|
+
if (diffMins < 60) return `${diffMins}m ago`
|
|
70
|
+
const diffHours = Math.floor(diffMins / 60)
|
|
71
|
+
if (diffHours < 24) return `${diffHours}h ago`
|
|
72
|
+
const diffDays = Math.floor(diffHours / 24)
|
|
73
|
+
if (diffDays < 7) return `${diffDays}d ago`
|
|
74
|
+
return date.toLocaleDateString("en-US", { month: "short", day: "numeric" })
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async function submitReply(parentId: string) {
|
|
78
|
+
if (!replyContent.trim() || isSubmitting) return
|
|
79
|
+
isSubmitting = true
|
|
80
|
+
try {
|
|
81
|
+
await callbacks.onAddReply(parentId, replyContent.trim())
|
|
82
|
+
replyContent = ""
|
|
83
|
+
onAnnotationsChanged?.()
|
|
84
|
+
} catch (err) {
|
|
85
|
+
console.error("Error posting reply:", err)
|
|
86
|
+
} finally {
|
|
87
|
+
isSubmitting = false
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async function toggleResolve(annotation: Annotation) {
|
|
92
|
+
try {
|
|
93
|
+
await callbacks.onResolve(annotation.id, !annotation.resolved)
|
|
94
|
+
onAnnotationsChanged?.()
|
|
95
|
+
} catch (err) {
|
|
96
|
+
console.error("Error updating annotation:", err)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async function deleteAnnotation(id: string) {
|
|
101
|
+
try {
|
|
102
|
+
await callbacks.onDelete(id)
|
|
103
|
+
if (activeAnnotationId === id) onAnnotationSelect?.(null)
|
|
104
|
+
onAnnotationsChanged?.()
|
|
105
|
+
} catch (err) {
|
|
106
|
+
console.error("Error deleting annotation:", err)
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async function saveEdit(id: string) {
|
|
111
|
+
if (!editContent.trim() || isSubmitting) return
|
|
112
|
+
isSubmitting = true
|
|
113
|
+
try {
|
|
114
|
+
await callbacks.onEdit(id, editContent.trim())
|
|
115
|
+
editingId = null
|
|
116
|
+
editContent = ""
|
|
117
|
+
onAnnotationsChanged?.()
|
|
118
|
+
} catch (err) {
|
|
119
|
+
console.error("Error editing annotation:", err)
|
|
120
|
+
} finally {
|
|
121
|
+
isSubmitting = false
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function startEdit(annotation: Annotation) {
|
|
126
|
+
editingId = annotation.id
|
|
127
|
+
editContent = annotation.content
|
|
128
|
+
}
|
|
129
|
+
</script>
|
|
130
|
+
|
|
131
|
+
<div class="flex flex-col h-full">
|
|
132
|
+
<!-- Header -->
|
|
133
|
+
<div class="flex items-center justify-between px-3 py-2 border-b border-base-300 flex-shrink-0">
|
|
134
|
+
<div class="flex items-center gap-2">
|
|
135
|
+
<h4 class="text-sm font-semibold">Annotations</h4>
|
|
136
|
+
{#if totalRoots > 0}
|
|
137
|
+
<span class="badge badge-xs badge-ghost">{totalRoots - resolvedCount} open</span>
|
|
138
|
+
{/if}
|
|
139
|
+
</div>
|
|
140
|
+
{#if resolvedCount > 0}
|
|
141
|
+
<label class="flex items-center gap-1.5 cursor-pointer">
|
|
142
|
+
<span class="text-xs text-base-content/50">Show resolved</span>
|
|
143
|
+
<input
|
|
144
|
+
type="checkbox"
|
|
145
|
+
class="toggle toggle-xs"
|
|
146
|
+
bind:checked={showResolved}
|
|
147
|
+
/>
|
|
148
|
+
</label>
|
|
149
|
+
{/if}
|
|
150
|
+
</div>
|
|
151
|
+
|
|
152
|
+
<!-- Thread list -->
|
|
153
|
+
<div class="flex-1 overflow-y-auto">
|
|
154
|
+
{#if threads.length === 0}
|
|
155
|
+
<div class="text-center py-8 px-4">
|
|
156
|
+
<svg class="mx-auto h-10 w-10 text-base-content/20" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
157
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M7 8h10M7 12h4m1 8l-4-4H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-3l-4 4z" />
|
|
158
|
+
</svg>
|
|
159
|
+
<p class="text-sm text-base-content/40 mt-2">No annotations yet</p>
|
|
160
|
+
<p class="text-xs text-base-content/30 mt-1">Click the pin icon then click on the image to add one</p>
|
|
161
|
+
</div>
|
|
162
|
+
{:else}
|
|
163
|
+
{#each threads as thread (thread.root.id)}
|
|
164
|
+
{@const isActive = activeAnnotationId === thread.root.id}
|
|
165
|
+
<div
|
|
166
|
+
class="border-b border-base-200 transition-colors {isActive ? 'bg-primary/5' : 'hover:bg-base-200/50'}"
|
|
167
|
+
>
|
|
168
|
+
<!-- Root annotation -->
|
|
169
|
+
<button
|
|
170
|
+
class="w-full text-left px-3 py-2.5"
|
|
171
|
+
onclick={() => onAnnotationSelect?.(isActive ? null : thread.root)}
|
|
172
|
+
>
|
|
173
|
+
<div class="flex items-start gap-2">
|
|
174
|
+
<!-- Avatar -->
|
|
175
|
+
<div class="flex-shrink-0 w-6 h-6 rounded-full bg-primary/10 text-primary flex items-center justify-center text-[10px] font-bold mt-0.5">
|
|
176
|
+
{getInitials(thread.root.user_id)}
|
|
177
|
+
</div>
|
|
178
|
+
<div class="flex-1 min-w-0">
|
|
179
|
+
<div class="flex items-center gap-1.5">
|
|
180
|
+
<span class="text-xs font-medium text-base-content">{getAuthorName(thread.root.user_id)}</span>
|
|
181
|
+
<span class="text-[10px] text-base-content/40">{formatTime(thread.root.created_at)}</span>
|
|
182
|
+
{#if thread.root.resolved}
|
|
183
|
+
<span class="badge badge-xs badge-success gap-0.5">
|
|
184
|
+
<svg class="w-2.5 h-2.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
185
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M5 13l4 4L19 7" />
|
|
186
|
+
</svg>
|
|
187
|
+
Resolved
|
|
188
|
+
</span>
|
|
189
|
+
{/if}
|
|
190
|
+
</div>
|
|
191
|
+
<p class="text-sm text-base-content/80 mt-0.5 {isActive ? '' : 'line-clamp-2'}">{thread.root.content}</p>
|
|
192
|
+
{#if thread.replies.length > 0 && !isActive}
|
|
193
|
+
<span class="text-xs text-base-content/40 mt-1 inline-block">
|
|
194
|
+
{thread.replies.length} {thread.replies.length === 1 ? "reply" : "replies"}
|
|
195
|
+
</span>
|
|
196
|
+
{/if}
|
|
197
|
+
</div>
|
|
198
|
+
</div>
|
|
199
|
+
</button>
|
|
200
|
+
|
|
201
|
+
<!-- Expanded thread -->
|
|
202
|
+
{#if isActive}
|
|
203
|
+
<div class="px-3 pb-3 space-y-2">
|
|
204
|
+
<!-- Actions for root -->
|
|
205
|
+
<div class="flex items-center gap-1 ml-8">
|
|
206
|
+
<button
|
|
207
|
+
class="btn btn-ghost btn-xs gap-1 {thread.root.resolved ? 'text-warning' : 'text-success'}"
|
|
208
|
+
onclick={() => toggleResolve(thread.root)}
|
|
209
|
+
>
|
|
210
|
+
{#if thread.root.resolved}
|
|
211
|
+
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
212
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
|
213
|
+
</svg>
|
|
214
|
+
Reopen
|
|
215
|
+
{:else}
|
|
216
|
+
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
217
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
|
|
218
|
+
</svg>
|
|
219
|
+
Resolve
|
|
220
|
+
{/if}
|
|
221
|
+
</button>
|
|
222
|
+
{#if thread.root.user_id === currentUserId}
|
|
223
|
+
<button
|
|
224
|
+
class="btn btn-ghost btn-xs text-base-content/50"
|
|
225
|
+
onclick={() => startEdit(thread.root)}
|
|
226
|
+
>Edit</button>
|
|
227
|
+
<button
|
|
228
|
+
class="btn btn-ghost btn-xs text-error/70"
|
|
229
|
+
onclick={() => deleteAnnotation(thread.root.id)}
|
|
230
|
+
>Delete</button>
|
|
231
|
+
{/if}
|
|
232
|
+
</div>
|
|
233
|
+
|
|
234
|
+
<!-- Edit form for root -->
|
|
235
|
+
{#if editingId === thread.root.id}
|
|
236
|
+
<div class="ml-8 space-y-1.5">
|
|
237
|
+
<textarea
|
|
238
|
+
class="textarea textarea-bordered textarea-sm w-full"
|
|
239
|
+
rows="2"
|
|
240
|
+
bind:value={editContent}
|
|
241
|
+
></textarea>
|
|
242
|
+
<div class="flex gap-1">
|
|
243
|
+
<button class="btn btn-xs btn-primary" onclick={() => saveEdit(thread.root.id)} disabled={isSubmitting}>Save</button>
|
|
244
|
+
<button class="btn btn-xs btn-ghost" onclick={() => { editingId = null; editContent = ""; }}>Cancel</button>
|
|
245
|
+
</div>
|
|
246
|
+
</div>
|
|
247
|
+
{/if}
|
|
248
|
+
|
|
249
|
+
<!-- Replies -->
|
|
250
|
+
{#each thread.replies as reply (reply.id)}
|
|
251
|
+
<div class="ml-8 flex items-start gap-2 bg-base-200/50 rounded-lg p-2">
|
|
252
|
+
<div class="flex-shrink-0 w-5 h-5 rounded-full bg-secondary/10 text-secondary flex items-center justify-center text-[9px] font-bold mt-0.5">
|
|
253
|
+
{getInitials(reply.user_id)}
|
|
254
|
+
</div>
|
|
255
|
+
<div class="flex-1 min-w-0">
|
|
256
|
+
<div class="flex items-center gap-1.5">
|
|
257
|
+
<span class="text-xs font-medium">{getAuthorName(reply.user_id)}</span>
|
|
258
|
+
<span class="text-[10px] text-base-content/40">{formatTime(reply.created_at)}</span>
|
|
259
|
+
</div>
|
|
260
|
+
{#if editingId === reply.id}
|
|
261
|
+
<div class="mt-1 space-y-1.5">
|
|
262
|
+
<textarea
|
|
263
|
+
class="textarea textarea-bordered textarea-xs w-full"
|
|
264
|
+
rows="2"
|
|
265
|
+
bind:value={editContent}
|
|
266
|
+
></textarea>
|
|
267
|
+
<div class="flex gap-1">
|
|
268
|
+
<button class="btn btn-xs btn-primary" onclick={() => saveEdit(reply.id)} disabled={isSubmitting}>Save</button>
|
|
269
|
+
<button class="btn btn-xs btn-ghost" onclick={() => { editingId = null; editContent = ""; }}>Cancel</button>
|
|
270
|
+
</div>
|
|
271
|
+
</div>
|
|
272
|
+
{:else}
|
|
273
|
+
<p class="text-sm text-base-content/80 mt-0.5">{reply.content}</p>
|
|
274
|
+
{#if reply.user_id === currentUserId}
|
|
275
|
+
<div class="flex gap-1 mt-1">
|
|
276
|
+
<button class="btn btn-ghost btn-xs text-base-content/40 h-5 min-h-0" onclick={() => startEdit(reply)}>Edit</button>
|
|
277
|
+
<button class="btn btn-ghost btn-xs text-error/50 h-5 min-h-0" onclick={() => deleteAnnotation(reply.id)}>Delete</button>
|
|
278
|
+
</div>
|
|
279
|
+
{/if}
|
|
280
|
+
{/if}
|
|
281
|
+
</div>
|
|
282
|
+
</div>
|
|
283
|
+
{/each}
|
|
284
|
+
|
|
285
|
+
<!-- Reply input -->
|
|
286
|
+
<div class="ml-8 flex gap-2">
|
|
287
|
+
<textarea
|
|
288
|
+
class="textarea textarea-bordered textarea-sm flex-1 min-h-[36px]"
|
|
289
|
+
rows="1"
|
|
290
|
+
placeholder="Reply..."
|
|
291
|
+
bind:value={replyContent}
|
|
292
|
+
onkeydown={(e) => {
|
|
293
|
+
if (e.key === "Enter" && !e.shiftKey) {
|
|
294
|
+
e.preventDefault()
|
|
295
|
+
submitReply(thread.root.id)
|
|
296
|
+
}
|
|
297
|
+
}}
|
|
298
|
+
></textarea>
|
|
299
|
+
<button
|
|
300
|
+
class="btn btn-sm btn-primary self-end"
|
|
301
|
+
disabled={!replyContent.trim() || isSubmitting}
|
|
302
|
+
onclick={() => submitReply(thread.root.id)}
|
|
303
|
+
>
|
|
304
|
+
{#if isSubmitting}
|
|
305
|
+
<span class="loading loading-spinner loading-xs"></span>
|
|
306
|
+
{:else}
|
|
307
|
+
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
308
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8" />
|
|
309
|
+
</svg>
|
|
310
|
+
{/if}
|
|
311
|
+
</button>
|
|
312
|
+
</div>
|
|
313
|
+
</div>
|
|
314
|
+
{/if}
|
|
315
|
+
</div>
|
|
316
|
+
{/each}
|
|
317
|
+
{/if}
|
|
318
|
+
</div>
|
|
319
|
+
</div>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Annotation, AnnotationProfile, AnnotationCallbacks } from '../types/annotation';
|
|
2
|
+
interface Props {
|
|
3
|
+
annotations: Annotation[];
|
|
4
|
+
profiles: Record<string, AnnotationProfile>;
|
|
5
|
+
activeAnnotationId?: string | null;
|
|
6
|
+
currentUserId: string;
|
|
7
|
+
callbacks: AnnotationCallbacks;
|
|
8
|
+
onAnnotationSelect?: (annotation: Annotation | null) => void;
|
|
9
|
+
onAnnotationsChanged?: () => void;
|
|
10
|
+
}
|
|
11
|
+
declare const AnnotationPanel: import("svelte").Component<Props, {}, "">;
|
|
12
|
+
type AnnotationPanel = ReturnType<typeof AnnotationPanel>;
|
|
13
|
+
export default AnnotationPanel;
|