@brimveyn/aimux 1.8.0 → 1.9.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.
Files changed (80) hide show
  1. package/package.json +2 -2
  2. package/src/app-runtime/side-effects.ts +6 -1
  3. package/src/app.tsx +20 -16
  4. package/src/input/keymap/describe-bindings.ts +63 -2
  5. package/src/input/modes/bridge.ts +1 -0
  6. package/src/input/modes/transitions.ts +3 -1
  7. package/src/input/modes/types.ts +1 -0
  8. package/src/pty/terminal-snapshot.ts +7 -5
  9. package/src/services/ai-usage/adapters/claude.ts +102 -26
  10. package/src/services/ai-usage/adapters/codex.ts +83 -32
  11. package/src/services/ai-usage/cache.ts +60 -0
  12. package/src/services/ai-usage/pace.ts +79 -0
  13. package/src/services/ai-usage/provider.ts +43 -20
  14. package/src/services/ai-usage/types.ts +31 -0
  15. package/src/state/ai-usage-store.ts +10 -3
  16. package/src/state/reducers/git-mode-state.ts +58 -0
  17. package/src/state/reducers/modal-state.ts +13 -0
  18. package/src/state/types.ts +18 -6
  19. package/src/ui/breaking-update-screen.tsx +2 -2
  20. package/src/ui/components/git/diff-renderer/build-rows.ts +467 -0
  21. package/src/ui/components/{diff-renderer → git/diff-renderer}/fold-strip.tsx +1 -1
  22. package/src/ui/components/git/diff-renderer/highlight.ts +102 -0
  23. package/src/ui/components/{diff-renderer → git/diff-renderer}/pierre-diff.tsx +23 -15
  24. package/src/ui/components/git/diff-renderer/prepare-diff.ts +62 -0
  25. package/src/ui/components/{diff-renderer → git/diff-renderer}/split-view.tsx +122 -47
  26. package/src/ui/components/{diff-renderer → git/diff-renderer}/stacked-view.tsx +106 -35
  27. package/src/ui/components/{diff-renderer → git/diff-renderer}/use-diff-prefetch.ts +8 -17
  28. package/src/ui/components/git/diff-renderer/use-diff-preparation.ts +240 -0
  29. package/src/ui/components/git/diff-renderer/use-segment-virtualization.ts +95 -0
  30. package/src/ui/components/{git-panel.tsx → git/git-panel.tsx} +9 -5
  31. package/src/ui/components/{git-view.tsx → git/git-view.tsx} +70 -37
  32. package/src/ui/components/{git-pane-context-menu.ts → git/pane/git-pane-context-menu.ts} +2 -2
  33. package/src/ui/components/{git-pane-widget.tsx → git/pane/git-pane-widget.tsx} +4 -4
  34. package/src/ui/components/{session-bar.tsx → layout/session-bar.tsx} +36 -13
  35. package/src/ui/components/{sidebar-group-metadata.ts → layout/sidebar/sidebar-group-metadata.ts} +2 -2
  36. package/src/ui/components/{sidebar.tsx → layout/sidebar/sidebar.tsx} +6 -6
  37. package/src/ui/components/{tab-item.tsx → layout/sidebar/tab-item.tsx} +5 -5
  38. package/src/ui/components/{use-sidebar-branch.ts → layout/sidebar/use-sidebar-branch.ts} +1 -1
  39. package/src/ui/components/{split-layout.tsx → layout/split-layout.tsx} +5 -5
  40. package/src/ui/components/{status-bar.tsx → layout/status-bar.tsx} +7 -7
  41. package/src/ui/components/{terminal-pane.tsx → layout/terminal-pane.tsx} +8 -8
  42. package/src/ui/components/modals/app/ai-usage-modal.tsx +190 -0
  43. package/src/ui/components/{help-modal.tsx → modals/app/help-modal.tsx} +6 -6
  44. package/src/ui/components/{update-available-modal.tsx → modals/app/update-available-modal.tsx} +4 -4
  45. package/src/ui/components/{git-commit-modal.tsx → modals/git/git-commit-modal.tsx} +8 -8
  46. package/src/ui/components/modals/sessions/create-session-modal.tsx +79 -0
  47. package/src/ui/components/modals/sessions/session-name-modal.tsx +10 -0
  48. package/src/ui/components/{session-picker-modal.tsx → modals/sessions/session-picker-modal.tsx} +8 -8
  49. package/src/ui/components/modals/shared/form.tsx +164 -0
  50. package/src/ui/components/{modal-keybinds-overlay.tsx → modals/shared/modal-keybinds-overlay.tsx} +7 -7
  51. package/src/ui/components/{modal-shell.tsx → modals/shared/modal-shell.tsx} +1 -1
  52. package/src/ui/components/{picker.tsx → modals/shared/picker.tsx} +3 -3
  53. package/src/ui/components/modals/snippets/snippet-editor-modal.tsx +30 -0
  54. package/src/ui/components/{snippet-picker-modal.tsx → modals/snippets/snippet-picker-modal.tsx} +6 -6
  55. package/src/ui/components/{new-tab-modal.tsx → modals/tabs/new-tab-modal.tsx} +17 -20
  56. package/src/ui/components/{theme-picker-modal.tsx → modals/themes/theme-picker-modal.tsx} +6 -6
  57. package/src/ui/components/{ai-usage-indicator.tsx → overlays/ai-usage/ai-usage-indicator.tsx} +56 -24
  58. package/src/ui/components/{context-menu-box.tsx → overlays/context-menu/context-menu-box.tsx} +1 -1
  59. package/src/ui/components/{context-menu-overlay.tsx → overlays/context-menu/context-menu-overlay.tsx} +3 -3
  60. package/src/ui/components/{pending-chord-overlay.tsx → overlays/pending-chord-overlay.tsx} +6 -6
  61. package/src/ui/components/{bare-input.tsx → primitives/bare-input.tsx} +2 -2
  62. package/src/ui/components/{input-field.tsx → primitives/input-field.tsx} +2 -2
  63. package/src/ui/components/{list-item.tsx → primitives/list-item.tsx} +1 -1
  64. package/src/ui/components/{surface.tsx → primitives/surface.tsx} +1 -1
  65. package/src/ui/root.tsx +26 -23
  66. package/src/ui/theme-store.ts +30 -26
  67. package/src/ui/themes.ts +1 -0
  68. package/src/ui/ai-usage/controller.ts +0 -35
  69. package/src/ui/components/ai-usage-popover.tsx +0 -152
  70. package/src/ui/components/create-session-modal.tsx +0 -96
  71. package/src/ui/components/diff-renderer/build-rows.ts +0 -349
  72. package/src/ui/components/diff-renderer/highlight.ts +0 -44
  73. package/src/ui/components/diff-renderer/prepare-diff.ts +0 -65
  74. package/src/ui/components/diff-renderer/use-diff-preparation.ts +0 -106
  75. package/src/ui/components/session-name-modal.tsx +0 -11
  76. package/src/ui/components/snippet-editor-modal.tsx +0 -40
  77. /package/src/ui/components/{diff-renderer → git/diff-renderer}/filetype.ts +0 -0
  78. /package/src/ui/components/{diff-renderer → git/diff-renderer}/index.ts +0 -0
  79. /package/src/ui/components/{sidebar-scroll.ts → layout/sidebar/sidebar-scroll.ts} +0 -0
  80. /package/src/ui/components/{use-sidebar-auto-scroll.ts → layout/sidebar/use-sidebar-auto-scroll.ts} +0 -0
