jira-dashboard 0.1.0
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/README.md +219 -0
- package/dist/cli.js +1347 -0
- package/dist/cli.js.map +7 -0
- package/package.json +52 -0
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/cli.tsx", "../src/app.tsx", "../src/components/IssueList.tsx", "../src/components/PanelFrame.tsx", "../src/components/PanelStatus.tsx", "../src/components/IssuePreview.tsx", "../src/components/WorkChartPanel.tsx", "../src/components/WorklogPanel.tsx", "../src/data/adf.ts", "../src/data/sprintField.ts", "../src/data/issues.ts", "../src/data/worklogs.ts", "../src/hooks/useHomeEndKeys.ts", "../src/hooks/useMouse.ts", "../src/hooks/useTerminalSize.ts", "../src/openUrl.ts", "../src/types.ts", "../src/data/client.ts", "../src/data/config.ts"],
|
|
4
|
+
"sourcesContent": ["#!/usr/bin/env node\nimport { render } from 'ink';\nimport { createRequire } from 'node:module';\n\nimport { App } from './app';\nimport { JiraClient } from './data/client';\nimport { CONFIG_PATH, DEFAULT_ISSUES_JQL, loadConfig } from './data/config';\nimport { JiraError } from './types';\n\nconst require = createRequire(import.meta.url);\nconst { version } = require('../package.json') as { version: string };\n\n/**\n * Built from a char code on purpose: a literal escape byte in source is\n * invisible and easy to lose to an editor or a copy-paste.\n */\nconst ESC = String.fromCharCode(27);\nconst ALT_SCREEN_ON = `${ESC}[?1049h`;\nconst ALT_SCREEN_OFF = `${ESC}[?1049l`;\nconst CURSOR_SHOW = `${ESC}[?25h`;\n\nconst HELP = `\n jdb \u2014 Jira dashboard in your terminal\n\n Usage\n $ jdb\n\n Options\n -h, --help Show this help\n -v, --version Show the version\n\n Configuration (environment variables take precedence)\n JIRA_BASE_URL https://your-team.atlassian.net\n JIRA_EMAIL your Atlassian account email\n JIRA_API_TOKEN https://id.atlassian.com/manage-profile/security/api-tokens\n\n Or ${CONFIG_PATH}:\n {\n \"baseUrl\": \"https://your-team.atlassian.net\",\n \"email\": \"you@example.com\",\n \"apiToken\": \"...\",\n \"issuesJql\": ${JSON.stringify(DEFAULT_ISSUES_JQL)},\n \"worklogDays\": 14\n }\n\n Keys\n \u2191 \u2193 / j k move within the focused panel\n home / end jump to the first / last item\n enter open the selected ticket in your browser\n tab cycle panels (worklogs \u2192 tickets \u2192 preview)\n click/wheel select and scroll\n r reload\n q / esc quit\n`;\n\n/** Print an error the way clig.dev recommends: what broke, then how to fix it. */\nfunction reportError(error: unknown): void {\n const jiraError = error instanceof JiraError ? error : null;\n process.stderr.write(`\\njdb: ${jiraError?.message ?? String(error)}\\n`);\n for (const hint of jiraError?.hints ?? []) {\n process.stderr.write(` ${hint}\\n`);\n }\n process.stderr.write('\\n');\n}\n\nasync function main(): Promise<number> {\n const args = process.argv.slice(2);\n\n if (args.includes('-h') || args.includes('--help')) {\n process.stdout.write(`${HELP}\\n`);\n return 0;\n }\n if (args.includes('-v') || args.includes('--version')) {\n process.stdout.write(`${version}\\n`);\n return 0;\n }\n\n if (!process.stdout.isTTY) {\n process.stderr.write('jdb: needs an interactive terminal (stdout is not a TTY).\\n');\n return 1;\n }\n\n const config = loadConfig();\n const client = new JiraClient(config);\n const me = await client.myself();\n\n // The alternate screen keeps the user's scrollback intact: everything jdb\n // draws goes to a separate buffer that the terminal discards on the way out.\n let restored = false;\n const restoreScreen = (): void => {\n if (restored) return;\n restored = true;\n process.stdout.write(ALT_SCREEN_OFF + CURSOR_SHOW);\n };\n\n process.stdout.write(ALT_SCREEN_ON);\n // Cover every way the process can end, including a crash or a signal.\n process.on('exit', restoreScreen);\n for (const signal of ['SIGINT', 'SIGTERM', 'SIGHUP'] as const) {\n process.once(signal, () => {\n restoreScreen();\n process.exit(signal === 'SIGINT' ? 130 : 143);\n });\n }\n\n try {\n const { waitUntilExit } = render(<App config={config} client={client} me={me} />, {\n exitOnCtrlC: true,\n });\n await waitUntilExit();\n } finally {\n restoreScreen();\n }\n\n return 0;\n}\n\nmain()\n .then((code) => {\n process.exitCode = code;\n })\n .catch((error: unknown) => {\n reportError(error);\n process.exitCode = 1;\n });\n", "import { Box, Text, useApp, useInput } from 'ink';\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport type { ReactElement } from 'react';\n\nimport { IssueList } from './components/IssueList';\nimport { IssuePreview } from './components/IssuePreview';\nimport { PANEL_HEADER_ROWS } from './components/PanelFrame';\nimport { spinnerChar } from './components/PanelStatus';\nimport { chartWidthFor, WorkChartPanel } from './components/WorkChartPanel';\nimport { WorklogPanel } from './components/WorklogPanel';\nimport type { JiraClient } from './data/client';\nimport { fetchOpenIssues } from './data/issues';\nimport { fetchWorklogDays } from './data/worklogs';\nimport { useHomeEndKeys } from './hooks/useHomeEndKeys';\nimport { isMouseSequence, useMouse, type MouseEvent } from './hooks/useMouse';\nimport { useTerminalSize } from './hooks/useTerminalSize';\nimport { openUrl } from './openUrl';\nimport { JiraError } from './types';\nimport type { Config, CurrentUser, Issue, Loadable, WorklogDay, WorklogSummary } from './types';\n\ninterface AppProps {\n config: Config;\n client: JiraClient;\n me: CurrentUser;\n}\n\ntype Panel = 'worklog' | 'issues' | 'preview';\nconst PANEL_ORDER: Panel[] = ['worklog', 'issues', 'preview'];\n\nconst MIN_TOP_HEIGHT = 6;\nconst MIN_LEFT_WIDTH = 34;\nconst SPINNER_INTERVAL_MS = 90;\nconst NOTICE_TIMEOUT_MS = 3000;\n\nconst pending = <T,>(previous?: Loadable<T>): Loadable<T> => ({\n data: previous?.data ?? null,\n loading: true,\n error: null,\n});\n\nfunction toJiraError(cause: unknown): JiraError {\n if (cause instanceof JiraError) return cause;\n return new JiraError(cause instanceof Error ? cause.message : String(cause));\n}\n\nexport function App({ config, client, me }: AppProps): ReactElement {\n const { exit } = useApp();\n const { columns, rows } = useTerminalSize();\n\n const [worklogs, setWorklogs] = useState<Loadable<WorklogSummary>>(() => pending());\n const [issues, setIssues] = useState<Loadable<Issue[]>>(() => pending());\n const [focus, setFocus] = useState<Panel>('issues');\n const [selectedIndex, setSelectedIndex] = useState(0);\n const [issueOffset, setIssueOffset] = useState(0);\n const [worklogOffset, setWorklogOffset] = useState(0);\n const [previewOffset, setPreviewOffset] = useState(0);\n const [spinnerFrame, setSpinnerFrame] = useState(0);\n const [reloadToken, setReloadToken] = useState(0);\n const [notice, setNotice] = useState<{ text: string; error: boolean } | null>(null);\n\n // ---- layout -------------------------------------------------------------\n const topHeight = Math.max(MIN_TOP_HEIGHT, Math.floor((rows - 1) / 2));\n const bottomHeight = Math.max(MIN_TOP_HEIGHT, rows - 1 - topHeight);\n const leftWidth = Math.max(MIN_LEFT_WIDTH, Math.min(52, Math.floor(columns * 0.42)));\n const issueRows = Math.max(1, topHeight - 3);\n const worklogRows = issueRows;\n\n // The chart takes the width its bars need, but never more than half the screen;\n // the preview gets the rest.\n const dayCount = worklogs.data?.days.length ?? config.worklogDays;\n const chartWidth = Math.min(\n Math.floor(columns / 2),\n Math.max(chartWidthFor(dayCount, 1), Math.min(chartWidthFor(dayCount, 2), columns - 40)),\n );\n\n // ---- data ---------------------------------------------------------------\n useEffect(() => {\n let cancelled = false;\n\n setIssues(pending);\n fetchOpenIssues(client, config)\n .then((data) => {\n if (!cancelled) setIssues({ data, loading: false, error: null });\n })\n .catch((cause: unknown) => {\n if (!cancelled) setIssues({ data: null, loading: false, error: toJiraError(cause) });\n });\n\n setWorklogs(pending);\n fetchWorklogDays(client, config, me)\n .then((data) => {\n if (!cancelled) setWorklogs({ data, loading: false, error: null });\n })\n .catch((cause: unknown) => {\n if (!cancelled) setWorklogs({ data: null, loading: false, error: toJiraError(cause) });\n });\n\n return () => {\n cancelled = true;\n };\n }, [client, config, me, reloadToken]);\n\n const loading = worklogs.loading || issues.loading;\n\n useEffect(() => {\n if (!loading) return;\n const timer = setInterval(() => {\n setSpinnerFrame((frame) => frame + 1);\n }, SPINNER_INTERVAL_MS);\n return () => {\n clearInterval(timer);\n };\n }, [loading]);\n\n const issueList = issues.data ?? [];\n const selected = issueList[selectedIndex] ?? null;\n\n // Keep the selection valid when a reload returns a shorter list.\n useEffect(() => {\n setSelectedIndex((index) => Math.min(index, Math.max(0, issueList.length - 1)));\n }, [issueList.length]);\n\n // Days run oldest-first, so today sits at the bottom \u2014 start scrolled there.\n useEffect(() => {\n setWorklogOffset(Math.max(0, (worklogs.data?.days.length ?? 0) - worklogRows));\n }, [worklogs.data, worklogRows]);\n\n // Reading a new ticket always starts at the top of its description.\n useEffect(() => {\n setPreviewOffset(0);\n }, [selected?.key]);\n\n // ---- selection ----------------------------------------------------------\n const selectIndex = useCallback(\n (next: number) => {\n if (issueList.length === 0) return;\n const clamped = Math.max(0, Math.min(next, issueList.length - 1));\n setSelectedIndex(clamped);\n setIssueOffset((offset) => {\n if (clamped < offset) return clamped;\n if (clamped >= offset + issueRows) return clamped - issueRows + 1;\n return offset;\n });\n },\n [issueList.length, issueRows],\n );\n\n const scrollPanel = useCallback(\n (panel: Panel, delta: number) => {\n switch (panel) {\n case 'issues':\n selectIndex(selectedIndex + delta);\n break;\n case 'worklog':\n setWorklogOffset((offset) =>\n Math.max(0, Math.min(offset + delta, (worklogs.data?.days.length ?? 0) - worklogRows)),\n );\n break;\n case 'preview':\n setPreviewOffset((offset) => Math.max(0, offset + delta));\n break;\n }\n },\n [selectIndex, selectedIndex, worklogs.data?.days.length, worklogRows],\n );\n\n /**\n * The selected issue's hours across the whole window, zero-filled.\n *\n * `byIssue` only holds days that have work, so the days axis comes from the\n * summary to keep every ticket's chart on the same 14-day scale.\n */\n const chartDays = useMemo<WorklogDay[]>(() => {\n const summary = worklogs.data;\n if (!summary || !selected) return [];\n\n const hoursByDate = new Map(\n (summary.byIssue[selected.key] ?? []).map((entry) => [entry.date, entry.hours]),\n );\n return summary.days.map((day) => ({\n date: day.date,\n hours: hoursByDate.get(day.date) ?? 0,\n issueKeys: [],\n }));\n }, [worklogs.data, selected]);\n\n // Notices are transient; clearing on a timer keeps the status bar honest.\n useEffect(() => {\n if (!notice) return;\n const timer = setTimeout(() => {\n setNotice(null);\n }, NOTICE_TIMEOUT_MS);\n return () => {\n clearTimeout(timer);\n };\n }, [notice]);\n\n const openSelected = useCallback(() => {\n if (!selected) return;\n openUrl(selected.url)\n .then(() => {\n setNotice({ text: `opened ${selected.key} in your browser`, error: false });\n })\n .catch((cause: unknown) => {\n setNotice({\n text: cause instanceof Error ? cause.message : String(cause),\n error: true,\n });\n });\n }, [selected]);\n\n // Home/End jump to the ends of whichever panel has focus.\n const goToStart = useCallback(() => {\n switch (focus) {\n case 'issues':\n selectIndex(0);\n break;\n case 'worklog':\n setWorklogOffset(0);\n break;\n case 'preview':\n setPreviewOffset(0);\n break;\n }\n }, [focus, selectIndex]);\n\n const goToEnd = useCallback(() => {\n switch (focus) {\n case 'issues':\n selectIndex(issueList.length - 1);\n break;\n case 'worklog':\n setWorklogOffset(Math.max(0, (worklogs.data?.days.length ?? 0) - worklogRows));\n break;\n case 'preview':\n // The description's total line count lives inside the preview, so there\n // is no \"last line\" to jump to from here.\n break;\n }\n }, [focus, issueList.length, selectIndex, worklogs.data?.days.length, worklogRows]);\n\n useHomeEndKeys(goToStart, goToEnd);\n\n // ---- mouse --------------------------------------------------------------\n const layout = useRef({ leftWidth, topHeight, issueRows, issueOffset });\n layout.current = { leftWidth, topHeight, issueRows, issueOffset };\n\n const onMouse = useCallback(\n (event: MouseEvent) => {\n const { leftWidth: left, topHeight: top, issueRows: visible, issueOffset: offset } =\n layout.current;\n\n const panel: Panel =\n event.row >= top ? 'preview' : event.column >= left ? 'issues' : 'worklog';\n\n if (event.type === 'wheel') {\n setFocus(panel);\n scrollPanel(panel, event.direction === 'up' ? -1 : 1);\n return;\n }\n\n setFocus(panel);\n if (panel !== 'issues') return;\n\n const row = event.row - PANEL_HEADER_ROWS;\n if (row >= 0 && row < visible) selectIndex(offset + row);\n },\n [scrollPanel, selectIndex],\n );\n\n useMouse(onMouse);\n\n // ---- keyboard -----------------------------------------------------------\n useInput((input, key) => {\n if (isMouseSequence(input)) return; // Ink also sees the raw mouse bytes.\n\n if (key.ctrl && input === 'c') {\n exit();\n return;\n }\n if (input === 'q' || key.escape) {\n exit();\n return;\n }\n if (input === 'r') {\n setReloadToken((token) => token + 1);\n return;\n }\n if (key.return && focus === 'issues') {\n openSelected();\n return;\n }\n if (key.tab) {\n const step = key.shift ? -1 : 1;\n setFocus((current) => {\n const next = (PANEL_ORDER.indexOf(current) + step + PANEL_ORDER.length) % PANEL_ORDER.length;\n return PANEL_ORDER[next] ?? 'issues';\n });\n return;\n }\n\n // A held key arrives as one chunk (\"jjj\"), so count the repeats.\n if (key.downArrow) scrollPanel(focus, 1);\n else if (key.upArrow) scrollPanel(focus, -1);\n else if (key.pageDown) scrollPanel(focus, issueRows);\n else if (key.pageUp) scrollPanel(focus, -issueRows);\n else if (/^j+$/.test(input)) scrollPanel(focus, input.length);\n else if (/^k+$/.test(input)) scrollPanel(focus, -input.length);\n else if (input === 'g') scrollPanel(focus, -Number.MAX_SAFE_INTEGER);\n else if (input === 'G') selectIndex(issueList.length - 1);\n });\n\n const hint = useMemo(\n () =>\n focus === 'issues'\n ? '\u2191\u2193/jk move \u00B7 home/end first/last \u00B7 enter open \u00B7 tab panel \u00B7 r reload \u00B7 q quit'\n : '\u2191\u2193/jk scroll \u00B7 home/end top/bottom \u00B7 tab panel \u00B7 r reload \u00B7 q quit',\n [focus],\n );\n\n return (\n <Box flexDirection=\"column\" width={columns} height={rows}>\n <Box flexDirection=\"row\" height={topHeight} flexShrink={0}>\n <WorklogPanel\n state={worklogs}\n focused={focus === 'worklog'}\n height={topHeight}\n width={leftWidth}\n offset={worklogOffset}\n spinnerFrame={spinnerFrame}\n />\n <IssueList\n state={issues}\n focused={focus === 'issues'}\n height={topHeight}\n width={columns - leftWidth}\n selectedIndex={selectedIndex}\n offset={issueOffset}\n spinnerFrame={spinnerFrame}\n />\n </Box>\n\n <Box flexDirection=\"row\" height={bottomHeight} flexShrink={0}>\n <WorkChartPanel\n days={chartDays}\n issueKey={selected?.key ?? null}\n state={worklogs}\n focused={false}\n height={bottomHeight}\n width={chartWidth}\n spinnerFrame={spinnerFrame}\n />\n <IssuePreview\n issue={selected}\n worklog={(selected && worklogs.data?.byIssue[selected.key]) || []}\n worklogWindowDays={worklogs.data?.windowDays ?? config.worklogDays}\n focused={focus === 'preview'}\n height={bottomHeight}\n width={columns - chartWidth}\n offset={previewOffset}\n />\n </Box>\n\n <Box height={1} flexShrink={0} justifyContent=\"space-between\">\n <Text color=\"gray\" wrap=\"truncate-end\">\n {' '}\n {hint}\n </Text>\n {notice ? (\n <Text color={notice.error ? 'red' : 'green'} wrap=\"truncate-end\">\n {notice.error ? '\u2716' : '\u2713'} {notice.text}{' '}\n </Text>\n ) : loading ? (\n <Text color=\"cyan\" wrap=\"truncate-end\">\n {spinnerChar(spinnerFrame)} loading\u2026{' '}\n </Text>\n ) : (\n <Text color=\"gray\" wrap=\"truncate-end\">\n {me.displayName}{' '}\n </Text>\n )}\n </Box>\n </Box>\n );\n}\n", "import { Box, Text } from 'ink';\nimport type { ReactElement } from 'react';\n\nimport { PANEL_CHROME_ROWS, PanelFrame } from './PanelFrame';\nimport { PanelError, Spinner, spinnerChar } from './PanelStatus';\nimport type { Issue, Loadable } from '../types';\n\ninterface IssueListProps {\n state: Loadable<Issue[]>;\n focused: boolean;\n height: number;\n width: number;\n selectedIndex: number;\n offset: number;\n spinnerFrame: number;\n}\n\n/** Status colouring keyed on Jira's status category, which is stable per site. */\nfunction statusColor(category: string): string {\n switch (category.toLowerCase()) {\n case 'in progress':\n return 'yellow';\n case 'done':\n return 'green';\n default:\n return 'blue';\n }\n}\n\nconst KEY_WIDTH = 10;\nconst STATUS_WIDTH = 14;\n\nexport function IssueList({\n state,\n focused,\n height,\n width,\n selectedIndex,\n offset,\n spinnerFrame,\n}: IssueListProps): ReactElement {\n const issues = state.data ?? [];\n const visibleRows = Math.max(1, height - PANEL_CHROME_ROWS);\n const summaryWidth = Math.max(8, width - 4 - 2 - KEY_WIDTH - STATUS_WIDTH);\n\n const base =\n issues.length > visibleRows\n ? `my open tickets (${selectedIndex + 1}/${issues.length})`\n : `my open tickets (${issues.length})`;\n // Reloading keeps the old rows on screen, so the spinner moves into the title.\n const title = state.loading && issues.length > 0 ? `${spinnerChar(spinnerFrame)} ${base}` : base;\n\n return (\n <PanelFrame title={title} focused={focused} height={height} flexGrow={1}>\n {state.loading && issues.length === 0 ? (\n <Spinner label=\"loading tickets\u2026\" frame={spinnerFrame} />\n ) : state.error ? (\n <PanelError error={state.error} />\n ) : issues.length === 0 ? (\n <Text color=\"gray\">No open tickets matched the query.</Text>\n ) : (\n <Box flexDirection=\"column\">\n {issues.slice(offset, offset + visibleRows).map((issue, index) => {\n const selected = offset + index === selectedIndex;\n return (\n <Box key={issue.key}>\n <Box width={2} flexShrink={0}>\n <Text color=\"cyan\">{selected ? '\u25B8 ' : ' '}</Text>\n </Box>\n <Box width={KEY_WIDTH} flexShrink={0}>\n <Text bold={selected} color={selected ? 'cyan' : 'white'} wrap=\"truncate-end\">\n {issue.key}\n </Text>\n </Box>\n <Box width={STATUS_WIDTH} flexShrink={0}>\n <Text color={statusColor(issue.statusCategory)} wrap=\"truncate-end\">\n {issue.status}\n </Text>\n </Box>\n <Box width={summaryWidth} flexShrink={1}>\n <Text\n color={selected ? 'white' : 'gray'}\n bold={selected}\n inverse={selected && focused}\n wrap=\"truncate-end\"\n >\n {issue.summary}\n </Text>\n </Box>\n </Box>\n );\n })}\n </Box>\n )}\n </PanelFrame>\n );\n}\n", "import { Box, Text } from 'ink';\nimport type { ReactElement, ReactNode } from 'react';\n\ninterface PanelFrameProps {\n title: string;\n focused: boolean;\n height: number;\n width?: number | string;\n flexGrow?: number;\n children: ReactNode;\n}\n\n/**\n * Bordered panel with a one-line header.\n *\n * The border and header occupy fixed rows, so callers can map a screen row to\n * their own content: content starts at the panel's top + {@link PANEL_HEADER_ROWS}.\n */\nexport const PANEL_HEADER_ROWS = 2; // top border + header line\nexport const PANEL_CHROME_ROWS = 3; // ...plus the bottom border\n\nexport function PanelFrame({\n title,\n focused,\n height,\n width,\n flexGrow,\n children,\n}: PanelFrameProps): ReactElement {\n return (\n <Box\n flexDirection=\"column\"\n height={height}\n width={width}\n flexGrow={flexGrow}\n // A panel given an explicit width must keep it: shrinking one column makes\n // fixed-width content (chart rows, aligned labels) wrap and fall apart.\n flexShrink={width === undefined ? 1 : 0}\n borderStyle=\"round\"\n borderColor={focused ? 'cyan' : 'gray'}\n paddingX={1}\n overflow=\"hidden\"\n >\n <Text bold color={focused ? 'cyan' : 'white'}>\n {title}\n </Text>\n {children}\n </Box>\n );\n}\n", "import { Box, Text } from 'ink';\nimport type { ReactElement } from 'react';\n\nimport type { JiraError } from '../types';\n\n/** Spinner frames chosen to render on any terminal that supports Braille. */\nconst FRAMES = ['\u280B', '\u2819', '\u2839', '\u2838', '\u283C', '\u2834', '\u2826', '\u2827', '\u2807', '\u280F'];\n\n/** Current spinner glyph, for inlining into a panel title or status bar. */\nexport function spinnerChar(frame: number): string {\n return FRAMES[frame % FRAMES.length] ?? FRAMES[0] ?? '\u00B7';\n}\n\nexport function Spinner({ label, frame }: { label: string; frame: number }): ReactElement {\n return (\n <Text color=\"cyan\">\n {spinnerChar(frame)} <Text color=\"gray\">{label}</Text>\n </Text>\n );\n}\n\n/** Panel-local error: one panel failing must not blank the other. */\nexport function PanelError({ error }: { error: JiraError }): ReactElement {\n return (\n <Box flexDirection=\"column\">\n <Text color=\"red\" wrap=\"truncate-end\">\n \u2716 {error.message}\n </Text>\n {error.hints.slice(0, 3).map((hint, index) => (\n <Text key={index} color=\"gray\" wrap=\"truncate-end\">\n {hint}\n </Text>\n ))}\n <Text color=\"gray\">Press r to retry.</Text>\n </Box>\n );\n}\n", "import { Box, Text } from 'ink';\nimport type { ReactElement } from 'react';\n\nimport { PANEL_CHROME_ROWS, PanelFrame } from './PanelFrame';\nimport type { Issue, IssueWorklogEntry } from '../types';\n\ninterface IssuePreviewProps {\n issue: Issue | null;\n /** The current user's worklog entries for this issue, within the window. */\n worklog: IssueWorklogEntry[];\n /** How many days that worklog window covers, for labelling. */\n worklogWindowDays: number;\n focused: boolean;\n height: number;\n width: number;\n offset: number;\n}\n\nconst MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];\nconst FIELD_LABEL_WIDTH = 11;\n\nfunction formatUpdated(value: string | null): string {\n if (!value) return 'unknown';\n const date = new Date(value);\n if (Number.isNaN(date.getTime())) return value;\n return date.toLocaleString();\n}\n\n/** `28 Jul` from a `YYYY-MM-DD` key. */\nfunction formatDay(key: string): string {\n const [, month = '01', day = '01'] = key.split('-');\n return `${day} ${MONTHS[Number(month) - 1] ?? month}`;\n}\n\n/** `7.5h`, or `45m` for sub-hour amounts where decimals read badly. */\nfunction formatHours(hours: number): string {\n if (hours === 0) return '0h';\n return hours < 1 ? `${Math.round(hours * 60)}m` : `${hours.toFixed(1)}h`;\n}\n\n/**\n * Parent chain, most general first: project \u2192 parent \u2192 this issue.\n *\n * Jira's `parent` field only exposes one level, so a subtask shows its story but\n * not the story's epic.\n */\nfunction breadcrumbs(issue: Issue): string {\n const parts = [issue.projectName, issue.parent && `${issue.parent.key} ${issue.parent.summary}`]\n .filter((part): part is string => Boolean(part))\n .map((part) => part.trim());\n return [...parts, issue.key].join(' \u203A ');\n}\n\n/** Hard-wrap description text so we can window it by line for scrolling. */\nfunction wrapLines(text: string, width: number): string[] {\n const out: string[] = [];\n for (const line of text.split('\\n')) {\n if (line.length <= width) {\n out.push(line);\n continue;\n }\n let rest = line;\n while (rest.length > width) {\n const cut = rest.lastIndexOf(' ', width);\n const at = cut > width / 2 ? cut : width;\n out.push(rest.slice(0, at));\n rest = rest.slice(at).trimStart();\n }\n out.push(rest);\n }\n return out;\n}\n\nfunction Field({ label, value, color }: { label: string; value: string; color?: string }): ReactElement {\n return (\n <Box>\n <Box width={FIELD_LABEL_WIDTH} flexShrink={0}>\n <Text color=\"gray\">{label}</Text>\n </Box>\n <Text color={color} wrap=\"truncate-end\">\n {value}\n </Text>\n </Box>\n );\n}\n\nexport function IssuePreview({\n issue,\n worklog,\n worklogWindowDays,\n focused,\n height,\n width,\n offset,\n}: IssuePreviewProps): ReactElement {\n if (!issue) {\n return (\n <PanelFrame title=\"preview\" focused={focused} height={height} flexGrow={1}>\n <Text color=\"gray\">Select a ticket above to preview it.</Text>\n </PanelFrame>\n );\n }\n\n const loggedHours = worklog.reduce((sum, entry) => sum + entry.hours, 0);\n const loggedDates = worklog.map((entry) => `${formatDay(entry.date)} (${formatHours(entry.hours)})`);\n const myWork =\n worklog.length > 0\n ? `${formatHours(loggedHours)} \u00B7 ${loggedDates.join(', ')}`\n : `nothing in the last ${worklogWindowDays} days`;\n\n const total =\n issue.totalTimeSpentSeconds !== null && issue.totalTimeSpentSeconds > 0\n ? `${formatHours(issue.totalTimeSpentSeconds / 3600)} logged by everyone`\n : null;\n\n // Breadcrumbs + summary + type/status/sprint/assignee/reporter/priority/labels/\n // your work/updated. Keep in step with the fields rendered below.\n const fieldRows = 11 + (total ? 1 : 0);\n const lines = wrapLines(issue.description || '(no description)', Math.max(20, width - 4));\n\n // Fields are fixed-height; the description gets the rest, minus one row for the\n // \"more lines\" indicator whenever it will be shown. Overshooting here makes Ink\n // clip the panel, which silently eats the title row.\n const available = Math.max(1, height - PANEL_CHROME_ROWS - fieldRows);\n\n // Clamp so scrolling past the end keeps the last screenful visible instead of\n // sliding into blank space.\n const start = Math.min(offset, Math.max(0, lines.length - available));\n const bodyRows = lines.length - start > available ? Math.max(1, available - 1) : available;\n\n const visible = lines.slice(start, start + bodyRows);\n const more = lines.length - start - visible.length;\n\n return (\n <PanelFrame\n title={`${issue.key} \u00B7 ${issue.url}`}\n focused={focused}\n height={height}\n flexGrow={1}\n >\n <Text color=\"gray\" wrap=\"truncate-end\">\n {breadcrumbs(issue)}\n </Text>\n <Text bold wrap=\"truncate-end\">\n {issue.summary}\n </Text>\n <Field label=\"type\" value={issue.issueType || '\u2014'} />\n <Field label=\"status\" value={issue.status} />\n <Field label=\"sprint\" value={issue.sprints.length > 0 ? issue.sprints.join(', ') : 'None'} />\n <Field label=\"assignee\" value={issue.assignee ?? 'Unassigned'} />\n <Field label=\"reporter\" value={issue.reporter ?? '\u2014'} />\n <Field label=\"priority\" value={issue.priority ?? '\u2014'} />\n <Field label=\"labels\" value={issue.labels.length > 0 ? issue.labels.join(', ') : '\u2014'} />\n <Field\n label=\"your work\"\n value={myWork}\n color={worklog.length > 0 ? 'green' : undefined}\n />\n {total && <Field label=\"total work\" value={total} />}\n <Field label=\"updated\" value={formatUpdated(issue.updated)} />\n\n <Box flexDirection=\"column\">\n {visible.map((line, index) => (\n <Text key={start + index} color=\"white\" wrap=\"truncate-end\">\n {line || ' '}\n </Text>\n ))}\n {more > 0 && <Text color=\"gray\">\u2026 {more} more lines (\u2193 to scroll)</Text>}\n </Box>\n </PanelFrame>\n );\n}\n", "import { Box, Text } from 'ink';\nimport type { ReactElement } from 'react';\n\nimport { PANEL_CHROME_ROWS, PanelFrame } from './PanelFrame';\nimport { PanelError, Spinner } from './PanelStatus';\nimport type { Loadable, WorklogDay, WorklogSummary } from '../types';\n\ninterface WorkChartPanelProps {\n /** One entry per day in the window, oldest first, already scoped to an issue. */\n days: WorklogDay[];\n /** The issue the bars belong to; `null` when nothing is selected. */\n issueKey: string | null;\n /** Loading/error state of the worklog fetch the days come from. */\n state: Loadable<WorklogSummary>;\n focused: boolean;\n height: number;\n width: number;\n spinnerFrame: number;\n}\n\n/** One full row of bar equals this many hours; half a row equals half of it. */\nexport const HOURS_PER_BAR = 4;\n\n/** Rows reserved at the bottom for the day labels. */\nconst LABEL_ROWS = 1;\n\n/** A full working day is two rows, so never draw a shorter plot than that. */\nconst MIN_BAR_ROWS = 2;\n\nconst FULL_BLOCK = '\u2588';\nconst HALF_BLOCK = '\u2584';\n/** Anything logged but under half a bar, so a short day still reads as worked. */\nconst TRACE_BLOCK = '\u2581';\n\n/** Borders (2) + horizontal padding (2), plus one spare column of slack. */\nconst PANEL_SIDE_COLUMNS = 5;\n\nconst MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];\n\n/**\n * Width the chart needs for `dayCount` bars at the given bar width, including a\n * one-column gap between bars. A row that overflows by even one column wraps and\n * destroys the chart, so this has to be exact.\n */\nexport function chartWidthFor(dayCount: number, barWidth: number): number {\n const bars = dayCount * barWidth + Math.max(0, dayCount - 1);\n return bars + PANEL_SIDE_COLUMNS;\n}\n\n/**\n * Vertical bar chart of hours logged per day on one issue.\n *\n * The scale is fixed rather than fitted to the data, so a bar height means the\n * same thing between issues and between reloads: 2h is half a row, 4h one row,\n * 8h two rows.\n */\nexport function WorkChartPanel({\n days,\n issueKey,\n state,\n focused,\n height,\n width,\n spinnerFrame,\n}: WorkChartPanelProps): ReactElement {\n if (state.loading && days.length === 0) {\n return (\n <PanelFrame title=\"logged work\" focused={focused} height={height} width={width}>\n <Spinner label=\"loading\u2026\" frame={spinnerFrame} />\n </PanelFrame>\n );\n }\n if (state.error) {\n return (\n <PanelFrame title=\"logged work\" focused={focused} height={height} width={width}>\n <PanelError error={state.error} />\n </PanelFrame>\n );\n }\n if (!issueKey || days.length === 0) {\n return (\n <PanelFrame title=\"logged work\" focused={focused} height={height} width={width}>\n <Text color=\"gray\">Select a ticket to see your work on it.</Text>\n </PanelFrame>\n );\n }\n\n // Two columns per bar reads better, but fall back to one when space is tight.\n const barWidth = chartWidthFor(days.length, 2) <= width ? 2 : 1;\n\n const tallest = days.reduce((max, day) => Math.max(max, day.hours), 0);\n const roomForRows = Math.max(1, height - PANEL_CHROME_ROWS - LABEL_ROWS);\n const barRows = Math.min(roomForRows, Math.max(MIN_BAR_ROWS, Math.ceil(tallest / HOURS_PER_BAR)));\n\n // Drawn top-down, so the first row is the highest and the last is the baseline.\n const rows = Array.from({ length: barRows }, (_, index) => barRows - 1 - index);\n\n const total = days.reduce((sum, day) => sum + day.hours, 0);\n const clipped = tallest / HOURS_PER_BAR > barRows;\n const title =\n `${issueKey} \u00B7 ${total.toFixed(1)}h over ${days.length}d \u00B7 ${HOURS_PER_BAR}h = 1 bar` +\n (clipped ? ` \u00B7 peak ${tallest.toFixed(1)}h` : '');\n\n return (\n <PanelFrame title={title} focused={focused} height={height} width={width}>\n {/* Bars grow upward from a baseline pinned to the bottom of the panel. */}\n <Box flexDirection=\"column\" flexGrow={1} justifyContent=\"flex-end\">\n {/* An all-blank plot reads as broken, so say so instead. */}\n {total === 0 && <Text color=\"gray\">no time logged in this window</Text>}\n {total > 0 &&\n rows.map((rowFromBottom) => (\n <Text key={rowFromBottom}>\n {days.map((day, index) => (\n <Text key={day.date}>\n {index > 0 ? ' ' : ''}\n <Text color={barColor(day)}>\n {glyphFor(day.hours, rowFromBottom).repeat(barWidth)}\n </Text>\n </Text>\n ))}\n </Text>\n ))}\n\n {barWidth >= 2 ? (\n <Text>\n {days.map((day, index) => (\n <Text key={day.date}>\n {index > 0 ? ' ' : ''}\n <Text color={isToday(day.date) ? 'cyan' : 'gray'} bold={isToday(day.date)}>\n {dayOfMonth(day.date)}\n </Text>\n </Text>\n ))}\n </Text>\n ) : (\n // One column per bar leaves no room for a label per day; a date range\n // reads better than a row of single digits.\n <Text color=\"gray\" wrap=\"truncate-end\">\n {rangeLabel(days)}\n </Text>\n )}\n </Box>\n </PanelFrame>\n );\n}\n\n/**\n * Which block character a bar shows on a given row.\n *\n * `rowFromBottom` 0 is the baseline. A row is a full block once the day's hours\n * reach its top and a half block once they reach its middle, so 2h renders as a\n * half block sitting on the baseline. Below that, any logged time still gets a\n * thin mark \u2014 an hour of work must not look the same as none.\n */\nfunction glyphFor(hours: number, rowFromBottom: number): string {\n const rowsFilled = hours / HOURS_PER_BAR - rowFromBottom;\n if (rowsFilled >= 1) return FULL_BLOCK;\n if (rowsFilled >= 0.5) return HALF_BLOCK;\n if (rowsFilled > 0) return TRACE_BLOCK;\n return ' ';\n}\n\n/** Weekend work is tinted differently so an empty Saturday reads as expected. */\nfunction barColor(day: WorklogDay): string {\n if (day.hours === 0) return 'gray';\n return isWeekend(day.date) ? 'blue' : 'green';\n}\n\nfunction parseDate(key: string): Date {\n const [year = '1970', month = '01', day = '01'] = key.split('-');\n return new Date(Number(year), Number(month) - 1, Number(day));\n}\n\nfunction isWeekend(key: string): boolean {\n const weekday = parseDate(key).getDay();\n return weekday === 0 || weekday === 6;\n}\n\nfunction isToday(key: string): boolean {\n return parseDate(key).toDateString() === new Date().toDateString();\n}\n\n/** Two-digit day of month, so labels line up under their two-column bars. */\nfunction dayOfMonth(key: string): string {\n const [, , day = '01'] = key.split('-');\n return day.padStart(2, '0');\n}\n\n/** `16 Jul \u2192 29 Jul`, for when per-bar labels do not fit. */\nfunction rangeLabel(days: WorklogDay[]): string {\n const format = (key: string): string => {\n const [, month = '01', day = '01'] = key.split('-');\n return `${day} ${MONTHS[Number(month) - 1] ?? month}`;\n };\n const first = days[0]?.date;\n const last = days[days.length - 1]?.date;\n return first && last ? `${format(first)} \u2192 ${format(last)}` : '';\n}\n", "import { Box, Text } from 'ink';\nimport type { ReactElement } from 'react';\n\nimport { PANEL_CHROME_ROWS, PanelFrame } from './PanelFrame';\nimport { PanelError, Spinner, spinnerChar } from './PanelStatus';\nimport type { Loadable, WorklogSummary } from '../types';\n\ninterface WorklogPanelProps {\n state: Loadable<WorklogSummary>;\n focused: boolean;\n height: number;\n width: number;\n offset: number;\n spinnerFrame: number;\n}\n\nconst WEEKDAYS = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];\nconst MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];\n\n/** `Mon 28 Jul` from a `YYYY-MM-DD` key, parsed as a local date. */\nfunction formatDate(key: string): string {\n const [year = '1970', month = '01', day = '01'] = key.split('-');\n const date = new Date(Number(year), Number(month) - 1, Number(day));\n return `${WEEKDAYS[date.getDay()]} ${day} ${MONTHS[date.getMonth()]}`;\n}\n\nfunction isWeekend(key: string): boolean {\n const [year = '1970', month = '01', day = '01'] = key.split('-');\n const weekday = new Date(Number(year), Number(month) - 1, Number(day)).getDay();\n return weekday === 0 || weekday === 6;\n}\n\n/** Colour hours by how close the day is to a full working day. */\nfunction hoursColor(hours: number, weekend: boolean): string {\n if (hours === 0) return weekend ? 'gray' : 'red';\n if (hours < 6) return 'yellow';\n return 'green';\n}\n\nexport function WorklogPanel({\n state,\n focused,\n height,\n width,\n offset,\n spinnerFrame,\n}: WorklogPanelProps): ReactElement {\n const days = state.data?.days ?? [];\n const visibleRows = Math.max(1, height - PANEL_CHROME_ROWS);\n const total = days.reduce((sum, day) => sum + day.hours, 0);\n // Panel width minus borders, padding, date column and hours column.\n const keysWidth = Math.max(6, width - 4 - 12 - 7);\n\n const base =\n days.length > 0\n ? `last ${days.length} days \u00B7 ${total.toFixed(1)}h total`\n : `last ${days.length || 14} days`;\n // Reloading keeps the old rows on screen, so the spinner moves into the title.\n const title = state.loading && days.length > 0 ? `${spinnerChar(spinnerFrame)} ${base}` : base;\n\n return (\n <PanelFrame title={title} focused={focused} height={height} width={width}>\n {state.loading && days.length === 0 ? (\n <Spinner label=\"loading worklogs\u2026\" frame={spinnerFrame} />\n ) : state.error ? (\n <PanelError error={state.error} />\n ) : (\n <Box flexDirection=\"column\">\n {days.slice(offset, offset + visibleRows).map((day) => {\n const weekend = isWeekend(day.date);\n const keys = day.issueKeys.length > 0 ? day.issueKeys.join(', ') : '\u2014';\n return (\n <Box key={day.date}>\n <Box width={12} flexShrink={0}>\n <Text color={weekend ? 'gray' : 'white'}>{formatDate(day.date)}</Text>\n </Box>\n <Box width={7} flexShrink={0}>\n <Text color={hoursColor(day.hours, weekend)}>\n {`${day.hours.toFixed(1)}h`.padStart(6)}\n </Text>\n </Box>\n <Box width={keysWidth} flexShrink={1}>\n <Text color=\"gray\" wrap=\"truncate-end\">\n {keys}\n </Text>\n </Box>\n </Box>\n );\n })}\n </Box>\n )}\n </PanelFrame>\n );\n}\n", "/**\n * Minimal Atlassian Document Format \u2192 plain text renderer.\n *\n * Jira returns rich descriptions as an ADF node tree. We only need something\n * readable in a terminal pane, so unknown node types simply recurse into their\n * children rather than failing.\n */\n\ninterface AdfNode {\n type?: string;\n text?: string;\n content?: AdfNode[];\n attrs?: Record<string, unknown>;\n marks?: { type?: string; attrs?: Record<string, unknown> }[];\n}\n\nexport function adfToText(doc: unknown): string {\n if (typeof doc === 'string') return doc;\n if (typeof doc !== 'object' || doc === null) return '';\n return renderNodes((doc as AdfNode).content ?? [], 0)\n .join('\\n')\n .replace(/\\n{3,}/g, '\\n\\n')\n .trim();\n}\n\nfunction renderNodes(nodes: AdfNode[], depth: number): string[] {\n return nodes.flatMap((node) => renderNode(node, depth));\n}\n\nfunction renderNode(node: AdfNode, depth: number): string[] {\n const indent = ' '.repeat(depth);\n\n switch (node.type) {\n case 'text':\n return [applyMarks(node)];\n\n case 'hardBreak':\n return [''];\n\n case 'paragraph':\n return [indent + inline(node.content ?? []), ''];\n\n case 'heading': {\n const level = Number(node.attrs?.['level'] ?? 1);\n return [`${'#'.repeat(Math.min(level, 6))} ${inline(node.content ?? [])}`, ''];\n }\n\n case 'bulletList':\n return [...listItems(node, depth, () => '\u2022'), ''];\n\n case 'orderedList':\n return [...listItems(node, depth, (i) => `${i + 1}.`), ''];\n\n case 'codeBlock': {\n const lang = typeof node.attrs?.['language'] === 'string' ? node.attrs['language'] : '';\n const body = inline(node.content ?? []).split('\\n');\n return [`${indent}\\`\\`\\`${lang}`, ...body.map((l) => indent + l), `${indent}\\`\\`\\``, ''];\n }\n\n case 'blockquote':\n return [...renderNodes(node.content ?? [], depth).map((l) => `${indent}> ${l}`), ''];\n\n case 'rule':\n return ['\u2500\u2500\u2500', ''];\n\n case 'mediaSingle':\n case 'mediaGroup':\n return [`${indent}[attachment]`];\n\n case 'table':\n // Rendering real table borders is out of scope; flatten rows instead.\n return [...renderNodes(node.content ?? [], depth), ''];\n\n case 'tableRow':\n return [indent + (node.content ?? []).map((cell) => inline(cell.content ?? [])).join(' | ')];\n\n default:\n return node.content ? renderNodes(node.content, depth) : [];\n }\n}\n\nfunction listItems(node: AdfNode, depth: number, bullet: (index: number) => string): string[] {\n const indent = ' '.repeat(depth);\n return (node.content ?? []).flatMap((item, index) => {\n const lines = renderNodes(item.content ?? [], depth + 1).filter((l) => l.trim() !== '');\n const [first = '', ...rest] = lines;\n return [`${indent}${bullet(index)} ${first.trim()}`, ...rest];\n });\n}\n\n/** Flatten a run of inline nodes onto a single line. */\nfunction inline(nodes: AdfNode[]): string {\n return nodes\n .map((node) => {\n switch (node.type) {\n case 'text':\n return applyMarks(node);\n case 'hardBreak':\n return '\\n';\n case 'mention':\n return attr(node, 'text') || '@unknown';\n case 'emoji':\n return attr(node, 'text') || attr(node, 'shortName');\n case 'inlineCard':\n return attr(node, 'url');\n case 'date':\n return attr(node, 'timestamp');\n case 'status':\n return `[${attr(node, 'text')}]`;\n default:\n return node.content ? inline(node.content) : '';\n }\n })\n .join('');\n}\n\n/** Read a node attribute as a plain string; non-scalar attrs are dropped. */\nfunction attr(node: AdfNode, name: string): string {\n const value = node.attrs?.[name];\n return typeof value === 'string' || typeof value === 'number' ? String(value) : '';\n}\n\n/** Only links carry information that would be lost in plain text. */\nfunction applyMarks(node: AdfNode): string {\n const text = node.text ?? '';\n const link = node.marks?.find((m) => m.type === 'link');\n const href = link?.attrs?.['href'];\n return typeof href === 'string' && href !== text ? `${text} (${href})` : text;\n}\n", "import type { JiraClient } from './client';\n\n/** Jira's schema id for the Scrum board sprint field, stable across sites. */\nconst SPRINT_SCHEMA = 'com.pyxis.greenhopper.jira:gh-sprint';\n\ninterface FieldMeta {\n id?: string;\n schema?: { custom?: string };\n}\n\n/** `undefined` = not looked up yet, `null` = looked up and unavailable. */\nlet cached: string | null | undefined;\n\n/**\n * Resolve the site's sprint custom-field id (e.g. `customfield_10020`).\n *\n * The id differs per Jira site, so it has to be discovered rather than\n * hard-coded. Resolved once per process; a failure is not fatal \u2014 the preview\n * just shows \"None\" rather than breaking the whole ticket list.\n */\nexport async function resolveSprintFieldId(client: JiraClient): Promise<string | null> {\n if (cached !== undefined) return cached;\n\n try {\n const fields = await client.get<FieldMeta[]>('/rest/api/3/field');\n cached = fields.find((field) => field.schema?.custom === SPRINT_SCHEMA)?.id ?? null;\n } catch {\n cached = null;\n }\n\n return cached;\n}\n\ninterface RawSprint {\n name?: string;\n state?: string;\n}\n\n/**\n * Sprint names for display, active ones first.\n *\n * The field is an array because an issue can span sprints. Older Jira versions\n * return stringified Java objects instead of JSON, so both shapes are handled.\n */\nexport function sprintNames(value: unknown): string[] {\n if (!Array.isArray(value)) return [];\n\n const sprints = value\n .map((entry): RawSprint | null => {\n if (typeof entry === 'string') return parseLegacySprint(entry);\n if (typeof entry === 'object' && entry !== null) return entry as RawSprint;\n return null;\n })\n .filter((sprint): sprint is RawSprint => Boolean(sprint?.name));\n\n // Active first, then future, then closed \u2014 the order a person cares about.\n const rank = (state?: string): number => {\n switch (state?.toLowerCase()) {\n case 'active':\n return 0;\n case 'future':\n return 1;\n default:\n return 2;\n }\n };\n\n return sprints\n .sort((a, b) => rank(a.state) - rank(b.state))\n .map((sprint) =>\n sprint.state && sprint.state.toLowerCase() !== 'active'\n ? `${sprint.name ?? ''} (${sprint.state.toLowerCase()})`\n : (sprint.name ?? ''),\n );\n}\n\n/** `...Sprint@1a2b[id=5,name=Sprint 7,state=ACTIVE,...]` \u2192 `{ name, state }`. */\nfunction parseLegacySprint(raw: string): RawSprint | null {\n const name = /\\bname=([^,\\]]+)/.exec(raw)?.[1];\n const state = /\\bstate=([^,\\]]+)/.exec(raw)?.[1];\n return name ? { name, state } : null;\n}\n", "import type { JiraClient } from './client';\nimport { adfToText } from './adf';\nimport { resolveSprintFieldId, sprintNames } from './sprintField';\nimport type { Config, Issue } from '../types';\n\nconst MAX_ISSUES = 100;\n\nconst FIELDS = [\n 'summary',\n 'status',\n 'priority',\n 'assignee',\n 'reporter',\n 'labels',\n 'updated',\n 'description',\n 'issuetype',\n 'parent',\n 'project',\n 'aggregatetimespent',\n 'timespent',\n];\n\ninterface RawIssue {\n key: string;\n fields?: {\n summary?: string;\n status?: { name?: string; statusCategory?: { name?: string } };\n priority?: { name?: string } | null;\n assignee?: { displayName?: string } | null;\n reporter?: { displayName?: string } | null;\n labels?: string[];\n updated?: string;\n description?: unknown;\n issuetype?: { name?: string };\n project?: { name?: string; key?: string };\n parent?: {\n key?: string;\n fields?: { summary?: string; issuetype?: { name?: string } };\n } | null;\n aggregatetimespent?: number | null;\n timespent?: number | null;\n /** The sprint custom field, whose id varies per site. */\n [customField: string]: unknown;\n };\n}\n\nexport interface SearchResponse {\n issues?: RawIssue[];\n}\n\n/** Fetch the open tickets shown in the top-right panel. */\nexport async function fetchOpenIssues(client: JiraClient, config: Config): Promise<Issue[]> {\n const sprintFieldId = await resolveSprintFieldId(client);\n\n const response = await client.post<SearchResponse>('/rest/api/3/search/jql', {\n jql: config.issuesJql,\n maxResults: MAX_ISSUES,\n fields: sprintFieldId ? [...FIELDS, sprintFieldId] : FIELDS,\n });\n\n return (response.issues ?? []).map((raw) => toIssue(raw, client, sprintFieldId));\n}\n\nfunction toIssue(raw: RawIssue, client: JiraClient, sprintFieldId: string | null): Issue {\n const fields = raw.fields ?? {};\n return {\n key: raw.key,\n summary: fields.summary ?? '(no summary)',\n status: fields.status?.name ?? 'Unknown',\n statusCategory: fields.status?.statusCategory?.name ?? '',\n issueType: fields.issuetype?.name ?? '',\n priority: fields.priority?.name ?? null,\n assignee: fields.assignee?.displayName ?? null,\n reporter: fields.reporter?.displayName ?? null,\n labels: fields.labels ?? [],\n updated: fields.updated ?? null,\n description: adfToText(fields.description),\n url: client.issueUrl(raw.key),\n projectName: fields.project?.name ?? fields.project?.key ?? null,\n parent: fields.parent?.key\n ? {\n key: fields.parent.key,\n summary: fields.parent.fields?.summary ?? '',\n issueType: fields.parent.fields?.issuetype?.name ?? '',\n }\n : null,\n totalTimeSpentSeconds: fields.aggregatetimespent ?? fields.timespent ?? null,\n sprints: sprintFieldId ? sprintNames(fields[sprintFieldId]) : [],\n };\n}\n", "import type { JiraClient } from './client';\nimport type { Config, CurrentUser, IssueWorklogEntry, WorklogSummary } from '../types';\nimport type { SearchResponse } from './issues';\n\nconst WORKLOG_PAGE_SIZE = 1000;\nconst CONCURRENCY = 5;\n\ninterface RawWorklog {\n author?: { accountId?: string };\n started?: string;\n timeSpentSeconds?: number;\n}\n\ninterface WorklogResponse {\n worklogs?: RawWorklog[];\n}\n\n/** Local `YYYY-MM-DD` key \u2014 worklog days are calendar days in the user's timezone. */\nexport function localDateKey(date: Date): string {\n const month = String(date.getMonth() + 1).padStart(2, '0');\n const day = String(date.getDate()).padStart(2, '0');\n return `${date.getFullYear()}-${month}-${day}`;\n}\n\n/** Midnight local time, `daysAgo` days back from today. */\nfunction startOfDay(daysAgo: number): Date {\n const date = new Date();\n date.setHours(0, 0, 0, 0);\n date.setDate(date.getDate() - daysAgo);\n return date;\n}\n\n/**\n * Aggregate the current user's worklogs over the last `config.worklogDays` days\n * two ways: one row per day (oldest first, zero-hour days included so the panel\n * shows a complete window) and a per-issue breakdown for the preview panel.\n */\nexport async function fetchWorklogDays(\n client: JiraClient,\n config: Config,\n me: CurrentUser,\n): Promise<WorklogSummary> {\n const days = config.worklogDays;\n const windowStart = startOfDay(days - 1);\n\n const search = await client.post<SearchResponse>('/rest/api/3/search/jql', {\n jql: `worklogAuthor = currentUser() AND worklogDate >= -${days}d ORDER BY updated DESC`,\n maxResults: 200,\n fields: ['summary'],\n });\n\n const keys = (search.issues ?? []).map((issue) => issue.key);\n\n // Pre-seed every day in the window so gaps render as 0.0h.\n const buckets = new Map<string, { seconds: number; keys: Set<string> }>();\n for (let i = 0; i < days; i += 1) {\n buckets.set(localDateKey(startOfDay(i)), { seconds: 0, keys: new Set() });\n }\n\n // Seconds logged per issue per day: issue key \u2192 date \u2192 seconds.\n const perIssue = new Map<string, Map<string, number>>();\n\n const startedAfter = String(windowStart.getTime());\n await forEachLimited(keys, CONCURRENCY, async (key) => {\n const response = await client.get<WorklogResponse>(`/rest/api/3/issue/${key}/worklog`, {\n startedAfter,\n maxResults: String(WORKLOG_PAGE_SIZE),\n });\n\n for (const entry of response.worklogs ?? []) {\n if (entry.author?.accountId !== me.accountId || !entry.started) continue;\n\n const started = new Date(entry.started);\n if (Number.isNaN(started.getTime()) || started < windowStart) continue;\n\n const date = localDateKey(started);\n const bucket = buckets.get(date);\n if (!bucket) continue; // A worklog dated in the future.\n\n const seconds = entry.timeSpentSeconds ?? 0;\n bucket.seconds += seconds;\n bucket.keys.add(key);\n\n const issueDays = perIssue.get(key) ?? new Map<string, number>();\n issueDays.set(date, (issueDays.get(date) ?? 0) + seconds);\n perIssue.set(key, issueDays);\n }\n });\n\n const byIssue: Record<string, IssueWorklogEntry[]> = {};\n for (const [key, issueDays] of perIssue) {\n byIssue[key] = Array.from(issueDays.entries())\n .map(([date, seconds]) => ({ date, hours: seconds / 3600 }))\n .sort((a, b) => a.date.localeCompare(b.date));\n }\n\n return {\n days: Array.from(buckets.entries())\n .map(([date, { seconds, keys: issueKeys }]) => ({\n date,\n hours: seconds / 3600,\n issueKeys: Array.from(issueKeys).sort(compareIssueKeys),\n }))\n .sort((a, b) => a.date.localeCompare(b.date)),\n byIssue,\n windowDays: days,\n };\n}\n\n/** Sort by project then numeric part, so MR-2 precedes MR-10. */\nfunction compareIssueKeys(a: string, b: string): number {\n const [aProject = a, aNumber = '0'] = a.split('-');\n const [bProject = b, bNumber = '0'] = b.split('-');\n return aProject === bProject\n ? Number(aNumber) - Number(bNumber)\n : aProject.localeCompare(bProject);\n}\n\n/** Run `worker` over `items` with at most `limit` requests in flight. */\nasync function forEachLimited<T>(\n items: T[],\n limit: number,\n worker: (item: T) => Promise<void>,\n): Promise<void> {\n let cursor = 0;\n const runners = Array.from({ length: Math.min(limit, items.length) }, async () => {\n while (cursor < items.length) {\n const item = items[cursor];\n cursor += 1;\n if (item !== undefined) await worker(item);\n }\n });\n await Promise.all(runners);\n}\n", "import { useEffect, useRef } from 'react';\nimport { useStdin } from 'ink';\n\n/**\n * Built from a char code on purpose: a literal escape byte in source is\n * invisible and easy to lose to an editor or a copy-paste.\n */\nconst ESC = String.fromCharCode(27);\n\n/**\n * Home and End have no single encoding \u2014 xterm, VT and rxvt families each pick\n * a different one, and terminals vary in application vs normal cursor mode.\n */\nconst HOME_SEQUENCES = [`${ESC}[H`, `${ESC}[1~`, `${ESC}[7~`, `${ESC}OH`];\nconst END_SEQUENCES = [`${ESC}[F`, `${ESC}[4~`, `${ESC}[8~`, `${ESC}OF`];\n\n/**\n * Report Home and End key presses.\n *\n * Ink's `useInput` cannot help here: it parses all of these into an empty input\n * with no key flags set, making Home and End indistinguishable. Reading stdin\n * directly is the only way to tell them apart. Ink still sees the same bytes and\n * emits its own (harmless) empty events.\n */\nexport function useHomeEndKeys(onHome: () => void, onEnd: () => void): void {\n const { stdin, isRawModeSupported } = useStdin();\n\n // Handlers change on nearly every render; a ref keeps the listener stable.\n const handlers = useRef({ onHome, onEnd });\n handlers.current = { onHome, onEnd };\n\n useEffect(() => {\n if (!isRawModeSupported) return;\n\n const onData = (data: Buffer | string): void => {\n const text = typeof data === 'string' ? data : data.toString('utf8');\n if (HOME_SEQUENCES.some((sequence) => text.includes(sequence))) {\n handlers.current.onHome();\n } else if (END_SEQUENCES.some((sequence) => text.includes(sequence))) {\n handlers.current.onEnd();\n }\n };\n\n stdin.on('data', onData);\n return () => {\n stdin.off('data', onData);\n };\n }, [stdin, isRawModeSupported]);\n}\n", "import { useEffect } from 'react';\nimport { useStdin, useStdout } from 'ink';\n\n/**\n * Built from a char code on purpose: a literal escape byte in source is\n * invisible and easy to lose to an editor or a copy-paste.\n */\nconst ESC = String.fromCharCode(27);\n\n/** SGR mouse reporting: button presses + wheel, with unbounded coordinates. */\nconst MOUSE_ON = `${ESC}[?1000h${ESC}[?1006h`;\nconst MOUSE_OFF = `${ESC}[?1006l${ESC}[?1000l`;\n\n/** `ESC [ < button ; col ; row (M|m)` \u2014 M is press, m is release. */\nconst SGR_MOUSE = new RegExp(`${ESC}\\\\[<(\\\\d+);(\\\\d+);(\\\\d+)([Mm])`, 'g');\nconst SGR_PREFIX = `${ESC}[<`;\n\nexport type MouseEvent =\n | { type: 'press'; column: number; row: number }\n | { type: 'wheel'; direction: 'up' | 'down'; column: number; row: number };\n\n/**\n * True when `input` is (or contains) an SGR mouse report. Keyboard handlers\n * should ignore these, since Ink's key parser sees the same bytes.\n */\nexport function isMouseSequence(input: string): boolean {\n return input.includes(SGR_PREFIX);\n}\n\n/**\n * Enable SGR mouse reporting for the lifetime of the component and report\n * parsed events. Terminal state is restored on unmount and on abrupt exits.\n */\nexport function useMouse(onEvent: (event: MouseEvent) => void): void {\n const { stdin, isRawModeSupported } = useStdin();\n const { stdout } = useStdout();\n\n useEffect(() => {\n if (!isRawModeSupported) return;\n\n stdout.write(MOUSE_ON);\n const disable = (): void => {\n stdout.write(MOUSE_OFF);\n };\n // Ink's own cleanup does not know about mouse mode, so cover hard exits.\n process.on('exit', disable);\n\n const onData = (data: Buffer | string): void => {\n const text = typeof data === 'string' ? data : data.toString('utf8');\n SGR_MOUSE.lastIndex = 0;\n\n for (let match = SGR_MOUSE.exec(text); match; match = SGR_MOUSE.exec(text)) {\n const [, rawButton = '0', rawColumn = '1', rawRow = '1', kind] = match;\n if (kind !== 'M') continue; // Ignore releases.\n\n const button = Number(rawButton);\n const column = Number(rawColumn) - 1;\n const row = Number(rawRow) - 1;\n\n // Bit 6 (64) marks wheel events; the low bit gives the direction.\n if (button & 64) {\n onEvent({ type: 'wheel', direction: (button & 1) === 0 ? 'up' : 'down', column, row });\n } else if ((button & 3) === 0) {\n onEvent({ type: 'press', column, row });\n }\n }\n };\n\n stdin.on('data', onData);\n return () => {\n stdin.off('data', onData);\n process.off('exit', disable);\n disable();\n };\n }, [stdin, stdout, isRawModeSupported, onEvent]);\n}\n", "import { useEffect, useState } from 'react';\nimport { useStdout } from 'ink';\n\nexport interface TerminalSize {\n columns: number;\n rows: number;\n}\n\n/** Track the terminal dimensions so the layout fills the window on resize. */\nexport function useTerminalSize(): TerminalSize {\n const { stdout } = useStdout();\n const [size, setSize] = useState<TerminalSize>(() => ({\n columns: stdout.columns || 80,\n rows: stdout.rows || 24,\n }));\n\n useEffect(() => {\n const onResize = (): void => {\n setSize({ columns: stdout.columns || 80, rows: stdout.rows || 24 });\n };\n\n stdout.on('resize', onResize);\n onResize();\n return () => {\n stdout.off('resize', onResize);\n };\n }, [stdout]);\n\n return size;\n}\n", "import { spawn } from 'node:child_process';\n\n/** Platform command that hands a URL to the user's default browser. */\nfunction opener(): { command: string; args: string[] } {\n switch (process.platform) {\n case 'darwin':\n return { command: 'open', args: [] };\n case 'win32':\n // `start` is a cmd builtin; the empty string is the (ignored) window title.\n return { command: 'cmd', args: ['/c', 'start', ''] };\n default:\n return { command: 'xdg-open', args: [] };\n }\n}\n\n/**\n * Open `url` in the default browser.\n *\n * The child is detached and its streams are discarded, so a browser writing to\n * stderr cannot corrupt the TUI. Resolves once the process has been spawned;\n * rejects if the opener is missing or exits non-zero straight away.\n */\nexport async function openUrl(url: string): Promise<void> {\n const { command, args } = opener();\n\n return new Promise((resolve, reject) => {\n // Arguments are passed as an array, so the URL is never shell-interpreted.\n const child = spawn(command, [...args, url], {\n detached: true,\n stdio: 'ignore',\n });\n\n child.once('error', (cause: NodeJS.ErrnoException) => {\n reject(\n new Error(\n cause.code === 'ENOENT'\n ? `${command} is not installed, so the browser could not be opened.`\n : `${command} failed: ${cause.message}`,\n ),\n );\n });\n\n child.once('spawn', () => {\n child.unref(); // Let jdb exit without waiting for the browser.\n resolve();\n });\n });\n}\n", "export interface Config {\n baseUrl: string;\n email: string;\n apiToken: string;\n /** Overridable JQL for the open-tickets panel. */\n issuesJql: string;\n /** How many days of worklog history the left panel covers. */\n worklogDays: number;\n}\n\nexport interface Issue {\n key: string;\n summary: string;\n status: string;\n statusCategory: string;\n issueType: string;\n priority: string | null;\n assignee: string | null;\n reporter: string | null;\n labels: string[];\n updated: string | null;\n description: string;\n url: string;\n projectName: string | null;\n /** Immediate parent (epic for a story, story for a subtask), if any. */\n parent: { key: string; summary: string; issueType: string } | null;\n /** Total time logged on the issue by everyone, per Jira's aggregate. */\n totalTimeSpentSeconds: number | null;\n /** Sprint names the issue belongs to, active ones first; empty if none. */\n sprints: string[];\n}\n\nexport interface WorklogDay {\n /** Local calendar date, `YYYY-MM-DD`. */\n date: string;\n hours: number;\n issueKeys: string[];\n}\n\n/** One day's worth of the current user's work on a single issue. */\nexport interface IssueWorklogEntry {\n date: string;\n hours: number;\n}\n\nexport interface WorklogSummary {\n /** One row per day in the window, oldest first. */\n days: WorklogDay[];\n /** Per-issue breakdown within the same window, keyed by issue key. */\n byIssue: Record<string, IssueWorklogEntry[]>;\n /** How many days the window covers, for labelling. */\n windowDays: number;\n}\n\nexport interface CurrentUser {\n accountId: string;\n displayName: string;\n}\n\n/** An error carrying user-facing remediation advice. */\nexport class JiraError extends Error {\n readonly hints: string[];\n\n constructor(message: string, hints: string[] = []) {\n super(message);\n this.name = 'JiraError';\n this.hints = hints;\n }\n}\n\n/** Async resource with independent loading/error state per panel. */\nexport interface Loadable<T> {\n data: T | null;\n loading: boolean;\n error: JiraError | null;\n}\n", "import { JiraError, type Config, type CurrentUser } from '../types';\n\nconst REQUEST_TIMEOUT_MS = 20_000;\n\n/** Thin authenticated wrapper around the Jira Cloud REST API v3. */\nexport class JiraClient {\n private readonly baseUrl: string;\n private readonly authHeader: string;\n\n constructor(config: Config) {\n this.baseUrl = config.baseUrl;\n this.authHeader = `Basic ${Buffer.from(`${config.email}:${config.apiToken}`).toString('base64')}`;\n }\n\n /** Browser URL for an issue, for display in the preview panel. */\n issueUrl(key: string): string {\n return `${this.baseUrl}/browse/${key}`;\n }\n\n async get<T>(path: string, params?: Record<string, string>): Promise<T> {\n const url = new URL(`${this.baseUrl}${path}`);\n for (const [key, value] of Object.entries(params ?? {})) {\n url.searchParams.set(key, value);\n }\n return this.request<T>('GET', url.toString());\n }\n\n async post<T>(path: string, body: unknown): Promise<T> {\n return this.request<T>('POST', `${this.baseUrl}${path}`, body);\n }\n\n private async request<T>(method: string, url: string, body?: unknown): Promise<T> {\n let response: Response;\n try {\n response = await fetch(url, {\n method,\n headers: {\n Authorization: this.authHeader,\n Accept: 'application/json',\n ...(body === undefined ? {} : { 'Content-Type': 'application/json' }),\n },\n ...(body === undefined ? {} : { body: JSON.stringify(body) }),\n signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),\n });\n } catch (cause) {\n throw asNetworkError(cause, this.baseUrl);\n }\n\n if (!response.ok) {\n throw await asHttpError(response, url);\n }\n\n return (await response.json()) as T;\n }\n\n /** Identify the caller; doubles as the credential check at startup. */\n async myself(): Promise<CurrentUser> {\n const me = await this.get<{ accountId: string; displayName: string }>('/rest/api/3/myself');\n return { accountId: me.accountId, displayName: me.displayName };\n }\n}\n\nfunction asNetworkError(cause: unknown, baseUrl: string): JiraError {\n const name = cause instanceof Error ? cause.name : '';\n if (name === 'TimeoutError' || name === 'AbortError') {\n return new JiraError('Jira did not respond within 20s.', [\n 'Check your network connection or VPN, then press r to retry.',\n ]);\n }\n return new JiraError(`Could not reach ${baseUrl}.`, [\n cause instanceof Error ? cause.message : String(cause),\n 'Verify JIRA_BASE_URL points at your Atlassian site.',\n ]);\n}\n\nasync function asHttpError(response: Response, url: string): Promise<JiraError> {\n const detail = await readErrorDetail(response);\n\n switch (response.status) {\n case 401:\n return new JiraError('Jira rejected your credentials (401).', [\n 'JIRA_EMAIL must be your Atlassian account email and JIRA_API_TOKEN an API token',\n '(not your password). Tokens: https://id.atlassian.com/manage-profile/security/api-tokens',\n ]);\n case 403:\n return new JiraError('Jira denied access (403).', [\n detail || 'Your account may lack permission for this project.',\n ]);\n case 400:\n return new JiraError('Jira rejected the request (400).', [\n detail || 'The JQL query may be invalid for this site.',\n 'Adjust \"issuesJql\" in your config file.',\n ]);\n case 429:\n return new JiraError('Jira rate-limited this client (429).', [\n 'Wait a moment, then press r to retry.',\n ]);\n default:\n return new JiraError(`Jira returned ${response.status} for ${new URL(url).pathname}.`, [\n detail || response.statusText,\n ]);\n }\n}\n\n/** Jira reports failures as `errorMessages` and/or a field-keyed `errors` map. */\nasync function readErrorDetail(response: Response): Promise<string> {\n try {\n const body: unknown = await response.json();\n if (typeof body !== 'object' || body === null) return '';\n const { errorMessages, errors } = body as {\n errorMessages?: unknown;\n errors?: Record<string, unknown>;\n };\n\n const parts: string[] = [];\n if (Array.isArray(errorMessages)) parts.push(...errorMessages.map(String));\n if (errors && typeof errors === 'object') {\n parts.push(...Object.entries(errors).map(([k, v]) => `${k}: ${String(v)}`));\n }\n return parts.join('; ');\n } catch {\n return '';\n }\n}\n", "import { readFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nimport { JiraError, type Config } from '../types';\n\nexport const CONFIG_PATH = join(\n process.env['XDG_CONFIG_HOME'] ?? join(homedir(), '.config'),\n 'jira-dashboard',\n 'config.json',\n);\n\nconst TOKEN_URL = 'https://id.atlassian.com/manage-profile/security/api-tokens';\n\nexport const DEFAULT_ISSUES_JQL = [\n 'assignee = currentUser()',\n 'AND statusCategory != Done',\n 'AND status NOT IN (\"Cancelled\", \"Closed\", \"Backlog\")',\n 'ORDER BY updated DESC',\n].join(' ');\n\nconst DEFAULT_WORKLOG_DAYS = 14;\n\ninterface FileConfig {\n baseUrl?: string;\n email?: string;\n apiToken?: string;\n issuesJql?: string;\n worklogDays?: number;\n}\n\nfunction readConfigFile(): FileConfig {\n let raw: string;\n try {\n raw = readFileSync(CONFIG_PATH, 'utf8');\n } catch {\n return {};\n }\n\n try {\n const parsed: unknown = JSON.parse(raw);\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n throw new Error('expected a JSON object');\n }\n return parsed;\n } catch (cause) {\n throw new JiraError(`Config file at ${CONFIG_PATH} is not valid JSON.`, [\n cause instanceof Error ? cause.message : String(cause),\n 'Fix the file, or delete it and use environment variables instead.',\n ]);\n }\n}\n\n/** Strip a trailing slash so path joining stays predictable. */\nfunction normalizeBaseUrl(value: string): string {\n const trimmed = value.trim().replace(/\\/+$/, '');\n if (!/^https?:\\/\\//.test(trimmed)) {\n throw new JiraError(`JIRA_BASE_URL must start with https:// (got \"${trimmed}\")`, [\n 'Example: https://your-team.atlassian.net',\n ]);\n }\n return trimmed;\n}\n\n/**\n * Resolve credentials from the environment first, then the config file.\n * Throws a JiraError carrying setup instructions when anything is missing.\n */\nexport function loadConfig(): Config {\n const file = readConfigFile();\n\n const baseUrl = process.env['JIRA_BASE_URL'] ?? file.baseUrl;\n const email = process.env['JIRA_EMAIL'] ?? file.email;\n const apiToken = process.env['JIRA_API_TOKEN'] ?? file.apiToken;\n\n const missing: string[] = [];\n if (!baseUrl) missing.push('JIRA_BASE_URL');\n if (!email) missing.push('JIRA_EMAIL');\n if (!apiToken) missing.push('JIRA_API_TOKEN');\n\n if (missing.length > 0 || !baseUrl || !email || !apiToken) {\n throw new JiraError(`Missing Jira credentials: ${missing.join(', ')}`, [\n 'Set them in your shell:',\n ' export JIRA_BASE_URL=\"https://your-team.atlassian.net\"',\n ' export JIRA_EMAIL=\"you@example.com\"',\n ' export JIRA_API_TOKEN=\"...\"',\n '',\n `Or write them to ${CONFIG_PATH}:`,\n ' { \"baseUrl\": \"...\", \"email\": \"...\", \"apiToken\": \"...\" }',\n '',\n `Create an API token at ${TOKEN_URL}`,\n ]);\n }\n\n return {\n baseUrl: normalizeBaseUrl(baseUrl),\n email: email.trim(),\n apiToken: apiToken.trim(),\n issuesJql: file.issuesJql?.trim() || DEFAULT_ISSUES_JQL,\n worklogDays:\n typeof file.worklogDays === 'number' && file.worklogDays > 0\n ? Math.floor(file.worklogDays)\n : DEFAULT_WORKLOG_DAYS,\n };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["Box", "Text", "useEffect", "useRef", "useState", "Box", "Text", "Box", "Text", "jsx", "jsxs", "jsx", "jsxs", "Text", "Box", "Box", "Text", "jsx", "jsxs", "Box", "Text", "Box", "Text", "jsx", "jsxs", "MONTHS", "Text", "Box", "Box", "Text", "jsx", "jsxs", "MONTHS", "isWeekend", "Box", "Text", "useEffect", "useStdin", "ESC", "useEffect", "useStdout", "jsx", "jsxs", "useState", "useEffect", "useRef", "Box", "Text", "jsx", "require", "ESC"]
|
|
7
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "jira-dashboard",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Full-screen terminal dashboard for Jira: 14-day worklog summary and your open tickets",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"jdb": "dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"README.md"
|
|
12
|
+
],
|
|
13
|
+
"engines": {
|
|
14
|
+
"node": ">=20"
|
|
15
|
+
},
|
|
16
|
+
"scripts": {
|
|
17
|
+
"dev": "tsx src/cli.tsx",
|
|
18
|
+
"build": "esbuild src/cli.tsx --bundle --outfile=dist/cli.js --platform=node --format=esm --target=node20 --packages=external --sourcemap && chmod +x dist/cli.js",
|
|
19
|
+
"publish-check": "npm run lint && npm run typecheck && npm run build",
|
|
20
|
+
"prepublishOnly": "npm run publish-check",
|
|
21
|
+
"typecheck": "tsc --noEmit",
|
|
22
|
+
"lint": "eslint .",
|
|
23
|
+
"lint:fix": "eslint . --fix",
|
|
24
|
+
"lint:staged": "lint-staged"
|
|
25
|
+
},
|
|
26
|
+
"keywords": [
|
|
27
|
+
"jira",
|
|
28
|
+
"cli",
|
|
29
|
+
"tui",
|
|
30
|
+
"ink",
|
|
31
|
+
"dashboard",
|
|
32
|
+
"worklog"
|
|
33
|
+
],
|
|
34
|
+
"license": "MIT",
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"ink": "^5.1.0",
|
|
37
|
+
"react": "^18.3.1"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@eslint/js": "^9.17.0",
|
|
41
|
+
"@types/node": "^22.10.2",
|
|
42
|
+
"@types/react": "^18.3.18",
|
|
43
|
+
"esbuild": "^0.28.1",
|
|
44
|
+
"eslint": "^9.17.0",
|
|
45
|
+
"eslint-plugin-react": "^7.37.3",
|
|
46
|
+
"eslint-plugin-react-hooks": "^5.1.0",
|
|
47
|
+
"lint-staged": "^15.3.0",
|
|
48
|
+
"tsx": "^4.19.2",
|
|
49
|
+
"typescript": "^5.7.2",
|
|
50
|
+
"typescript-eslint": "^8.19.0"
|
|
51
|
+
}
|
|
52
|
+
}
|