@joewinke/jatui 0.1.23 → 0.1.25
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 -136
- 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,188 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Mention Parser Utilities
|
|
3
|
-
*
|
|
4
|
-
* Handles parsing and rendering of @-mentions in messages.
|
|
5
|
-
* Format: [@Display Name](type:id) -> clickable links
|
|
6
|
-
*
|
|
7
|
-
* The URL generator is configurable — consuming apps provide their own
|
|
8
|
-
* routing via the `urlGenerator` parameter or by setting a default.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
export interface MentionMatch {
|
|
12
|
-
type: string
|
|
13
|
-
id: string
|
|
14
|
-
displayName: string
|
|
15
|
-
url: string
|
|
16
|
-
startIndex: number
|
|
17
|
-
endIndex: number
|
|
18
|
-
fullMatch: string
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export type MentionUrlGenerator = (type: string, id: string) => string
|
|
22
|
-
|
|
23
|
-
// Regex to match mention pattern: [@Display Name](type:id)
|
|
24
|
-
const MENTION_REGEX = /\[@([^\]]+)\]\(([^:]+):([^)]+)\)/g
|
|
25
|
-
|
|
26
|
-
// Default URL generator (apps can override)
|
|
27
|
-
let defaultUrlGenerator: MentionUrlGenerator = (type, id) => `#${type}/${id}`
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Set the default URL generator for mentions.
|
|
31
|
-
* Call this once at app startup to configure mention link routing.
|
|
32
|
-
*/
|
|
33
|
-
export function setMentionUrlGenerator(generator: MentionUrlGenerator): void {
|
|
34
|
-
defaultUrlGenerator = generator
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Parse message content and extract all mentions
|
|
39
|
-
*/
|
|
40
|
-
export function parseMentions(
|
|
41
|
-
content: string,
|
|
42
|
-
urlGenerator?: MentionUrlGenerator
|
|
43
|
-
): MentionMatch[] {
|
|
44
|
-
const mentions: MentionMatch[] = []
|
|
45
|
-
const gen = urlGenerator ?? defaultUrlGenerator
|
|
46
|
-
let match
|
|
47
|
-
|
|
48
|
-
MENTION_REGEX.lastIndex = 0
|
|
49
|
-
|
|
50
|
-
while ((match = MENTION_REGEX.exec(content)) !== null) {
|
|
51
|
-
const [fullMatch, displayName, type, id] = match
|
|
52
|
-
|
|
53
|
-
mentions.push({
|
|
54
|
-
type,
|
|
55
|
-
id,
|
|
56
|
-
displayName,
|
|
57
|
-
url: gen(type, id),
|
|
58
|
-
startIndex: match.index,
|
|
59
|
-
endIndex: match.index + fullMatch.length,
|
|
60
|
-
fullMatch
|
|
61
|
-
})
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return mentions
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Convert message content with mentions to HTML
|
|
69
|
-
*/
|
|
70
|
-
export function renderMentionsAsHTML(
|
|
71
|
-
content: string,
|
|
72
|
-
urlGenerator?: MentionUrlGenerator
|
|
73
|
-
): string {
|
|
74
|
-
const mentions = parseMentions(content, urlGenerator)
|
|
75
|
-
|
|
76
|
-
if (mentions.length === 0) {
|
|
77
|
-
return escapeHTML(content)
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
let result = ''
|
|
81
|
-
let lastIndex = 0
|
|
82
|
-
|
|
83
|
-
mentions.forEach((mention) => {
|
|
84
|
-
result += escapeHTML(content.substring(lastIndex, mention.startIndex))
|
|
85
|
-
result += `<a href="${mention.url}" class="mention-link mention-${mention.type}" data-mention-type="${mention.type}" data-mention-id="${mention.id}">@${escapeHTML(mention.displayName)}</a>`
|
|
86
|
-
lastIndex = mention.endIndex
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
result += escapeHTML(content.substring(lastIndex))
|
|
90
|
-
return result
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Create a mention string for insertion into message input
|
|
95
|
-
*/
|
|
96
|
-
export function createMentionString(type: string, id: string, displayName: string): string {
|
|
97
|
-
return `[@${displayName}](${type}:${id})`
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Extract plain text from message content (remove mention markup)
|
|
102
|
-
*/
|
|
103
|
-
export function extractPlainText(content: string): string {
|
|
104
|
-
MENTION_REGEX.lastIndex = 0
|
|
105
|
-
return content.replace(MENTION_REGEX, '@$1')
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Check if cursor is inside a mention
|
|
110
|
-
*/
|
|
111
|
-
export function findMentionAtCursor(content: string, cursorPosition: number): MentionMatch | null {
|
|
112
|
-
const mentions = parseMentions(content)
|
|
113
|
-
return (
|
|
114
|
-
mentions.find(
|
|
115
|
-
(mention) => cursorPosition >= mention.startIndex && cursorPosition <= mention.endIndex
|
|
116
|
-
) || null
|
|
117
|
-
)
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Find @ symbol and following text that could be a mention being typed.
|
|
122
|
-
* Supported symbols: @ $ # ! ~ / +
|
|
123
|
-
*/
|
|
124
|
-
export function findPartialMention(
|
|
125
|
-
content: string,
|
|
126
|
-
cursorPosition: number
|
|
127
|
-
): { start: number; query: string } | null {
|
|
128
|
-
const mentionSymbols = ['@', '$', '#', '!', '~', '/', '+']
|
|
129
|
-
let symbolIndex = -1
|
|
130
|
-
|
|
131
|
-
for (let i = cursorPosition - 1; i >= 0; i--) {
|
|
132
|
-
const char = content[i]
|
|
133
|
-
|
|
134
|
-
if (mentionSymbols.includes(char)) {
|
|
135
|
-
symbolIndex = i
|
|
136
|
-
break
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
if (char === ' ' || char === '\n' || char === ']' || char === ')') {
|
|
140
|
-
break
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
if (symbolIndex === -1) return null
|
|
145
|
-
|
|
146
|
-
const query = content.substring(symbolIndex + 1, cursorPosition).trim()
|
|
147
|
-
|
|
148
|
-
const existingMention = findMentionAtCursor(content, symbolIndex)
|
|
149
|
-
if (existingMention) return null
|
|
150
|
-
|
|
151
|
-
return { start: symbolIndex, query }
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* Replace partial mention with complete mention string
|
|
156
|
-
*/
|
|
157
|
-
export function replaceMentionInContent(
|
|
158
|
-
content: string,
|
|
159
|
-
startIndex: number,
|
|
160
|
-
endIndex: number,
|
|
161
|
-
mentionString: string
|
|
162
|
-
): { newContent: string; newCursorPosition: number } {
|
|
163
|
-
const before = content.substring(0, startIndex)
|
|
164
|
-
const after = content.substring(endIndex)
|
|
165
|
-
const newContent = before + mentionString + after
|
|
166
|
-
const newCursorPosition = startIndex + mentionString.length
|
|
167
|
-
|
|
168
|
-
return { newContent, newCursorPosition }
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Simple HTML escape function
|
|
173
|
-
*/
|
|
174
|
-
export function escapeHTML(text: string): string {
|
|
175
|
-
return text
|
|
176
|
-
.replace(/&/g, '&')
|
|
177
|
-
.replace(/</g, '<')
|
|
178
|
-
.replace(/>/g, '>')
|
|
179
|
-
.replace(/"/g, '"')
|
|
180
|
-
.replace(/'/g, ''')
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* Count mentions in content
|
|
185
|
-
*/
|
|
186
|
-
export function countMentions(content: string): number {
|
|
187
|
-
return parseMentions(content).length
|
|
188
|
-
}
|
package/src/lib/utils/phone.ts
DELETED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
parsePhoneNumberFromString,
|
|
3
|
-
type CountryCode,
|
|
4
|
-
getCountries,
|
|
5
|
-
getCountryCallingCode,
|
|
6
|
-
} from "libphonenumber-js";
|
|
7
|
-
|
|
8
|
-
export const DEFAULT_COUNTRY: CountryCode = "US";
|
|
9
|
-
|
|
10
|
-
export function toE164(
|
|
11
|
-
raw: string | null | undefined,
|
|
12
|
-
defaultCountry: CountryCode = DEFAULT_COUNTRY,
|
|
13
|
-
): string | null {
|
|
14
|
-
if (!raw) return null;
|
|
15
|
-
const trimmed = raw.trim();
|
|
16
|
-
if (!trimmed) return null;
|
|
17
|
-
const parsed = parsePhoneNumberFromString(trimmed, defaultCountry);
|
|
18
|
-
if (parsed?.isValid()) return parsed.number;
|
|
19
|
-
return null;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export function formatPhoneForDisplay(e164: string | null | undefined): string {
|
|
23
|
-
if (!e164) return "";
|
|
24
|
-
const parsed = parsePhoneNumberFromString(e164);
|
|
25
|
-
if (!parsed) return e164;
|
|
26
|
-
return parsed.country === DEFAULT_COUNTRY ? parsed.formatNational() : parsed.formatInternational();
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export function isValidPhone(
|
|
30
|
-
raw: string | null | undefined,
|
|
31
|
-
defaultCountry: CountryCode = DEFAULT_COUNTRY,
|
|
32
|
-
): boolean {
|
|
33
|
-
if (!raw) return false;
|
|
34
|
-
const parsed = parsePhoneNumberFromString(raw.trim(), defaultCountry);
|
|
35
|
-
return parsed?.isValid() ?? false;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export type CountryOption = {
|
|
39
|
-
code: CountryCode;
|
|
40
|
-
name: string;
|
|
41
|
-
dialCode: string;
|
|
42
|
-
flag: string;
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
const COUNTRY_NAMES: Record<string, string> = {
|
|
46
|
-
US: "United States", CA: "Canada", GB: "United Kingdom", AU: "Australia", NZ: "New Zealand",
|
|
47
|
-
IE: "Ireland", DE: "Germany", FR: "France", ES: "Spain", IT: "Italy", NL: "Netherlands",
|
|
48
|
-
BE: "Belgium", CH: "Switzerland", AT: "Austria", SE: "Sweden", NO: "Norway", DK: "Denmark",
|
|
49
|
-
FI: "Finland", PT: "Portugal", PL: "Poland", CZ: "Czechia", GR: "Greece", MX: "Mexico",
|
|
50
|
-
BR: "Brazil", AR: "Argentina", CL: "Chile", CO: "Colombia", PE: "Peru", JP: "Japan",
|
|
51
|
-
KR: "South Korea", CN: "China", HK: "Hong Kong", TW: "Taiwan", SG: "Singapore", MY: "Malaysia",
|
|
52
|
-
TH: "Thailand", PH: "Philippines", ID: "Indonesia", VN: "Vietnam", IN: "India", PK: "Pakistan",
|
|
53
|
-
BD: "Bangladesh", IL: "Israel", AE: "UAE", SA: "Saudi Arabia", TR: "Turkey", EG: "Egypt",
|
|
54
|
-
ZA: "South Africa", NG: "Nigeria", KE: "Kenya", RU: "Russia", UA: "Ukraine",
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
function flagEmoji(code: string): string {
|
|
58
|
-
return code
|
|
59
|
-
.toUpperCase()
|
|
60
|
-
.split("")
|
|
61
|
-
.map((c) => String.fromCodePoint(127397 + c.charCodeAt(0)))
|
|
62
|
-
.join("");
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
let countriesCache: CountryOption[] | null = null;
|
|
66
|
-
export function listCountries(): CountryOption[] {
|
|
67
|
-
if (countriesCache) return countriesCache;
|
|
68
|
-
const all = getCountries()
|
|
69
|
-
.map((code) => ({
|
|
70
|
-
code,
|
|
71
|
-
name: COUNTRY_NAMES[code] ?? code,
|
|
72
|
-
dialCode: `+${getCountryCallingCode(code)}`,
|
|
73
|
-
flag: flagEmoji(code),
|
|
74
|
-
}))
|
|
75
|
-
.sort((a, b) => a.name.localeCompare(b.name));
|
|
76
|
-
countriesCache = all;
|
|
77
|
-
return all;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export const PRIORITY_COUNTRIES: CountryCode[] = ["US", "CA", "GB", "AU", "MX"];
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Phone number formatting utilities
|
|
3
|
-
*
|
|
4
|
-
* Provides consistent phone number formatting as (xxx) xxx-xxxx
|
|
5
|
-
* Limits input to 10 digits and handles formatting as user types
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Strips all non-numeric characters from a phone number
|
|
10
|
-
*/
|
|
11
|
-
export function stripPhoneNumber(value: string): string {
|
|
12
|
-
return value.replace(/\D/g, '');
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Formats a phone number to (xxx) xxx-xxxx format
|
|
17
|
-
*/
|
|
18
|
-
export function formatPhoneNumber(value: string): string {
|
|
19
|
-
const digits = stripPhoneNumber(value);
|
|
20
|
-
const limitedDigits = digits.slice(0, 10);
|
|
21
|
-
|
|
22
|
-
if (limitedDigits.length === 0) {
|
|
23
|
-
return '';
|
|
24
|
-
} else if (limitedDigits.length <= 3) {
|
|
25
|
-
return `(${limitedDigits}`;
|
|
26
|
-
} else if (limitedDigits.length <= 6) {
|
|
27
|
-
return `(${limitedDigits.slice(0, 3)}) ${limitedDigits.slice(3)}`;
|
|
28
|
-
} else {
|
|
29
|
-
return `(${limitedDigits.slice(0, 3)}) ${limitedDigits.slice(3, 6)}-${limitedDigits.slice(6)}`;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Checks if a phone number has exactly 10 digits
|
|
35
|
-
*/
|
|
36
|
-
export function isCompletePhoneNumber(value: string): boolean {
|
|
37
|
-
return stripPhoneNumber(value).length === 10;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Validates phone number format (exactly 10 digits)
|
|
42
|
-
*/
|
|
43
|
-
export function isValidPhoneNumber(value: string): boolean {
|
|
44
|
-
return isCompletePhoneNumber(value);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Handles input formatting with cursor position preservation
|
|
49
|
-
*/
|
|
50
|
-
export function updatePhoneInput(input: HTMLInputElement, newValue: string, oldValue: string): void {
|
|
51
|
-
const cursorPosition = input.selectionStart || 0;
|
|
52
|
-
const oldLength = oldValue.length;
|
|
53
|
-
const newLength = newValue.length;
|
|
54
|
-
|
|
55
|
-
input.value = newValue;
|
|
56
|
-
|
|
57
|
-
let newCursorPosition = cursorPosition;
|
|
58
|
-
|
|
59
|
-
if (newLength > oldLength) {
|
|
60
|
-
newCursorPosition = cursorPosition + (newLength - oldLength);
|
|
61
|
-
} else if (newLength < oldLength) {
|
|
62
|
-
newCursorPosition = Math.max(0, cursorPosition - (oldLength - newLength));
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
if (newCursorPosition === 1 && newValue.startsWith('(')) {
|
|
66
|
-
newCursorPosition = 1;
|
|
67
|
-
} else if (newCursorPosition === 4 && newValue.includes(') ')) {
|
|
68
|
-
newCursorPosition = 6;
|
|
69
|
-
} else if (newCursorPosition === 9 && newValue.includes('-')) {
|
|
70
|
-
newCursorPosition = 10;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
input.setSelectionRange(newCursorPosition, newCursorPosition);
|
|
74
|
-
}
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared helpers for project_tasks UI.
|
|
3
|
-
* Used across all JST-based projects on tasks/feedback pages.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* DaisyUI badge class for a task status value.
|
|
8
|
-
* Usage: <span class="badge {statusColor(task.status)}">...</span>
|
|
9
|
-
*
|
|
10
|
-
* Aligned to the canonical JAT TASK_STATUS palette
|
|
11
|
-
* (jat/ide/src/lib/config/statusColors.ts → TASK_STATUS_VISUALS) and the
|
|
12
|
-
* committed JST portal palette (jst/src/lib/components/portal/portalStatus.ts
|
|
13
|
-
* → TASK_BADGE) so a task renders the same semantics in JAT-style admin
|
|
14
|
-
* surfaces, the JST portal, and the JAT IDE:
|
|
15
|
-
* open=ghost(neutral) · in_progress=info(blue) · blocked=warning(amber) ·
|
|
16
|
-
* closed=success(green). Paused / mid-flight states (waiting, review) and
|
|
17
|
-
* post-completion states (submitted, accepted, deployed) use the outline
|
|
18
|
-
* variants JAT applies. Where JAT and JST agree (open/in_progress/closed/
|
|
19
|
-
* waiting), this matches both; JAT's statusColors.ts is canonical otherwise.
|
|
20
|
-
*/
|
|
21
|
-
export function statusColor(status: string): string {
|
|
22
|
-
switch (status) {
|
|
23
|
-
case 'open': return 'badge-ghost';
|
|
24
|
-
case 'in_progress': return 'badge-info';
|
|
25
|
-
case 'waiting': return 'badge-info badge-outline';
|
|
26
|
-
case 'review': return 'badge-info badge-outline';
|
|
27
|
-
case 'blocked': return 'badge-warning';
|
|
28
|
-
case 'submitted': return 'badge-secondary badge-outline';
|
|
29
|
-
case 'accepted': return 'badge-success badge-outline';
|
|
30
|
-
case 'deployed': return 'badge-primary badge-outline';
|
|
31
|
-
case 'rejected': return 'badge-warning badge-outline';
|
|
32
|
-
case 'reopened': return 'badge-warning badge-outline';
|
|
33
|
-
case 'closed': return 'badge-success';
|
|
34
|
-
case 'dev': return 'badge-ghost badge-outline';
|
|
35
|
-
default: return 'badge-ghost';
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Tailwind text class for a task priority value.
|
|
41
|
-
* Usage: <span class={priorityColor(task.priority)}>...</span>
|
|
42
|
-
*/
|
|
43
|
-
export function priorityColor(priority: string | null | undefined): string {
|
|
44
|
-
switch (priority) {
|
|
45
|
-
case 'critical': return 'text-error font-bold';
|
|
46
|
-
case 'high': return 'text-warning font-semibold';
|
|
47
|
-
case 'medium': return 'text-info';
|
|
48
|
-
case 'low': return 'text-base-content/60';
|
|
49
|
-
default: return '';
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* @deprecated Prefer the `<TaskTypeIcon type={…} />` component — it renders a
|
|
55
|
-
* stroke SVG that inherits `currentColor`, scales precisely, and avoids
|
|
56
|
-
* cross-OS emoji rendering drift. Kept only for backwards compatibility.
|
|
57
|
-
* Usage: {typeIcon(task.issue_type)} {task.title}
|
|
58
|
-
*/
|
|
59
|
-
export function typeIcon(type: string): string {
|
|
60
|
-
switch (type) {
|
|
61
|
-
case 'bug': return '🐛';
|
|
62
|
-
case 'feature': return '✨';
|
|
63
|
-
case 'task': return '📋';
|
|
64
|
-
case 'epic': return '🏔️';
|
|
65
|
-
default: return '📄';
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Human-readable label for a task status value.
|
|
71
|
-
* "in_progress" → "In Progress"
|
|
72
|
-
*/
|
|
73
|
-
export function statusLabel(status: string): string {
|
|
74
|
-
return status.replace(/_/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase());
|
|
75
|
-
}
|