@kitlangton/ghui 0.1.21 → 0.2.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/package.json +9 -4
- package/src/App.tsx +620 -792
- package/src/appCommands.ts +70 -16
- package/src/domain.ts +13 -0
- package/src/index.tsx +1 -7
- package/src/keyboard/opentuiAdapter.ts +43 -0
- package/src/keymap/all.ts +103 -0
- package/src/keymap/closeModal.ts +13 -0
- package/src/keymap/commandPalette.ts +16 -0
- package/src/keymap/commentModal.ts +73 -0
- package/src/keymap/commentThreadModal.ts +24 -0
- package/src/keymap/detailView.ts +30 -0
- package/src/keymap/diffView.ts +91 -0
- package/src/keymap/filterMode.ts +13 -0
- package/src/keymap/helpers.ts +24 -0
- package/src/keymap/labelModal.ts +16 -0
- package/src/keymap/listNav.ts +119 -0
- package/src/keymap/mergeModal.ts +23 -0
- package/src/keymap/openRepositoryModal.ts +13 -0
- package/src/keymap/themeModal.ts +49 -0
- package/src/mergeActions.ts +4 -1
- package/src/services/GitHubService.ts +42 -6
- package/src/services/MockGitHubService.ts +37 -2
- package/src/themeStore.ts +28 -11
- package/src/ui/CommandPalette.tsx +123 -63
- package/src/ui/DetailsPane.tsx +192 -54
- package/src/ui/FooterHints.tsx +9 -18
- package/src/ui/LoadingLogo.tsx +75 -0
- package/src/ui/PullRequestDiffPane.tsx +25 -18
- package/src/ui/PullRequestList.tsx +6 -2
- package/src/ui/comments.tsx +143 -0
- package/src/ui/diff.ts +198 -6
- package/src/ui/modals.tsx +4 -39
- package/src/ui/primitives.tsx +5 -1
- package/src/ui/singleLineInput.ts +2 -1
- package/src/ui/spinner.ts +1 -0
package/src/ui/modals.tsx
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Data } from "effect"
|
|
2
|
-
import { formatShortDate, formatTimestamp } from "../date.js"
|
|
3
2
|
import type { PullRequestLabel, PullRequestMergeInfo, PullRequestReviewComment } from "../domain.js"
|
|
4
3
|
import { availableMergeActions } from "../mergeActions.js"
|
|
5
4
|
import { clampCursor, commentEditorLines, cursorLineIndexForLines } from "./commentEditor.js"
|
|
6
5
|
import { colors, filterThemeDefinitions, themeDefinitions, type ThemeId } from "./colors.js"
|
|
6
|
+
import { commentDisplayRows, CommentSegmentsLine, type CommentDisplayLine } from "./comments.js"
|
|
7
7
|
import { centerCell, Filler, fitCell, HintRow, PlainLine, StandardModal, standardModalDims, TextLine } from "./primitives.js"
|
|
8
8
|
import { labelColor, shortRepoName } from "./pullRequests.js"
|
|
9
9
|
|
|
@@ -484,41 +484,8 @@ export const CommentModal = ({
|
|
|
484
484
|
)
|
|
485
485
|
}
|
|
486
486
|
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
readonly text: string
|
|
490
|
-
readonly fg: string
|
|
491
|
-
readonly bold?: boolean
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
const wrapCommentBody = (body: string, width: number) => {
|
|
495
|
-
const lines = body.length === 0 ? [""] : body.split("\n")
|
|
496
|
-
return lines.flatMap((line) => {
|
|
497
|
-
if (line.length === 0) return [""]
|
|
498
|
-
const wrapped: string[] = []
|
|
499
|
-
for (let index = 0; index < line.length; index += width) {
|
|
500
|
-
wrapped.push(line.slice(index, index + width))
|
|
501
|
-
}
|
|
502
|
-
return wrapped
|
|
503
|
-
})
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
const formatCommentDate = (date: Date | null) => date ? `${formatShortDate(date)} ${formatTimestamp(date)}` : ""
|
|
507
|
-
|
|
508
|
-
const commentThreadRows = (comments: readonly PullRequestReviewComment[], width: number): readonly CommentThreadRow[] =>
|
|
509
|
-
comments.flatMap((comment, commentIndex) => {
|
|
510
|
-
const timestamp = formatCommentDate(comment.createdAt)
|
|
511
|
-
const header = timestamp ? `${comment.author} ${timestamp}` : comment.author
|
|
512
|
-
return [
|
|
513
|
-
{ key: `${comment.id}:header`, text: header, fg: colors.count, bold: true },
|
|
514
|
-
...wrapCommentBody(comment.body, width).map((line, lineIndex) => ({
|
|
515
|
-
key: `${comment.id}:body:${lineIndex}`,
|
|
516
|
-
text: line,
|
|
517
|
-
fg: colors.text,
|
|
518
|
-
})),
|
|
519
|
-
...(commentIndex < comments.length - 1 ? [{ key: `${comment.id}:gap`, text: "", fg: colors.muted }] : []),
|
|
520
|
-
]
|
|
521
|
-
})
|
|
487
|
+
const commentThreadRows = (comments: readonly PullRequestReviewComment[], width: number): readonly CommentDisplayLine[] =>
|
|
488
|
+
comments.flatMap((comment) => commentDisplayRows({ item: comment, width }))
|
|
522
489
|
|
|
523
490
|
export const CommentThreadModal = ({
|
|
524
491
|
state,
|
|
@@ -559,9 +526,7 @@ export const CommentThreadModal = ({
|
|
|
559
526
|
>
|
|
560
527
|
{visibleRows.length === 0 ? (
|
|
561
528
|
<PlainLine text={fitCell("No comments on this line.", contentWidth)} fg={colors.muted} />
|
|
562
|
-
) : visibleRows.map((row) =>
|
|
563
|
-
<PlainLine key={row.key} text={fitCell(row.text, contentWidth)} fg={row.fg} bold={row.bold ?? false} />
|
|
564
|
-
))}
|
|
529
|
+
) : visibleRows.map((row) => <CommentSegmentsLine key={row.key} segments={row.segments} />)}
|
|
565
530
|
</StandardModal>
|
|
566
531
|
)
|
|
567
532
|
}
|
package/src/ui/primitives.tsx
CHANGED
|
@@ -167,6 +167,7 @@ export const ModalFrame = ({
|
|
|
167
167
|
width,
|
|
168
168
|
height,
|
|
169
169
|
junctionRows = [],
|
|
170
|
+
topJunctionColumns = [],
|
|
170
171
|
backgroundColor = colors.modalBackground,
|
|
171
172
|
}: {
|
|
172
173
|
children: React.ReactNode
|
|
@@ -175,15 +176,18 @@ export const ModalFrame = ({
|
|
|
175
176
|
width: number
|
|
176
177
|
height: number
|
|
177
178
|
junctionRows?: readonly number[]
|
|
179
|
+
topJunctionColumns?: readonly number[]
|
|
178
180
|
backgroundColor?: string
|
|
179
181
|
}) => {
|
|
180
182
|
const innerWidth = Math.max(1, width - 2)
|
|
181
183
|
const innerHeight = Math.max(1, height - 2)
|
|
182
184
|
const junctions = new Set(junctionRows)
|
|
185
|
+
const topJunctions = new Set(topJunctionColumns)
|
|
186
|
+
const topBorder = Array.from({ length: innerWidth }, (_, index) => topJunctions.has(index) ? "┬" : "─").join("")
|
|
183
187
|
|
|
184
188
|
return (
|
|
185
189
|
<box position="absolute" left={left} top={top} width={width} height={height} flexDirection="column" backgroundColor={backgroundColor}>
|
|
186
|
-
<PlainLine text={`┌${
|
|
190
|
+
<PlainLine text={`┌${topBorder}┐`} fg={colors.separator} />
|
|
187
191
|
<box height={innerHeight} flexDirection="row">
|
|
188
192
|
<box width={1} height={innerHeight} flexDirection="column">
|
|
189
193
|
{Array.from({ length: innerHeight }, (_, index) => <PlainLine key={index} text={junctions.has(index) ? "├" : "│"} fg={colors.separator} />)}
|
|
@@ -11,7 +11,8 @@ export const singleLineText = (text: string) => text.replace(/[\r\n]+/g, " ")
|
|
|
11
11
|
|
|
12
12
|
export const printableKeyText = (key: Pick<SingleLineInputKey, "ctrl" | "meta" | "sequence">) => {
|
|
13
13
|
if (key.ctrl || key.meta || key.sequence.length === 0) return null
|
|
14
|
-
|
|
14
|
+
// oxlint-disable-next-line no-control-regex -- intentional: skip control characters
|
|
15
|
+
return /^[^\u0000-\u001f\u007f]+$/.test(key.sequence) ? key.sequence : null
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
export const editSingleLineInput = (value: string, key: SingleLineInputKey) => {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"] as const;
|