@kitlangton/ghui 0.2.1 → 0.3.2

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.
Files changed (53) hide show
  1. package/README.md +1 -4
  2. package/bin/ghui.js +6 -1
  3. package/dist/index.js +9419 -0
  4. package/package.json +7 -4
  5. package/src/App.tsx +0 -2604
  6. package/src/appCommands.ts +0 -384
  7. package/src/commands.ts +0 -73
  8. package/src/config.ts +0 -28
  9. package/src/date.ts +0 -20
  10. package/src/domain.ts +0 -139
  11. package/src/errors.ts +0 -10
  12. package/src/index.tsx +0 -50
  13. package/src/keyboard/opentuiAdapter.ts +0 -43
  14. package/src/keymap/all.ts +0 -103
  15. package/src/keymap/closeModal.ts +0 -13
  16. package/src/keymap/commandPalette.ts +0 -16
  17. package/src/keymap/commentModal.ts +0 -73
  18. package/src/keymap/commentThreadModal.ts +0 -24
  19. package/src/keymap/detailView.ts +0 -30
  20. package/src/keymap/diffView.ts +0 -91
  21. package/src/keymap/filterMode.ts +0 -13
  22. package/src/keymap/helpers.ts +0 -24
  23. package/src/keymap/labelModal.ts +0 -16
  24. package/src/keymap/listNav.ts +0 -119
  25. package/src/keymap/mergeModal.ts +0 -23
  26. package/src/keymap/openRepositoryModal.ts +0 -13
  27. package/src/keymap/themeModal.ts +0 -49
  28. package/src/mergeActions.ts +0 -88
  29. package/src/observability.ts +0 -46
  30. package/src/pullRequestCache.ts +0 -19
  31. package/src/pullRequestViews.ts +0 -45
  32. package/src/services/BrowserOpener.ts +0 -22
  33. package/src/services/Clipboard.ts +0 -46
  34. package/src/services/CommandRunner.ts +0 -91
  35. package/src/services/GitHubService.ts +0 -741
  36. package/src/services/MockGitHubService.ts +0 -181
  37. package/src/themeStore.ts +0 -60
  38. package/src/ui/CommandPalette.tsx +0 -216
  39. package/src/ui/DetailsPane.tsx +0 -656
  40. package/src/ui/FooterHints.tsx +0 -90
  41. package/src/ui/LoadingLogo.tsx +0 -75
  42. package/src/ui/PullRequestDiffPane.tsx +0 -248
  43. package/src/ui/PullRequestList.tsx +0 -210
  44. package/src/ui/colors.ts +0 -814
  45. package/src/ui/commentEditor.ts +0 -126
  46. package/src/ui/comments.tsx +0 -143
  47. package/src/ui/diff.ts +0 -650
  48. package/src/ui/diffStats.tsx +0 -25
  49. package/src/ui/modals.tsx +0 -614
  50. package/src/ui/primitives.tsx +0 -205
  51. package/src/ui/pullRequests.ts +0 -106
  52. package/src/ui/singleLineInput.ts +0 -26
  53. package/src/ui/spinner.ts +0 -1
