Jaspers Hub

jaspers / earningscall

pluginOfficialReviewed★ 0↓ 6

EarningsCall Transcripts & Calendar: Shows a company's earnings call transcripts and the daily earnings calendar from EarningsCall.biz in a single panel view.

2.3 KB
1import { useEffect, useState, type ReactElement } from 'react'
2import { usePublish, usePublishText, type PanelRef } from '@jaspers-ai/sdk'
3
4// What the calendar and the call share: the ticker field, the way a failure reads, a publish from a
5// branch that has nothing else to render, and dates written for people.
6
7/**
8 * Enter opens the ticker typed; a ticker the orchestrator sets shows here. Enter is read off the key,
9 * not a form: the view's frame is sandboxed without allow-forms, so a form never submits.
10 */
11export function TickerField({ symbol, onSymbol }: { symbol: string; onSymbol: (symbol: string) => void }): ReactElement {
12 const [draft, setDraft] = useState(symbol)
13 useEffect(() => setDraft(symbol), [symbol])
14 return (
15 <input
16 className="ec-input ec-input-ticker"
17 name="symbol"
18 value={draft}
19 placeholder="Ticker"
20 spellCheck={false}
21 onChange={(event) => setDraft(event.target.value)}
22 onKeyDown={(event) => {
23 if (event.key !== 'Enter' || event.nativeEvent.isComposing) return
24 event.preventDefault()
25 const next = draft.trim().toUpperCase()
26 if (next) onSymbol(next)
27 }}
28 />
29 )
30}
31
32/** What a branch shows, published: its output, and its text, which only a transcript has. */
33export function Publish({ panel, output, text }: { panel: PanelRef; output: Record<string, unknown>; text: string | null }): null {
34 usePublish(panel, output)
35 usePublishText(panel, text)
36 return null
37}
38
39/** The message as the server wrote it, without the wrapping the IPC adds. */
40export function cleanError(message: string): string {
41 return message.replace(/^Error invoking remote method '[^']*': (?:Error: )?/, '')
42}
43
44/** 'Wed, Jul 29, 2026' for a day or for the day part of a timestamp. */
45export function longDate(value: string | null): string {
46 const match = value ? /^(\d{4})-(\d{2})-(\d{2})/.exec(value) : null
47 if (!match) return ''
48 const day = new Date(Date.UTC(Number(match[1]), Number(match[2]) - 1, Number(match[3])))
49 return day.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric', year: 'numeric', timeZone: 'UTC' })
50}
51
52/** A call's start in the viewer's own zone, like '5:30 PM'. */
53export function localTime(value: string | null): string {
54 const time = value ? Date.parse(value) : NaN
55 return Number.isNaN(time) ? '—' : new Date(time).toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' })
56}