All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 7s
- Add brainstorm feature with collaborative canvas, AI idea generation, live cursors, playback, and export - Add PDF upload/extraction/ingestion pipeline with pgvector document search (RAG) - Add document Q&A overlay with streaming chat and PDF preview - Add note attachments UI with status polling, grid layout, and auto-scroll - Add task extraction AI tool and agent executor improvements - Fix NoteEmbedding missing updatedAt column, re-index 66 notes with 1536-dim embeddings - Fix brainstorm 'Create Note' button: add success toast and redirect to created note - Fix memory echo notification infinite polling - Fix chat route to always include document_search tool - Add brainstorm i18n keys across all 14 locales - Add socket server for real-time brainstorm collaboration - Add hierarchical notebook selector and organize notebook dialog improvements - Add sidebar brainstorm section with session management - Update prisma schema with brainstorm tables, attachments, and document chunks
75 lines
2.0 KiB
TypeScript
75 lines
2.0 KiB
TypeScript
import { format, type Locale } from 'date-fns'
|
||
import { toJalaali } from 'jalaali-js'
|
||
|
||
const JALALI_MONTHS_FA = [
|
||
'فروردین',
|
||
'اردیبهشت',
|
||
'خرداد',
|
||
'تیر',
|
||
'مرداد',
|
||
'شهریور',
|
||
'مهر',
|
||
'آبان',
|
||
'آذر',
|
||
'دی',
|
||
'بهمن',
|
||
'اسفند',
|
||
] as const
|
||
|
||
const PERSIAN_DIGITS = ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹'] as const
|
||
|
||
/** Western digits → Persian (Extended Arabic-Indic) numerals, e.g. 1405 → ۱۴۰۵ */
|
||
export function toPersianDigits(input: string): string {
|
||
return input.replace(/\d/g, (ch) => PERSIAN_DIGITS[Number(ch)] ?? ch)
|
||
}
|
||
|
||
function pad2(n: number): string {
|
||
return n < 10 ? `0${n}` : `${n}`
|
||
}
|
||
|
||
function formatJalaliAbsolute(date: Date, pattern: string): string {
|
||
const { jy, jm, jd } = toJalaali(date)
|
||
const monthName = JALALI_MONTHS_FA[jm - 1]
|
||
const h = pad2(date.getHours())
|
||
const min = pad2(date.getMinutes())
|
||
|
||
let s: string
|
||
switch (pattern) {
|
||
case 'd MMM yyyy':
|
||
s = `${jd} ${monthName} ${jy}`
|
||
break
|
||
case 'd MMM yyyy · HH:mm':
|
||
s = `${jd} ${monthName} ${jy} · ${h}:${min}`
|
||
break
|
||
case 'd MMM yyyy HH:mm':
|
||
s = `${jd} ${monthName} ${jy} ${h}:${min}`
|
||
break
|
||
case 'd MMM · HH:mm':
|
||
s = `${jd} ${monthName} · ${h}:${min}`
|
||
break
|
||
case 'MMM d, yyyy':
|
||
s = `${monthName} ${jd}، ${jy}`
|
||
break
|
||
default:
|
||
s = `${jd} ${monthName} ${jy}`
|
||
}
|
||
return toPersianDigits(s)
|
||
}
|
||
|
||
/**
|
||
* Absolute calendar dates for Persian (`fa`) use the Solar Hijri (Jalali / هجری شمسی) calendar.
|
||
* Times remain in the user's local timezone. For relative phrases, keep using `formatDistanceToNow` with `faIR`.
|
||
*/
|
||
export function formatAbsoluteDateLocalized(
|
||
date: Date | string,
|
||
language: string,
|
||
pattern: string,
|
||
locale: Locale
|
||
): string {
|
||
const d = typeof date === 'string' ? new Date(date) : date
|
||
if (language === 'fa') {
|
||
return formatJalaliAbsolute(d, pattern)
|
||
}
|
||
return format(d, pattern, { locale })
|
||
}
|