@@ -1,384 +0,0 @@
1
- import type { AppCommand } from "./commands.js"
2
- import { defineCommand } from "./commands.js"
3
- import type { LoadStatus, PullRequestItem } from "./domain.js"
4
- import type { DiffView, DiffWhitespaceMode, DiffWrapMode } from "./ui/diff.js"
5
- import { type PullRequestView, viewEquals, viewLabel, viewMode } from "./pullRequestViews.js"
6
-
7
- interface AppCommandActions {
8
- readonly openCommandPalette: () => void
9
- readonly refreshPullRequests: (message?: string, options?: { readonly resetTransientState?: boolean }) => void
10
- readonly openFilter: () => void
11
- readonly clearFilter: () => void
12
- readonly openThemeModal: () => void
13
- readonly openRepositoryPicker: () => void
14
- readonly loadMorePullRequests: () => void
15
- readonly switchViewTo: (view: PullRequestView) => void
16
- readonly openDetails: () => void
17
- readonly closeDetails: () => void
18
- readonly openDiffView: () => void
19
- readonly closeDiffView: () => void
20
- readonly reloadDiff: () => void
21
- readonly toggleDiffRenderView: () => void
22
- readonly toggleDiffWrapMode: () => void
23
- readonly toggleDiffWhitespaceMode: () => void
24
- readonly jumpDiffFile: (delta: 1 | -1) => void
25
- readonly openSelectedDiffComment: () => void
26
- readonly toggleDiffCommentRange: () => void
27
- readonly moveDiffCommentThread: (delta: 1 | -1) => void
28
- readonly openDiffCommentModal: () => void
29
- readonly togglePullRequestDraftStatus: () => void
30
- readonly openLabelModal: () => void
31
- readonly openMergeModal: () => void
32
- readonly openCloseModal: () => void
33
- readonly openPullRequestInBrowser: () => void
34
- readonly copyPullRequestMetadata: () => void
35
- readonly quit: () => void
36
- }
37
-
38
- interface BuildAppCommandsInput {
39
- readonly pullRequestStatus: LoadStatus
40
- readonly filterQuery: string
41
- readonly filterMode: boolean
42
- readonly selectedRepository: string | null
43
- readonly activeViews: readonly PullRequestView[]
44
- readonly activeView: PullRequestView
45
- readonly loadedPullRequestCount: number
46
- readonly hasMorePullRequests: boolean
47
- readonly isLoadingMorePullRequests: boolean
48
- readonly selectedPullRequest: PullRequestItem | null
49
- readonly detailFullView: boolean
50
- readonly diffFullView: boolean
51
- readonly diffReady: boolean
52
- readonly effectiveDiffRenderView: DiffView
53
- readonly diffWrapMode: DiffWrapMode
54
- readonly diffWhitespaceMode: DiffWhitespaceMode
55
- readonly readyDiffFileCount: number
56
- readonly diffFileIndex: number
57
- readonly diffRangeActive: boolean
58
- readonly selectedDiffCommentAnchorLabel: string | null
59
- readonly selectedDiffCommentThreadCount: number
60
- readonly hasDiffCommentThreads: boolean
61
- readonly actions: AppCommandActions
62
- }
63
-
64
- export const buildAppCommands = ({
65
- pullRequestStatus,
66
- filterQuery,
67
- filterMode,
68
- selectedRepository,
69
- activeViews,
70
- activeView,
71
- loadedPullRequestCount,
72
- hasMorePullRequests,
73
- isLoadingMorePullRequests,
74
- selectedPullRequest,
75
- detailFullView,
76
- diffFullView,
77
- diffReady,
78
- effectiveDiffRenderView,
79
- diffWrapMode,
80
- diffWhitespaceMode,
81
- readyDiffFileCount,
82
- diffFileIndex,
83
- diffRangeActive,
84
- selectedDiffCommentAnchorLabel,
85
- selectedDiffCommentThreadCount,
86
- hasDiffCommentThreads,
87
- actions,
88
- }: BuildAppCommandsInput): readonly AppCommand[] => {
89
- const selectedPullRequestLabel = selectedPullRequest ? `#${selectedPullRequest.number} ${selectedPullRequest.repository}` : "No pull request selected"
90
- const noPullRequestReason = selectedPullRequest ? null : "Select a pull request first."
91
- const noOpenPullRequestReason = selectedPullRequest?.state === "open" ? null : selectedPullRequest ? "Pull request is not open." : noPullRequestReason
92
- const diffReadyReason = selectedPullRequest
93
- ? diffReady ? null : "Load the diff before running this command."
94
- : noPullRequestReason
95
- const diffOpenReadyReason = diffFullView ? diffReadyReason : "Open a diff first."
96
- const selectedDiffLineReason = diffFullView && diffReady
97
- ? selectedDiffCommentAnchorLabel ? null : "No diff line selected."
98
- : diffOpenReadyReason
99
- const diffThreadReason = diffFullView && diffReady
100
- ? hasDiffCommentThreads ? null : "No diff comments loaded."
101
- : diffOpenReadyReason
102
- const loadMoreDisabledReason = isLoadingMorePullRequests
103
- ? "Already loading more pull requests."
104
- : hasMorePullRequests ? null : "No more pull requests loaded by this view."
105
-
106
- const forSelected = (
107
- command: Omit<AppCommand, "subtitle" | "disabledReason"> & { readonly requireOpen?: boolean },
108
- ): AppCommand => {
109
- const { requireOpen, ...rest } = command
110
- return defineCommand({
111
- ...rest,
112
- subtitle: selectedPullRequestLabel,
113
- disabledReason: requireOpen ? noOpenPullRequestReason : noPullRequestReason,
114
- })
115
- }
116
-
117
- return [
118
- defineCommand({
119
- id: "command.open",
120
- title: "Open command palette",
121
- scope: "Global",
122
- subtitle: "Search every available route through ghui",
123
- shortcut: "ctrl-p/cmd-k",
124
- keywords: ["palette", "commands", "deck"],
125
- run: actions.openCommandPalette,
126
- }),
127
- defineCommand({
128
- id: "pull.refresh",
129
- title: pullRequestStatus === "error" ? "Retry loading pull requests" : "Refresh pull requests",
130
- scope: "Global",
131
- subtitle: "Fetch the latest queue from GitHub",
132
- shortcut: "r",
133
- keywords: ["reload", "sync"],
134
- run: () => actions.refreshPullRequests("Refreshed", { resetTransientState: true }),
135
- }),
136
- defineCommand({
137
- id: "filter.open",
138
- title: "Filter pull requests",
139
- scope: "Global",
140
- subtitle: "Search the visible queue",
141
- shortcut: "/",
142
- keywords: ["search"],
143
- run: actions.openFilter,
144
- }),
145
- defineCommand({
146
- id: "filter.clear",
147
- title: "Clear pull request filter",
148
- scope: "Global",
149
- subtitle: "Show every pull request in the current queue",
150
- shortcut: "esc",
151
- disabledReason: filterQuery.length > 0 || filterMode ? null : "No filter is active.",
152
- run: actions.clearFilter,
153
- }),
154
- defineCommand({
155
- id: "theme.open",
156
- title: "Choose theme",
157
- scope: "Global",
158
- subtitle: "Preview and persist a terminal color theme",
159
- shortcut: "t",
160
- keywords: ["colors", "appearance"],
161
- run: actions.openThemeModal,
162
- }),
163
- defineCommand({
164
- id: "repository.open",
165
- title: "Open repository...",
166
- scope: "View",
167
- subtitle: selectedRepository ? `Current repository: ${selectedRepository}` : "Enter owner/name or a GitHub URL",
168
- keywords: ["repo", "repository", "owner", "github"],
169
- run: actions.openRepositoryPicker,
170
- }),
171
- ...activeViews.map((view) => defineCommand({
172
- id: view._tag === "Repository" ? "view.repository" : `view.${view.mode}`,
173
- title: `Show ${viewLabel(view)} view`,
174
- scope: "View" as const,
175
- subtitle: viewEquals(view, activeView) ? "Already showing this view" : "Switch pull request view",
176
- keywords: [viewMode(view), viewLabel(view), "queue", "view"],
177
- disabledReason: viewEquals(view, activeView) ? "Already showing this view." : null,
178
- run: () => actions.switchViewTo(view),
179
- })),
180
- defineCommand({
181
- id: "pull.load-more",
182
- title: "Load more pull requests",
183
- scope: "Navigation",
184
- subtitle: `${loadedPullRequestCount} loaded`,
185
- disabledReason: loadMoreDisabledReason,
186
- keywords: ["next page", "pagination", "more"],
187
- run: actions.loadMorePullRequests,
188
- }),
189
- forSelected({
190
- id: "detail.open",
191
- title: "Open pull request details",
192
- scope: "Pull request",
193
- shortcut: "enter",
194
- run: actions.openDetails,
195
- }),
196
- defineCommand({
197
- id: "detail.close",
198
- title: "Close details view",
199
- scope: "Pull request",
200
- subtitle: "Return to the queue",
201
- shortcut: "esc",
202
- disabledReason: detailFullView ? null : "Details view is not open.",
203
- run: actions.closeDetails,
204
- }),
205
- forSelected({
206
- id: "diff.open",
207
- title: "Open diff",
208
- scope: "Diff",
209
- shortcut: "d",
210
- keywords: ["files", "patch"],
211
- run: actions.openDiffView,
212
- }),
213
- defineCommand({
214
- id: "diff.close",
215
- title: "Close diff view",
216
- scope: "Diff",
217
- subtitle: "Return to the queue or detail view",
218
- shortcut: "esc",
219
- disabledReason: diffFullView ? null : "Diff view is not open.",
220
- run: actions.closeDiffView,
221
- }),
222
- defineCommand({
223
- id: "diff.reload",
224
- title: "Reload diff",
225
- scope: "Diff",
226
- subtitle: selectedPullRequestLabel,
227
- shortcut: "r",
228
- disabledReason: diffFullView && selectedPullRequest ? null : "Open a pull request diff first.",
229
- keywords: ["refresh", "comments"],
230
- run: actions.reloadDiff,
231
- }),
232
- defineCommand({
233
- id: "diff.toggle-view",
234
- title: "Toggle diff split/unified view",
235
- scope: "Diff",
236
- subtitle: effectiveDiffRenderView === "split" ? "Switch to unified view" : "Switch to split view",
237
- shortcut: "shift-v",
238
- disabledReason: diffFullView ? null : "Open a diff first.",
239
- run: actions.toggleDiffRenderView,
240
- }),
241
- defineCommand({
242
- id: "diff.toggle-wrap",
243
- title: "Toggle diff word wrap",
244
- scope: "Diff",
245
- subtitle: diffWrapMode === "none" ? "Wrap long diff lines" : "Keep diff lines unwrapped",
246
- shortcut: "w",
247
- disabledReason: diffFullView ? null : "Open a diff first.",
248
- run: actions.toggleDiffWrapMode,
249
- }),
250
- defineCommand({
251
- id: "diff.toggle-whitespace",
252
- title: diffWhitespaceMode === "ignore" ? "Show whitespace changes" : "Ignore whitespace changes",
253
- scope: "Diff",
254
- subtitle: diffWhitespaceMode === "ignore" ? "Display the original GitHub patch" : "Hide whitespace-only line changes",
255
- disabledReason: diffFullView ? null : "Open a diff first.",
256
- keywords: ["whitespace", "spacing", "ignore", "show"],
257
- run: actions.toggleDiffWhitespaceMode,
258
- }),
259
- defineCommand({
260
- id: "diff.next-file",
261
- title: "Next diff file",
262
- scope: "Diff",
263
- subtitle: readyDiffFileCount > 0 ? `${diffFileIndex + 1}/${readyDiffFileCount}` : "No diff files loaded",
264
- shortcut: "]",
265
- disabledReason: diffFullView && readyDiffFileCount > 0 ? null : diffOpenReadyReason,
266
- run: () => actions.jumpDiffFile(1),
267
- }),
268
- defineCommand({
269
- id: "diff.previous-file",
270
- title: "Previous diff file",
271
- scope: "Diff",
272
- subtitle: readyDiffFileCount > 0 ? `${diffFileIndex + 1}/${readyDiffFileCount}` : "No diff files loaded",
273
- shortcut: "[",
274
- disabledReason: diffFullView && readyDiffFileCount > 0 ? null : diffOpenReadyReason,
275
- run: () => actions.jumpDiffFile(-1),
276
- }),
277
- defineCommand({
278
- id: "diff.open-comment-target",
279
- title: selectedDiffCommentThreadCount > 0 ? "Open selected diff thread" : "Comment on selected diff line",
280
- scope: "Diff",
281
- subtitle: selectedDiffCommentAnchorLabel ?? "No diff line selected",
282
- shortcut: "enter",
283
- disabledReason: selectedDiffLineReason,
284
- keywords: ["review", "comment", "thread", "line"],
285
- run: actions.openSelectedDiffComment,
286
- }),
287
- defineCommand({
288
- id: "diff.toggle-range",
289
- title: diffRangeActive ? "Clear diff comment range" : "Start diff comment range",
290
- scope: "Diff",
291
- subtitle: selectedDiffCommentAnchorLabel ?? "No diff line selected",
292
- shortcut: "v",
293
- disabledReason: selectedDiffLineReason,
294
- keywords: ["review", "comment", "range", "visual"],
295
- run: actions.toggleDiffCommentRange,
296
- }),
297
- defineCommand({
298
- id: "diff.next-thread",
299
- title: "Next diff thread",
300
- scope: "Diff",
301
- subtitle: hasDiffCommentThreads ? "Jump to the next commented line" : "No diff comments loaded",
302
- shortcut: "n",
303
- disabledReason: diffThreadReason,
304
- keywords: ["review", "comment", "thread"],
305
- run: () => actions.moveDiffCommentThread(1),
306
- }),
307
- defineCommand({
308
- id: "diff.previous-thread",
309
- title: "Previous diff thread",
310
- scope: "Diff",
311
- subtitle: hasDiffCommentThreads ? "Jump to the previous commented line" : "No diff comments loaded",
312
- shortcut: "p",
313
- disabledReason: diffThreadReason,
314
- keywords: ["review", "comment", "thread"],
315
- run: () => actions.moveDiffCommentThread(-1),
316
- }),
317
- defineCommand({
318
- id: "diff.add-comment",
319
- title: "Add comment on selected diff line",
320
- scope: "Diff",
321
- subtitle: selectedDiffCommentAnchorLabel ?? "No diff line selected",
322
- shortcut: "a",
323
- disabledReason: selectedDiffLineReason,
324
- keywords: ["review", "reply"],
325
- run: actions.openDiffCommentModal,
326
- }),
327
- forSelected({
328
- id: "pull.toggle-draft",
329
- title: selectedPullRequest?.reviewStatus === "draft" ? "Mark ready for review" : "Mark as draft",
330
- scope: "Pull request",
331
- shortcut: "s",
332
- keywords: ["state", "ready"],
333
- run: actions.togglePullRequestDraftStatus,
334
- }),
335
- forSelected({
336
- id: "pull.labels",
337
- title: "Manage labels",
338
- scope: "Pull request",
339
- shortcut: "l",
340
- run: actions.openLabelModal,
341
- }),
342
- forSelected({
343
- id: "pull.merge",
344
- title: "Merge pull request",
345
- scope: "Pull request",
346
- shortcut: "m",
347
- keywords: ["auto merge", "squash"],
348
- run: actions.openMergeModal,
349
- }),
350
- forSelected({
351
- id: "pull.close",
352
- title: "Close pull request",
353
- scope: "Pull request",
354
- shortcut: "x",
355
- requireOpen: true,
356
- run: actions.openCloseModal,
357
- }),
358
- forSelected({
359
- id: "pull.open-browser",
360
- title: "Open pull request in browser",
361
- scope: "Pull request",
362
- shortcut: "o",
363
- keywords: ["github", "web"],
364
- run: actions.openPullRequestInBrowser,
365
- }),
366
- forSelected({
367
- id: "pull.copy-metadata",
368
- title: "Copy pull request metadata",
369
- scope: "Pull request",
370
- shortcut: "y",
371
- keywords: ["clipboard", "url", "title"],
372
- run: actions.copyPullRequestMetadata,
373
- }),
374
- defineCommand({
375
- id: "app.quit",
376
- title: "Quit ghui",
377
- scope: "System",
378
- subtitle: "Leave the terminal UI",
379
- shortcut: "q",
380
- keywords: ["exit"],
381
- run: actions.quit,
382
- }),
383
- ]
384
- }
package/src/commands.ts DELETED
@@ -1,73 +0,0 @@
1
- export type CommandScope = "Global" | "View" | "Pull request" | "Diff" | "Navigation" | "System"
2
-
3
- const SCOPE_ORDER: readonly CommandScope[] = ["Global", "View", "Pull request", "Diff", "Navigation", "System"]
4
-
5
- export const sortCommandsByScope = (commands: readonly AppCommand[]) =>
6
- [...commands].sort((left, right) => SCOPE_ORDER.indexOf(left.scope) - SCOPE_ORDER.indexOf(right.scope))
7
-
8
- export interface AppCommand {
9
- readonly id: string
10
- readonly title: string
11
- readonly scope: CommandScope
12
- readonly run: () => void
13
- readonly subtitle?: string
14
- readonly shortcut?: string
15
- readonly keywords?: readonly string[]
16
- readonly disabledReason?: string | null
17
- }
18
-
19
- export const defineCommand = (command: AppCommand): AppCommand => command
20
-
21
- export const commandEnabled = (command: AppCommand) => !command.disabledReason
22
-
23
- const normalize = (text: string) => text.toLowerCase().replace(/[^a-z0-9#]+/g, " ").trim()
24
-
25
- const acronym = (text: string) => normalize(text).split(" ").filter(Boolean).map((word) => word[0]).join("")
26
-
27
- const fuzzyIncludes = (text: string, query: string) => {
28
- let index = 0
29
- for (const char of text) {
30
- if (char === query[index]) index++
31
- if (index >= query.length) return true
32
- }
33
- return query.length === 0
34
- }
35
-
36
- const commandSearchText = (command: AppCommand) => normalize([
37
- command.title,
38
- command.subtitle,
39
- command.scope,
40
- command.shortcut,
41
- ...(command.keywords ?? []),
42
- ].filter(Boolean).join(" "))
43
-
44
- const commandScore = (command: AppCommand, query: string) => {
45
- const normalizedQuery = normalize(query)
46
- if (normalizedQuery.length === 0) return 0
47
-
48
- const title = normalize(command.title)
49
- const searchText = commandSearchText(command)
50
- const titleAcronym = acronym(command.title)
51
- if (title.startsWith(normalizedQuery)) return 0
52
- if (searchText.startsWith(normalizedQuery)) return 1
53
- if (title.includes(normalizedQuery)) return 2
54
- if (searchText.includes(normalizedQuery)) return 3
55
- if (titleAcronym.startsWith(normalizedQuery)) return 4
56
- if (fuzzyIncludes(searchText, normalizedQuery.replaceAll(" ", ""))) return 5
57
- return null
58
- }
59
-
60
- export const filterCommands = (commands: readonly AppCommand[], query: string) => {
61
- return commands.flatMap((command, index) => {
62
- const score = commandScore(command, query)
63
- return score === null ? [] : [{ command, index, score }]
64
- }).sort((left, right) => {
65
- const enabled = Number(commandEnabled(right.command)) - Number(commandEnabled(left.command))
66
- return enabled || left.score - right.score || left.index - right.index
67
- }).map(({ command }) => command)
68
- }
69
-
70
- export const clampCommandIndex = (index: number, commands: readonly AppCommand[]) => {
71
- if (commands.length === 0) return 0
72
- return Math.max(0, Math.min(commands.length - 1, index))
73
- }
package/src/config.ts DELETED
@@ -1,28 +0,0 @@
1
- import { Config, Effect } from "effect"
2
-
3
- const positiveIntOr = (fallback: number) => (value: number) => Number.isFinite(value) && value > 0 ? value : fallback
4
-
5
- const pageSizeOr = (fallback: number) => (value: number) => Math.min(100, positiveIntOr(fallback)(value))
6
-
7
- const appConfig = Config.all({
8
- author: Config.string("GHUI_AUTHOR").pipe(
9
- Config.withDefault("@me"),
10
- Config.map((value) => value.trim() || "@me"),
11
- ),
12
- prFetchLimit: Config.int("GHUI_PR_FETCH_LIMIT").pipe(
13
- Config.withDefault(200),
14
- Config.map(positiveIntOr(200)),
15
- ),
16
- prPageSize: Config.int("GHUI_PR_PAGE_SIZE").pipe(
17
- Config.withDefault(50),
18
- Config.map(pageSizeOr(50)),
19
- ),
20
- repository: Config.string("GHUI_REPO").pipe(
21
- Config.withDefault(""),
22
- Config.map((value) => value.trim() || null),
23
- ),
24
- })
25
-
26
- export const config = Effect.runSync(Effect.gen(function*() {
27
- return yield* appConfig
28
- }))
package/src/date.ts DELETED
@@ -1,20 +0,0 @@
1
- const DAY_MS = 24 * 60 * 60 * 1000
2
-
3
- export const formatShortDate = (date: Date) =>
4
- date.toLocaleDateString("en-US", { weekday: "short", month: "numeric", day: "numeric" })
5
-
6
- export const formatTimestamp = (date: Date) =>
7
- date.toLocaleTimeString("en-US", { hour: "numeric", minute: "2-digit" }).toLowerCase()
8
-
9
- export const daysOpen = (date: Date) => Math.max(0, Math.floor((Date.now() - date.getTime()) / DAY_MS))
10
-
11
- export const formatRelativeDate = (date: Date): string => {
12
- const days = daysOpen(date)
13
- if (days === 0) return "today"
14
- if (days === 1) return "yesterday"
15
- if (days < 7) return `${days} days ago`
16
- if (days < 14) return "last week"
17
- if (days < 30) return `${Math.floor(days / 7)} weeks ago`
18
- if (days < 60) return "last month"
19
- return `${Math.floor(days / 30)} months ago`
20
- }
package/src/domain.ts DELETED
@@ -1,139 +0,0 @@
1
- import { Schema } from "effect"
2
-
3
- export type LoadStatus = "loading" | "ready" | "error"
4
-
5
- export type PullRequestState = "open" | "closed" | "merged"
6
-
7
- export const pullRequestQueueModes = ["authored", "review", "assigned", "mentioned"] as const
8
- export type PullRequestUserQueueMode = (typeof pullRequestQueueModes)[number]
9
- export type PullRequestQueueMode = "repository" | PullRequestUserQueueMode
10
-
11
- export const pullRequestQueueLabels = {
12
- repository: "repository",
13
- authored: "authored",
14
- review: "review requested",
15
- assigned: "assigned",
16
- mentioned: "mentioned",
17
- } as const satisfies Record<PullRequestQueueMode, string>
18
-
19
- export const pullRequestQueueSearchQualifier = (mode: PullRequestQueueMode, author: string, repository: string | null) => {
20
- const qualifiers = {
21
- repository: repository ? `repo:${repository}` : `author:${author}`,
22
- authored: `author:${author}`,
23
- review: "review-requested:@me",
24
- assigned: "assignee:@me",
25
- mentioned: "mentions:@me",
26
- } as const satisfies Record<PullRequestQueueMode, string>
27
- return qualifiers[mode]
28
- }
29
-
30
- export type CheckConclusion = "success" | "failure" | "neutral" | "skipped" | "cancelled" | "timed_out"
31
-
32
- export type CheckRunStatus = "completed" | "in_progress" | "queued" | "pending"
33
-
34
- export type CheckRollupStatus = "passing" | "pending" | "failing" | "none"
35
-
36
- export type ReviewStatus = "draft" | "approved" | "changes" | "review" | "none"
37
-
38
- export type Mergeable = "mergeable" | "conflicting" | "unknown"
39
-
40
- // DiffCommentSide is the only literal type still consumed at runtime — GitHubService
41
- // uses it as a Schema inside PullRequestCommentSchema.
42
- export const DiffCommentSide = Schema.Literals(["LEFT", "RIGHT"])
43
- export type DiffCommentSide = Schema.Schema.Type<typeof DiffCommentSide>
44
-
45
- export type PullRequestMergeAction = "squash" | "auto" | "admin" | "disable-auto"
46
-
47
- export interface CheckItem {
48
- readonly name: string
49
- readonly status: CheckRunStatus
50
- readonly conclusion: CheckConclusion | null
51
- }
52
-
53
- export interface PullRequestLabel {
54
- readonly name: string
55
- readonly color: string | null
56
- }
57
-
58
- export interface CreatePullRequestCommentInput {
59
- readonly repository: string
60
- readonly number: number
61
- readonly commitId: string
62
- readonly path: string
63
- readonly line: number
64
- readonly side: DiffCommentSide
65
- readonly startLine?: number
66
- readonly startSide?: DiffCommentSide
67
- readonly body: string
68
- }
69
-
70
- export interface PullRequestReviewComment {
71
- readonly id: string
72
- readonly path: string
73
- readonly line: number
74
- readonly side: DiffCommentSide
75
- readonly author: string
76
- readonly body: string
77
- readonly createdAt: Date | null
78
- readonly url: string | null
79
- }
80
-
81
- export type PullRequestConversationItem =
82
- | {
83
- readonly _tag: "comment"
84
- readonly id: string
85
- readonly author: string
86
- readonly body: string
87
- readonly createdAt: Date | null
88
- readonly url: string | null
89
- }
90
- | ({ readonly _tag: "review-comment" } & PullRequestReviewComment)
91
-
92
- export interface PullRequestItem {
93
- readonly repository: string
94
- readonly author: string
95
- readonly headRefOid: string
96
- readonly number: number
97
- readonly title: string
98
- readonly body: string
99
- readonly labels: readonly PullRequestLabel[]
100
- readonly additions: number
101
- readonly deletions: number
102
- readonly changedFiles: number
103
- readonly state: PullRequestState
104
- readonly reviewStatus: ReviewStatus
105
- readonly checkStatus: CheckRollupStatus
106
- readonly checkSummary: string | null
107
- readonly checks: readonly CheckItem[]
108
- readonly autoMergeEnabled: boolean
109
- readonly detailLoaded: boolean
110
- readonly createdAt: Date
111
- readonly closedAt: Date | null
112
- readonly url: string
113
- }
114
-
115
- export interface PullRequestPage {
116
- readonly items: readonly PullRequestItem[]
117
- readonly endCursor: string | null
118
- readonly hasNextPage: boolean
119
- }
120
-
121
- export interface ListPullRequestPageInput {
122
- readonly mode: PullRequestQueueMode
123
- readonly repository: string | null
124
- readonly cursor: string | null
125
- readonly pageSize: number
126
- }
127
-
128
- export interface PullRequestMergeInfo {
129
- readonly repository: string
130
- readonly number: number
131
- readonly title: string
132
- readonly state: PullRequestState
133
- readonly isDraft: boolean
134
- readonly mergeable: Mergeable
135
- readonly reviewStatus: ReviewStatus
136
- readonly checkStatus: CheckRollupStatus
137
- readonly checkSummary: string | null
138
- readonly autoMergeEnabled: boolean
139
- }
package/src/errors.ts DELETED
@@ -1,10 +0,0 @@
1
- const hasStringField = <K extends string>(value: object, key: K): value is { [P in K]: string } & object =>
2
- key in value && typeof (value as Record<string, unknown>)[key] === "string"
3
-
4
- export const errorMessage = (error: unknown): string => {
5
- if (typeof error === "object" && error !== null) {
6
- if (hasStringField(error, "detail") && error.detail.length > 0) return error.detail
7
- if (hasStringField(error, "message") && error.message.length > 0) return error.message
8
- }
9
- return error instanceof Error ? error.message : String(error)
10
- }