@kud/jira-ink 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,4 +1,7 @@
1
1
  import * as react from 'react';
2
+ import { ReactNode } from 'react';
3
+ import { MarkdownTheme } from '@kud/ink-markdown';
4
+ import { Hint } from '@kud/ink-ui';
2
5
  import { LocatedAttachment, JiraClient } from '@kud/jira';
3
6
 
4
7
  /**
@@ -53,8 +56,23 @@ type IssueDetailViewProps = {
53
56
  onComment?: () => void;
54
57
  onAssign?: () => void;
55
58
  onOpenBrowser?: () => void;
59
+ /**
60
+ * A host that draws its own chrome — a bordered drill frame with the title
61
+ * on top and the hints pinned at the foot — takes the screen in parts and
62
+ * places them. The view then draws neither its title line nor its footer,
63
+ * so nothing appears twice. `height` is still the whole area the host has
64
+ * given the body, frame excluded.
65
+ */
66
+ frame?: (parts: {
67
+ title: string;
68
+ subtitle: string;
69
+ hints: Hint[];
70
+ body: ReactNode;
71
+ }) => ReactNode;
72
+ /** Colours for the Markdown body; a host passes its own tokens so links, code and headings sit in its palette. */
73
+ theme?: MarkdownTheme;
56
74
  };
57
- declare const IssueDetailView: ({ issue, height, width, onBack, onTransition, onComment, onAssign, onOpenBrowser, }: IssueDetailViewProps) => react.JSX.Element;
75
+ declare const IssueDetailView: ({ issue, height, width, onBack, onTransition, onComment, onAssign, onOpenBrowser, frame, theme, }: IssueDetailViewProps) => string | number | bigint | boolean | Iterable<ReactNode> | Promise<string | number | bigint | boolean | react.ReactPortal | react.ReactElement<unknown, string | react.JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | react.JSX.Element | null | undefined;
58
76
 
59
77
  type IssueBodyProps = {
60
78
  client: JiraClient;
@@ -65,6 +83,9 @@ type IssueBodyProps = {
65
83
  /** Override the terminal-derived size when the host draws inside a frame. */
66
84
  width?: number;
67
85
  height?: number;
86
+ /** The host's chrome around the screen — see `IssueDetailView`. */
87
+ frame?: IssueDetailViewProps["frame"];
88
+ theme?: IssueDetailViewProps["theme"];
68
89
  };
69
90
  /**
70
91
  * The issue screen as a host mounts it: hand it a client and a key, and it
@@ -76,6 +97,6 @@ type IssueBodyProps = {
76
97
  * `IssueDetailView` for jira-cli's own TUI, which owns the prompts they need;
77
98
  * here they are simply not wired, so the hint row does not offer them.
78
99
  */
79
- declare const IssueBody: ({ client, baseUrl, issueKey, onExit, width, height, }: IssueBodyProps) => react.JSX.Element;
100
+ declare const IssueBody: ({ client, baseUrl, issueKey, onExit, width, height, frame, theme, }: IssueBodyProps) => string | number | bigint | boolean | Iterable<ReactNode> | Promise<string | number | bigint | boolean | react.ReactPortal | react.ReactElement<unknown, string | react.JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | react.JSX.Element | null | undefined;
80
101
 
81
102
  export { IssueBody, type IssueBodyProps, type IssueDetail, IssueDetailView, type IssueDetailViewProps, type Transition, issueDetailOf, transitionsOf };
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { MarkdownViewport } from '@kud/ink-markdown';
2
- import { useTabs, KeyValue, Badge, Tabs, FooterHints, LoadingScreen, StatusMessage } from '@kud/ink-ui';
2
+ import { useTabs, KeyValue, Badge, Tabs, FooterHints, StatusMessage, LoadingScreen } from '@kud/ink-ui';
3
3
  import { useInput, Box, Text, useStdout } from 'ink';
4
4
  import { useState, useEffect } from 'react';
5
5
  import { jsxs, jsx } from 'react/jsx-runtime';
@@ -36,7 +36,9 @@ var IssueDetailView = ({
36
36
  onTransition,
37
37
  onComment,
38
38
  onAssign,
39
- onOpenBrowser
39
+ onOpenBrowser,
40
+ frame,
41
+ theme
40
42
  }) => {
41
43
  const tabs = [
42
44
  { value: "description", label: "Description" },
@@ -50,7 +52,7 @@ var IssueDetailView = ({
50
52
  const { active } = useTabs(tabs, { initial: "description" });
51
53
  const [scroll, setScroll] = useState(0);
52
54
  useInput((input, key) => {
53
- if (key.escape || key.leftArrow) return onBack();
55
+ if (key.escape) return onBack();
54
56
  if (input === "t" && onTransition) return onTransition();
55
57
  if (input === "c" && onComment) return onComment();
56
58
  if (input === "a" && onAssign) return onAssign();
@@ -59,7 +61,8 @@ var IssueDetailView = ({
59
61
  if (key.upArrow || input === "k") setScroll((s) => Math.max(0, s - 1));
60
62
  });
61
63
  const source = active === "comments" ? commentsAsMarkdown(issue) : active === "attachments" ? attachmentsAsMarkdown(issue) : issue.description || "_No description._";
62
- const bodyHeight = Math.max(3, height - 12);
64
+ const chromeLines = frame ? 8 : 12;
65
+ const bodyHeight = Math.max(3, height - chromeLines);
63
66
  const hints = [
64
67
  ["\u2190\u2192", "section"],
65
68
  ["\u2191\u2193", "scroll"],
@@ -69,13 +72,8 @@ var IssueDetailView = ({
69
72
  ...onOpenBrowser ? [["o", "browser"]] : [],
70
73
  ["esc", "back"]
71
74
  ];
72
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
73
- /* @__PURE__ */ jsxs(Text, { bold: true, children: [
74
- issue.key,
75
- " ",
76
- issue.summary
77
- ] }),
78
- /* @__PURE__ */ jsxs(Box, { marginTop: 1, flexDirection: "column", children: [
75
+ const body = /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
76
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
79
77
  issue.parent && /* @__PURE__ */ jsx(
80
78
  KeyValue,
81
79
  {
@@ -84,7 +82,7 @@ var IssueDetailView = ({
84
82
  }
85
83
  ),
86
84
  /* @__PURE__ */ jsx(KeyValue, { label: "status", value: /* @__PURE__ */ jsx(Badge, { children: issue.status }) }),
87
- /* @__PURE__ */ jsx(KeyValue, { label: "type", value: issue.type }),
85
+ frame ? null : /* @__PURE__ */ jsx(KeyValue, { label: "type", value: issue.type }),
88
86
  /* @__PURE__ */ jsx(KeyValue, { label: "assignee", value: issue.assignee }),
89
87
  /* @__PURE__ */ jsx(KeyValue, { label: "reporter", value: issue.reporter }),
90
88
  issue.labels.length > 0 && /* @__PURE__ */ jsx(KeyValue, { label: "labels", value: issue.labels.join(", ") })
@@ -96,9 +94,25 @@ var IssueDetailView = ({
96
94
  source,
97
95
  width: Math.max(20, width - 2),
98
96
  height: bodyHeight,
99
- scrollOffset: scroll
97
+ scrollOffset: scroll,
98
+ theme
100
99
  }
101
- ) }),
100
+ ) })
101
+ ] });
102
+ if (frame)
103
+ return frame({
104
+ title: `${issue.key} \xB7 ${issue.type}`,
105
+ subtitle: issue.summary,
106
+ hints,
107
+ body
108
+ });
109
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
110
+ /* @__PURE__ */ jsxs(Text, { bold: true, children: [
111
+ issue.key,
112
+ " ",
113
+ issue.summary
114
+ ] }),
115
+ /* @__PURE__ */ jsx(Box, { marginTop: 1, children: body }),
102
116
  /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(FooterHints, { hints }) })
103
117
  ] });
104
118
  };
