@brimveyn/aimux 1.5.0 → 1.6.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 +14 -2
- package/package.json +5 -3
- package/src/app-runtime/side-effects.ts +41 -6
- package/src/app-runtime/use-renderer-bindings.ts +1 -1
- package/src/app.tsx +17 -5
- package/src/config.ts +29 -4
- package/src/diff-parser/clean-last-newline.ts +5 -0
- package/src/diff-parser/constants.ts +13 -0
- package/src/diff-parser/index.ts +12 -0
- package/src/diff-parser/parse-line-type.ts +29 -0
- package/src/diff-parser/parse-patch-files.ts +369 -0
- package/src/diff-parser/types.ts +75 -0
- package/src/index.tsx +2 -2
- package/src/input/modes/bridge.ts +8 -0
- package/src/input/modes/transitions.ts +2 -1
- package/src/input/modes/types.ts +4 -1
- package/src/pty/terminal-snapshot.ts +4 -4
- package/src/state/git-tree.ts +220 -0
- package/src/state/reducers/git-mode-state.ts +232 -35
- package/src/state/reducers/git-panel-state.ts +29 -3
- package/src/state/reducers/modal-state.ts +43 -10
- package/src/state/store.ts +5 -1
- package/src/state/types.ts +41 -5
- package/src/state/workspace-save.ts +2 -0
- package/src/ui/components/create-session-modal.tsx +23 -7
- package/src/ui/components/diff-renderer/build-rows.ts +349 -0
- package/src/ui/components/diff-renderer/filetype.ts +29 -0
- package/src/ui/components/diff-renderer/fold-strip.tsx +84 -0
- package/src/ui/components/diff-renderer/highlight.ts +48 -0
- package/src/ui/components/diff-renderer/index.ts +1 -0
- package/src/ui/components/diff-renderer/pierre-diff.tsx +170 -0
- package/src/ui/components/diff-renderer/split-view.tsx +207 -0
- package/src/ui/components/diff-renderer/stacked-view.tsx +166 -0
- package/src/ui/components/git-commit-modal.tsx +14 -2
- package/src/ui/components/git-pane-widget.tsx +5 -0
- package/src/ui/components/git-panel.tsx +176 -79
- package/src/ui/components/git-view.tsx +89 -87
- package/src/ui/components/help-modal.tsx +43 -11
- package/src/ui/components/input-field.tsx +2 -2
- package/src/ui/components/list-item.tsx +9 -1
- package/src/ui/components/modal-filter-bar.tsx +1 -1
- package/src/ui/components/modal-keybinds-overlay.tsx +2 -2
- package/src/ui/components/modal-shell.tsx +3 -3
- package/src/ui/components/new-tab-modal.tsx +15 -3
- package/src/ui/components/pending-chord-overlay.tsx +3 -3
- package/src/ui/components/session-bar.tsx +10 -5
- package/src/ui/components/session-picker-modal.tsx +26 -9
- package/src/ui/components/sidebar.tsx +22 -10
- package/src/ui/components/snippet-editor-modal.tsx +16 -2
- package/src/ui/components/snippet-picker-modal.tsx +11 -3
- package/src/ui/components/split-layout.tsx +1 -1
- package/src/ui/components/status-bar.tsx +12 -9
- package/src/ui/components/surface.tsx +5 -5
- package/src/ui/components/tab-item.tsx +22 -16
- package/src/ui/components/terminal-pane.tsx +25 -15
- package/src/ui/components/theme-picker-modal.tsx +111 -16
- package/src/ui/components/update-available-modal.tsx +11 -1
- package/src/ui/filter-themes.ts +10 -0
- package/src/ui/house-themes.ts +313 -0
- package/src/ui/keymap-context.ts +10 -2
- package/src/ui/root.tsx +26 -6
- package/src/ui/shiki.ts +49 -0
- package/src/ui/status-bar-model.ts +19 -1
- package/src/ui/theme.ts +22 -10
- package/src/ui/themes.generated.ts +59794 -0
- package/src/ui/themes.ts +68 -274
- package/src/ui/syntax.ts +0 -102
package/README.md
CHANGED
|
@@ -19,9 +19,11 @@ snippets, themes, and fully configurable keymaps.
|
|
|
19
19
|
- profile-isolated config, catalogs, daemon sockets, and runtime state
|
|
20
20
|
- typed keymap customization through `@brimveyn/aimux-config`
|
|
21
21
|
- snippets catalog and snippet picker
|
|
22
|
-
- configurable git pane (embedded in the sidebar or as a standalone pane) and
|
|
22
|
+
- configurable git pane (embedded in the sidebar or as a standalone pane) and a
|
|
23
|
+
dedicated [git mode](docs/guide/git-mode.md) for review / stage / commit /
|
|
24
|
+
push with a split or stacked diff view and shiki-powered highlighting
|
|
23
25
|
- built-in help generated from the resolved keymap
|
|
24
|
-
- theme picker with
|
|
26
|
+
- theme picker with 67 built-in themes (shiki catalog + aimux house themes) and a `/` filter
|
|
25
27
|
|
|
26
28
|
## Install
|
|
27
29
|
|
|
@@ -130,6 +132,7 @@ See [`docs/guide/keymaps.md`](docs/guide/keymaps.md).
|
|
|
130
132
|
- `Ctrl+S` - open snippet picker
|
|
131
133
|
- `Ctrl+T` - open theme picker
|
|
132
134
|
- `Ctrl+B` - toggle sidebar
|
|
135
|
+
- `Ctrl+D` - enter git mode
|
|
133
136
|
- `Ctrl+W b` - toggle session bar
|
|
134
137
|
- `Ctrl+W 1` through `Ctrl+W 9` - switch sessions by index
|
|
135
138
|
|
|
@@ -172,6 +175,7 @@ See [`docs/developer/architecture.md`](docs/developer/architecture.md).
|
|
|
172
175
|
- [`docs/reference/cli.md`](docs/reference/cli.md)
|
|
173
176
|
- [`docs/reference/config-reference.md`](docs/reference/config-reference.md)
|
|
174
177
|
- [`docs/reference/runtime-paths.md`](docs/reference/runtime-paths.md)
|
|
178
|
+
- [`docs/guide/git-mode.md`](docs/guide/git-mode.md)
|
|
175
179
|
- [`docs/developer/architecture.md`](docs/developer/architecture.md)
|
|
176
180
|
- [`docs/developer/aimux-config-internals.md`](docs/developer/aimux-config-internals.md)
|
|
177
181
|
|
|
@@ -191,6 +195,14 @@ bun run lint
|
|
|
191
195
|
The repository dev scripts use `AIMUX_PROFILE=dev`, so local development does
|
|
192
196
|
not collide with a globally installed `aimux` instance.
|
|
193
197
|
|
|
198
|
+
## References
|
|
199
|
+
|
|
200
|
+
- [Pierre — diffs.com](https://diffs.com) — inspiration for the git-mode diff
|
|
201
|
+
review experience: the split / stacked layout, progressive context folding,
|
|
202
|
+
and focusing on the first change rather than the top of the file.
|
|
203
|
+
- [shiki](https://shiki.style) — syntax highlighting for the git diff view
|
|
204
|
+
and the source of the bundled theme catalog.
|
|
205
|
+
|
|
194
206
|
## License
|
|
195
207
|
|
|
196
208
|
MIT © BrimVeyn
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brimveyn/aimux",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"description": "A terminal multiplexer for AI CLIs. Run Claude, Codex, OpenCode side-by-side with tabbed navigation, split panes, and persistent sessions.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -57,15 +57,17 @@
|
|
|
57
57
|
"format": "oxfmt --write .",
|
|
58
58
|
"format:check": "oxfmt --check .",
|
|
59
59
|
"knip": "knip-bun",
|
|
60
|
-
"bump": "bun run scripts/bump.ts"
|
|
60
|
+
"bump": "bun run scripts/bump.ts",
|
|
61
|
+
"generate-themes": "bun run scripts/generate-themes.ts"
|
|
61
62
|
},
|
|
62
63
|
"dependencies": {
|
|
63
|
-
"@brimveyn/aimux-config": "0.
|
|
64
|
+
"@brimveyn/aimux-config": "0.4.0",
|
|
64
65
|
"@opentui/core": "^0.1.90",
|
|
65
66
|
"@opentui/react": "^0.1.90",
|
|
66
67
|
"@xterm/headless": "^6.0.0",
|
|
67
68
|
"bun-pty": "^0.4.8",
|
|
68
69
|
"react": "^19.2.4",
|
|
70
|
+
"shiki": "^4.0.2",
|
|
69
71
|
"zustand": "^5.0.12"
|
|
70
72
|
},
|
|
71
73
|
"devDependencies": {
|
|
@@ -34,9 +34,10 @@ import {
|
|
|
34
34
|
type TabSession,
|
|
35
35
|
} from '../state/types'
|
|
36
36
|
import { saveCurrentWorkspace } from '../state/workspace-save'
|
|
37
|
+
import { filterThemeIds } from '../ui/filter-themes'
|
|
37
38
|
import { scrollGitDiff } from '../ui/git-view-controls'
|
|
38
39
|
import { applyTheme } from '../ui/theme'
|
|
39
|
-
import {
|
|
40
|
+
import { type ThemeId } from '../ui/themes'
|
|
40
41
|
import {
|
|
41
42
|
handleCreateSessionEffect,
|
|
42
43
|
handleDeleteSessionEffect,
|
|
@@ -167,16 +168,18 @@ function applyThemeEffect(
|
|
|
167
168
|
ctx: SideEffectContext
|
|
168
169
|
): void {
|
|
169
170
|
const { state } = ctx
|
|
171
|
+
const filter = state.modal.type === 'theme-picker' ? state.modal.editBuffer : null
|
|
172
|
+
const ids = filterThemeIds(filter)
|
|
170
173
|
|
|
171
174
|
switch (effect.action) {
|
|
172
175
|
case 'open':
|
|
173
|
-
applyTheme(
|
|
176
|
+
applyTheme(ctx.themeId)
|
|
174
177
|
return
|
|
175
178
|
case 'restore':
|
|
176
179
|
applyTheme(ctx.themeId)
|
|
177
180
|
return
|
|
178
181
|
case 'confirm': {
|
|
179
|
-
const selectedId =
|
|
182
|
+
const selectedId = ids[state.modal.selectedIndex]
|
|
180
183
|
if (selectedId) {
|
|
181
184
|
applyTheme(selectedId)
|
|
182
185
|
ctx.setThemeId(selectedId)
|
|
@@ -185,9 +188,9 @@ function applyThemeEffect(
|
|
|
185
188
|
return
|
|
186
189
|
}
|
|
187
190
|
case 'preview': {
|
|
188
|
-
|
|
189
|
-
const nextIndex = (state.modal.selectedIndex + effect.delta +
|
|
190
|
-
const previewId =
|
|
191
|
+
if (ids.length === 0) return
|
|
192
|
+
const nextIndex = (state.modal.selectedIndex + effect.delta + ids.length) % ids.length
|
|
193
|
+
const previewId = ids[nextIndex]
|
|
191
194
|
if (previewId) {
|
|
192
195
|
applyTheme(previewId)
|
|
193
196
|
}
|
|
@@ -457,6 +460,38 @@ export function executeSideEffect(effect: SideEffect, ctx: SideEffectContext): v
|
|
|
457
460
|
scrollGitDiff(effect.delta)
|
|
458
461
|
return
|
|
459
462
|
}
|
|
463
|
+
case 'persist-git-diff-mode-ratio': {
|
|
464
|
+
const config = loadConfig()
|
|
465
|
+
const persistedGitPane = config.gitPane
|
|
466
|
+
saveConfig({
|
|
467
|
+
...config,
|
|
468
|
+
gitPane: {
|
|
469
|
+
diffModeRatio: effect.ratio,
|
|
470
|
+
fileListMode: persistedGitPane?.fileListMode,
|
|
471
|
+
mode: persistedGitPane?.mode ?? 'embedded',
|
|
472
|
+
position: persistedGitPane?.position ?? 'bottom',
|
|
473
|
+
ratio: persistedGitPane?.ratio ?? 0.5,
|
|
474
|
+
visible: persistedGitPane?.visible ?? true,
|
|
475
|
+
},
|
|
476
|
+
})
|
|
477
|
+
return
|
|
478
|
+
}
|
|
479
|
+
case 'persist-git-file-list-mode': {
|
|
480
|
+
const config = loadConfig()
|
|
481
|
+
const persistedGitPane = config.gitPane
|
|
482
|
+
saveConfig({
|
|
483
|
+
...config,
|
|
484
|
+
gitPane: {
|
|
485
|
+
diffModeRatio: persistedGitPane?.diffModeRatio,
|
|
486
|
+
fileListMode: effect.mode,
|
|
487
|
+
mode: persistedGitPane?.mode ?? 'embedded',
|
|
488
|
+
position: persistedGitPane?.position ?? 'bottom',
|
|
489
|
+
ratio: persistedGitPane?.ratio ?? 0.5,
|
|
490
|
+
visible: persistedGitPane?.visible ?? true,
|
|
491
|
+
},
|
|
492
|
+
})
|
|
493
|
+
return
|
|
494
|
+
}
|
|
460
495
|
case 'git-stage': {
|
|
461
496
|
void enqueueGitOp(() => runGitAction(ctx, ['add', '--', effect.path], effect.path))
|
|
462
497
|
return
|
package/src/app.tsx
CHANGED
|
@@ -15,7 +15,6 @@ import type { KeyChord } from './input/keymap/key-chord'
|
|
|
15
15
|
import type { TrieBinding } from './input/keymap/trie'
|
|
16
16
|
import type { KeyResult, ModeContext, ModeId } from './input/modes/types'
|
|
17
17
|
import type { SessionBackend } from './session-backend/types'
|
|
18
|
-
import type { ThemeId } from './ui/themes'
|
|
19
18
|
|
|
20
19
|
import { executeSideEffect, type SideEffectContext } from './app-runtime/side-effects'
|
|
21
20
|
import { useBackendRuntime } from './app-runtime/use-backend-runtime'
|
|
@@ -39,6 +38,7 @@ import { appReducer, createInitialState } from './state/store'
|
|
|
39
38
|
import { KeymapContext } from './ui/keymap-context'
|
|
40
39
|
import { RootView } from './ui/root'
|
|
41
40
|
import { applyTheme } from './ui/theme'
|
|
41
|
+
import { isKnownThemeId, registerUserThemes, type ThemeId } from './ui/themes'
|
|
42
42
|
import {
|
|
43
43
|
fetchLatestNpmVersion,
|
|
44
44
|
getCurrentPackageVersion,
|
|
@@ -66,11 +66,16 @@ export function App({
|
|
|
66
66
|
const renderer = useRenderer()
|
|
67
67
|
const dimensions = useTerminalDimensions()
|
|
68
68
|
const [themeId, setThemeId] = useState<ThemeId>(() => {
|
|
69
|
+
registerUserThemes(resolvedConfig.themes)
|
|
69
70
|
const config = loadConfig()
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
71
|
+
const persisted = config.themeId && isKnownThemeId(config.themeId) ? config.themeId : undefined
|
|
72
|
+
const fromConfig =
|
|
73
|
+
resolvedConfig.theme && isKnownThemeId(resolvedConfig.theme)
|
|
74
|
+
? resolvedConfig.theme
|
|
75
|
+
: undefined
|
|
76
|
+
const initial = persisted ?? fromConfig ?? 'aimux'
|
|
77
|
+
applyTheme(initial)
|
|
78
|
+
return initial
|
|
74
79
|
})
|
|
75
80
|
const [state, dispatch] = useReducer(appReducer, undefined, () => {
|
|
76
81
|
const json = loadConfig()
|
|
@@ -81,12 +86,19 @@ export function App({
|
|
|
81
86
|
// Merge config-file gitPane (persisted prefs) with user's resolved gitPane
|
|
82
87
|
// (programmatic config). User config wins; file provides persisted prior state.
|
|
83
88
|
const userGitPane = resolvedConfig.gitPane
|
|
89
|
+
const fileListMode = userGitPane?.fileListMode ?? json.gitPane?.fileListMode ?? 'tree'
|
|
90
|
+
const diffModeRatio = userGitPane?.diffModeRatio ?? json.gitPane?.diffModeRatio ?? 0.35
|
|
84
91
|
const gitPaneOverrides = {
|
|
85
92
|
...json.gitPane,
|
|
93
|
+
diffModeRatio,
|
|
94
|
+
fileListMode,
|
|
86
95
|
...(userGitPane?.visible !== undefined ? { visible: userGitPane.visible } : {}),
|
|
87
96
|
...(userGitPane?.mode !== undefined ? { mode: userGitPane.mode } : {}),
|
|
88
97
|
...(userGitPane?.position !== undefined ? { position: userGitPane.position } : {}),
|
|
89
98
|
...(userGitPane?.ratio !== undefined ? { ratio: userGitPane.ratio } : {}),
|
|
99
|
+
...(userGitPane?.diffModeRatio !== undefined
|
|
100
|
+
? { diffModeRatio: userGitPane.diffModeRatio }
|
|
101
|
+
: {}),
|
|
90
102
|
...(userGitPane?.path !== undefined ? { path: userGitPane.path } : {}),
|
|
91
103
|
...(userGitPane?.diffCount !== undefined ? { diffCount: userGitPane.diffCount } : {}),
|
|
92
104
|
}
|
package/src/config.ts
CHANGED
|
@@ -1,15 +1,30 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
|
|
2
2
|
|
|
3
|
-
import type { SessionBarPosition, WorkspaceSnapshotV1 } from './state/types'
|
|
3
|
+
import type { GitFileListMode, SessionBarPosition, WorkspaceSnapshotV1 } from './state/types'
|
|
4
4
|
|
|
5
5
|
import { logDebug } from './debug/input-log'
|
|
6
6
|
import { getProfileConfigDir } from './profile-paths'
|
|
7
7
|
import { isWorkspaceSnapshotV1 } from './state/validation'
|
|
8
8
|
import { THEME_IDS, type ThemeId } from './ui/themes'
|
|
9
9
|
|
|
10
|
+
const LEGACY_THEME_ALIASES: Record<string, ThemeId> = {
|
|
11
|
+
'everforest': 'everforest-dark',
|
|
12
|
+
'gruvbox-dark': 'gruvbox-dark-hard',
|
|
13
|
+
'kanagawa': 'kanagawa-wave',
|
|
14
|
+
'one-dark': 'one-dark-pro',
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function migrateThemeId(value: unknown): ThemeId | undefined {
|
|
18
|
+
if (typeof value !== 'string') return undefined
|
|
19
|
+
if (THEME_IDS.includes(value as ThemeId)) return value as ThemeId
|
|
20
|
+
return LEGACY_THEME_ALIASES[value]
|
|
21
|
+
}
|
|
22
|
+
|
|
10
23
|
export const CONFIG_PATH = `${getProfileConfigDir()}/aimux.json`
|
|
11
24
|
|
|
12
25
|
export interface PersistedGitPane {
|
|
26
|
+
diffModeRatio?: number
|
|
27
|
+
fileListMode?: GitFileListMode
|
|
13
28
|
visible: boolean
|
|
14
29
|
mode: 'embedded' | 'pane'
|
|
15
30
|
position: 'top' | 'bottom' | 'left' | 'right'
|
|
@@ -38,8 +53,18 @@ function isPersistedGitPane(value: unknown): value is PersistedGitPane {
|
|
|
38
53
|
v.position === 'right'
|
|
39
54
|
const ratioOk =
|
|
40
55
|
typeof v.ratio === 'number' && Number.isFinite(v.ratio) && v.ratio > 0 && v.ratio < 1
|
|
56
|
+
const diffModeRatioOk =
|
|
57
|
+
v.diffModeRatio === undefined ||
|
|
58
|
+
(typeof v.diffModeRatio === 'number' &&
|
|
59
|
+
Number.isFinite(v.diffModeRatio) &&
|
|
60
|
+
v.diffModeRatio > 0 &&
|
|
61
|
+
v.diffModeRatio < 1)
|
|
41
62
|
const visibleOk = typeof v.visible === 'boolean'
|
|
42
|
-
|
|
63
|
+
const fileListModeOk =
|
|
64
|
+
v.fileListMode === undefined || v.fileListMode === 'tree' || v.fileListMode === 'flat'
|
|
65
|
+
if (!modeOk || !positionOk || !ratioOk || !diffModeRatioOk || !visibleOk || !fileListModeOk) {
|
|
66
|
+
return false
|
|
67
|
+
}
|
|
43
68
|
// cross-field coherence: embedded => top|bottom; pane => left|right
|
|
44
69
|
if (v.mode === 'embedded' && v.position !== 'top' && v.position !== 'bottom') return false
|
|
45
70
|
if (v.mode === 'pane' && v.position !== 'left' && v.position !== 'right') return false
|
|
@@ -58,7 +83,7 @@ export interface ConfigLoadResult {
|
|
|
58
83
|
}
|
|
59
84
|
|
|
60
85
|
function isThemeId(value: unknown): value is ThemeId {
|
|
61
|
-
return
|
|
86
|
+
return migrateThemeId(value) !== undefined
|
|
62
87
|
}
|
|
63
88
|
|
|
64
89
|
function isCustomCommandsRecord(value: unknown): value is Record<string, string> {
|
|
@@ -174,7 +199,7 @@ export function loadConfigResult(): ConfigLoadResult {
|
|
|
174
199
|
sessionBarPosition: validSessionBarPosition,
|
|
175
200
|
sessionBarVisible: validSessionBarVisible,
|
|
176
201
|
skippedUpdateVersion: validSkippedUpdateVersion,
|
|
177
|
-
themeId:
|
|
202
|
+
themeId: migrateThemeId(parsed.themeId),
|
|
178
203
|
version: 2,
|
|
179
204
|
workspaceSnapshot: isWorkspaceSnapshotV1(parsed.workspaceSnapshot)
|
|
180
205
|
? parsed.workspaceSnapshot
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Vendored from @pierre/diffs v1.1.15 — parser only. See AIMUX-14.
|
|
2
|
+
|
|
3
|
+
export const COMMIT_METADATA_SPLIT = /(?=^From [a-f0-9]+ .+$)/m
|
|
4
|
+
export const GIT_DIFF_FILE_BREAK_REGEX = /(?=^diff --git)/gm
|
|
5
|
+
export const UNIFIED_DIFF_FILE_BREAK_REGEX = /(?=^---\s+\S)/gm
|
|
6
|
+
export const FILE_CONTEXT_BLOB = /(?=^@@ )/gm
|
|
7
|
+
export const HUNK_HEADER = /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@(?: (.*))?/m
|
|
8
|
+
export const SPLIT_WITH_NEWLINES = /(?<=\n)/
|
|
9
|
+
export const FILENAME_HEADER_REGEX = /^(---|\+\+\+)\s+([^\t\r\n]+)/
|
|
10
|
+
export const FILENAME_HEADER_REGEX_GIT = /^(---|\+\+\+)\s+[ab]\/([^\t\r\n]+)/
|
|
11
|
+
export const ALTERNATE_FILE_NAMES_GIT =
|
|
12
|
+
/^diff --git (?:"a\/(.+?)"|a\/(.+?)) (?:"b\/(.+?)"|b\/(.+?))$/
|
|
13
|
+
export const INDEX_LINE_METADATA = /^index ([0-9a-f]+)\.\.([0-9a-f]+)(?: (\d+))?$/i
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export { parsePatchFiles, processFile, processPatch } from './parse-patch-files'
|
|
2
|
+
export type {
|
|
3
|
+
ChangeContent,
|
|
4
|
+
ChangeTypes,
|
|
5
|
+
ContextContent,
|
|
6
|
+
FileContents,
|
|
7
|
+
FileDiffMetadata,
|
|
8
|
+
Hunk,
|
|
9
|
+
HunkContent,
|
|
10
|
+
HunkLineType,
|
|
11
|
+
ParsedPatch,
|
|
12
|
+
} from './types'
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Vendored from @pierre/diffs v1.1.15 — parser only. See AIMUX-14.
|
|
2
|
+
/* eslint-disable no-console, no-nested-ternary */
|
|
3
|
+
|
|
4
|
+
import type { HunkLineType } from './types'
|
|
5
|
+
|
|
6
|
+
export interface ParsedLine {
|
|
7
|
+
line: string
|
|
8
|
+
type: HunkLineType
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function parseLineType(line: string): ParsedLine | undefined {
|
|
12
|
+
const firstChar = line[0]
|
|
13
|
+
if (firstChar !== '+' && firstChar !== '-' && firstChar !== ' ' && firstChar !== '\\') {
|
|
14
|
+
console.error(`parseLineType: Invalid firstChar: "${firstChar}", full line: "${line}"`)
|
|
15
|
+
return
|
|
16
|
+
}
|
|
17
|
+
const processedLine = line.substring(1)
|
|
18
|
+
return {
|
|
19
|
+
line: processedLine === '' ? '\n' : processedLine,
|
|
20
|
+
type:
|
|
21
|
+
firstChar === ' '
|
|
22
|
+
? 'context'
|
|
23
|
+
: firstChar === '\\'
|
|
24
|
+
? 'metadata'
|
|
25
|
+
: firstChar === '+'
|
|
26
|
+
? 'addition'
|
|
27
|
+
: 'deletion',
|
|
28
|
+
}
|
|
29
|
+
}
|