@@ -1,106 +0,0 @@
1
- import type { ThemedToken } from 'shiki'
2
-
3
- import { useEffect, useMemo, useState } from 'react'
4
-
5
- import type { FileDiffMetadata } from '../../../diff-parser'
6
-
7
- import { useAppStore } from '../../../state/app-store'
8
- import { dispatchGlobal } from '../../../state/dispatch-ref'
9
- import { getHighlightsTokens, getParsedFile } from '../../../state/types'
10
- import { prepareDiff } from './prepare-diff'
11
-
12
- export interface DiffPreparation {
13
- file: FileDiffMetadata | null
14
- highlights: { add: ThemedToken[][]; del: ThemedToken[][] }
15
- /** True while prepare is running for this cache key + hash. */
16
- preparing: boolean
17
- /** Available once either cache or prepare has produced a valid hash. */
18
- ready: boolean
19
- }
20
-
21
- const EMPTY_HIGHLIGHTS = { add: [] as ThemedToken[][], del: [] as ThemedToken[][] }
22
-
23
- // Consults the parsed + highlight caches first, then falls back to an off-thread
24
- // prepareDiff that dispatches results into the store. Deduplicates in-flight
25
- // preparation per cacheKey+hash by hashing the diff string once.
26
- export function useDiffPreparation(
27
- cacheKey: string,
28
- diff: string,
29
- path: string,
30
- themeId: string
31
- ): DiffPreparation {
32
- const cachedParsed = useAppStore((s) => s.gitMode.parsedFiles[cacheKey])
33
- const highlightKey = `${cacheKey}|${themeId}`
34
- const cachedHighlights = useAppStore((s) => s.gitMode.highlights[highlightKey])
35
-
36
- const [preparing, setPreparing] = useState(false)
37
- const [localFile, setLocalFile] = useState<FileDiffMetadata | null>(null)
38
- const [localHighlights, setLocalHighlights] = useState(EMPTY_HIGHLIGHTS)
39
- const [localHash, setLocalHash] = useState<string | null>(null)
40
-
41
- const { file, highlights, ready } = useMemo(() => {
42
- const parsedFile = cachedParsed ? getParsedFile(cachedParsed) : null
43
- const parsedMatches = !!cachedParsed
44
- const tokens = cachedHighlights ? getHighlightsTokens(cachedHighlights) : null
45
- const hlMatches = !!cachedHighlights && cachedHighlights.hash === cachedParsed?.hash
46
-
47
- if (parsedMatches && hlMatches && tokens) {
48
- return { file: parsedFile, highlights: tokens, ready: true }
49
- }
50
- if (parsedMatches && !hlMatches) {
51
- return {
52
- file: parsedFile,
53
- highlights: tokens ?? EMPTY_HIGHLIGHTS,
54
- ready: true,
55
- }
56
- }
57
- if (localHash && localFile) {
58
- return { file: localFile, highlights: localHighlights, ready: true }
59
- }
60
- return { file: null, highlights: EMPTY_HIGHLIGHTS, ready: false }
61
- }, [cachedParsed, cachedHighlights, localFile, localHighlights, localHash])
62
-
63
- useEffect(() => {
64
- // If caches already cover both parsed and highlights for this theme, bail.
65
- const parsedOk = !!cachedParsed
66
- const hlOk = !!cachedHighlights && cachedParsed && cachedHighlights.hash === cachedParsed.hash
67
- if (parsedOk && hlOk) return
68
-
69
- const controller = new AbortController()
70
- setPreparing(true)
71
- void prepareDiff(diff, path, { signal: controller.signal })
72
- .then((result) => {
73
- if (controller.signal.aborted) return
74
- setLocalFile(result.file)
75
- setLocalHighlights(result.highlights)
76
- setLocalHash(result.hash)
77
- dispatchGlobal({
78
- file: result.file,
79
- hash: result.hash,
80
- key: cacheKey,
81
- type: 'git-mode-set-parsed',
82
- })
83
- dispatchGlobal({
84
- add: result.highlights.add,
85
- del: result.highlights.del,
86
- hash: result.hash,
87
- key: cacheKey,
88
- themeId,
89
- type: 'git-mode-set-highlights',
90
- })
91
- })
92
- .catch((err: unknown) => {
93
- if (err instanceof Error && err.name === 'AbortError') return
94
- // Swallow — the UI falls back to "(could not parse diff)" when file stays null.
95
- })
96
- .finally(() => {
97
- if (!controller.signal.aborted) setPreparing(false)
98
- })
99
-
100
- return () => {
101
- controller.abort()
102
- }
103
- }, [cacheKey, diff, path, themeId, cachedParsed, cachedHighlights])
104
-
105
- return { file, highlights, preparing, ready }
106
- }
@@ -1,11 +0,0 @@
1
- import { uiTokens } from '../ui-tokens'
2
- import { InputField } from './input-field'
3
- import { ModalShell } from './modal-shell'
4
-
5
- export function SessionNameModal({ title, value }: { title: string; value: string }) {
6
- return (
7
- <ModalShell title={title} keybindsModeId="modal.session-name" width={uiTokens.modalWidth.md}>
8
- <InputField active value={value} />
9
- </ModalShell>
10
- )
11
- }
@@ -1,40 +0,0 @@
1
- import { useTokens } from '../theme'
2
- import { uiTokens } from '../ui-tokens'
3
- import { InputField } from './input-field'
4
- import { ModalShell } from './modal-shell'
5
-
6
- interface SnippetEditorModalProps {
7
- activeField: 'name' | 'content'
8
- snippetName: string
9
- snippetContent: string
10
- isEditing: boolean
11
- }
12
-
13
- export function SnippetEditorModal({
14
- activeField,
15
- isEditing,
16
- snippetContent,
17
- snippetName,
18
- }: SnippetEditorModalProps) {
19
- const t = useTokens()
20
- const nameActive = activeField === 'name'
21
- const contentActive = activeField === 'content'
22
-
23
- return (
24
- <ModalShell
25
- title={isEditing ? 'Edit snippet' : 'Create snippet'}
26
- keybindsModeId="modal.snippet-editor"
27
- width={uiTokens.modalWidth.xl}
28
- >
29
- <box flexDirection="column">
30
- <text fg={nameActive ? t.palette.ink : t.muted}>Name</text>
31
- <InputField active={nameActive} value={snippetName} />
32
- </box>
33
-
34
- <box flexDirection="column">
35
- <text fg={contentActive ? t.palette.ink : t.muted}>Content</text>
36
- <InputField active={contentActive} value={snippetContent} />
37
- </box>
38
- </ModalShell>
39
- )
40
- }