@@ -157,7 +171,9 @@ var IssueBody = ({
157
171
  issueKey,
158
172
  onExit,
159
173
  width,
160
- height
174
+ height,
175
+ frame,
176
+ theme
161
177
  }) => {
162
178
  const term = useTermSize();
163
179
  const [state, setState] = useState({ phase: "loading" });
@@ -174,20 +190,26 @@ var IssueBody = ({
174
190
  live = false;
175
191
  };
176
192
  }, [client, baseUrl, issueKey]);
177
- useInput(
178
- (_input, key) => {
179
- if (key.escape) onExit();
180
- },
181
- { isActive: state.phase !== "ready" }
182
- );
193
+ useInput((input, key) => {
194
+ if (input === "q") return onExit();
195
+ if (key.escape && state.phase !== "ready") onExit();
196
+ });
197
+ const framed = (body) => frame ? frame({
198
+ title: issueKey,
199
+ subtitle: "",
200
+ hints: [["esc", "back"]],
201
+ body
202
+ }) : body;
183
203
  if (state.phase === "loading")
184
- return /* @__PURE__ */ jsx(LoadingScreen, { label: `Loading ${issueKey}\u2026` });
204
+ return framed(/* @__PURE__ */ jsx(LoadingScreen, { label: `Loading ${issueKey}\u2026` }));
185
205
  if (state.phase === "error")
186
- return /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: /* @__PURE__ */ jsxs(StatusMessage, { variant: "error", children: [
187
- issueKey,
188
- ": ",
189
- state.message
190
- ] }) });
206
+ return framed(
207
+ /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: /* @__PURE__ */ jsxs(StatusMessage, { variant: "error", children: [
208
+ issueKey,
209
+ ": ",
210
+ state.message
211
+ ] }) })
212
+ );
191
213
  return /* @__PURE__ */ jsx(
192
214
  IssueDetailView,
193
215
  {
@@ -195,7 +217,9 @@ var IssueBody = ({
195
217
  width: width ?? term.cols,
196
218
  height: height ?? term.rows,
197
219
  onBack: onExit,
198
- onOpenBrowser: () => openInBrowser(state.issue.url)
220
+ onOpenBrowser: () => openInBrowser(state.issue.url),
221
+ frame,
222
+ theme
199
223
  }
200
224
  );
201
225
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kud/jira-ink",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "Ink components for viewing Jira issues: the issue detail screen, shared by jira-cli's TUI and cockpit",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -30,7 +30,7 @@
30
30
  },
31
31
  "dependencies": {
32
32
  "@kud/ink-ui": "0.25.0",
33
- "@kud/ink-markdown": "0.1.1",
33
+ "@kud/ink-markdown": "0.3.0",
34
34
  "@kud/jira": "0.3.0"
35
35
  },
36
36
  "devDependencies": {