Skip to content

Commit 786af26

Browse files
committed
Bump version to 1.1.16
1 parent 3187a8e commit 786af26

File tree

11 files changed

+288
-84
lines changed

11 files changed

+288
-84
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "vbcdr",
3-
"version": "1.1.15",
3+
"version": "1.1.16",
44
"description": "Desktop vibe coding environment for Claude Code developers",
55
"author": {
66
"name": "jo vinkenroye",

src/main/index.ts

Lines changed: 55 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,41 @@ const configuredSessions = new WeakSet<Electron.Session>()
4646

4747
let mainWindow: BrowserWindow | null = null
4848

49+
function handleBeforeInput(_event: Electron.Event, input: Electron.Input): void {
50+
if (input.type !== 'keyDown') return
51+
52+
if (input.key === 'r' && input.meta && !input.shift) {
53+
_event.preventDefault()
54+
mainWindow?.webContents.send('browser:reload')
55+
return
56+
}
57+
58+
if (input.meta && input.alt && /^[1-9]$/.test(input.key)) {
59+
_event.preventDefault()
60+
mainWindow?.webContents.send('menu:action', `switch-project-${input.key}`)
61+
return
62+
}
63+
64+
if (input.meta && input.shift && input.key === '[') {
65+
_event.preventDefault()
66+
mainWindow?.webContents.send('menu:action', 'terminal-tab-prev')
67+
return
68+
}
69+
70+
if (input.meta && input.shift && input.key === ']') {
71+
_event.preventDefault()
72+
mainWindow?.webContents.send('menu:action', 'terminal-tab-next')
73+
return
74+
}
75+
76+
77+
if (input.meta && input.alt && input.key === 'w') {
78+
_event.preventDefault()
79+
mainWindow?.webContents.send('menu:action', 'close-file-tab')
80+
return
81+
}
82+
}
83+
4984
function createWindow(): void {
5085
mainWindow = new BrowserWindow({
5186
width: 1400,
@@ -62,12 +97,7 @@ function createWindow(): void {
6297
}
6398
})
6499

65-
mainWindow.webContents.on('before-input-event', (_event, input) => {
66-
if (input.type === 'keyDown' && input.key === 'r' && input.meta && !input.shift) {
67-
_event.preventDefault()
68-
mainWindow?.webContents.send('browser:reload')
69-
}
70-
})
100+
mainWindow.webContents.on('before-input-event', handleBeforeInput)
71101

72102
if (process.env.ELECTRON_RENDERER_URL) {
73103
mainWindow.loadURL(process.env.ELECTRON_RENDERER_URL)
@@ -129,6 +159,12 @@ function buildMenu(): Electron.MenuItemConstructorOptions[] {
129159
click: () => mainWindow?.webContents.send('menu:action', 'close-project')
130160
},
131161
{ type: 'separator' },
162+
{
163+
label: 'Save',
164+
accelerator: 'CmdOrCtrl+S',
165+
click: () => mainWindow?.webContents.send('menu:action', 'save-file')
166+
},
167+
{ type: 'separator' },
132168
{
133169
label: 'Close Window',
134170
accelerator: 'CmdOrCtrl+Shift+W',
@@ -153,6 +189,12 @@ function buildMenu(): Electron.MenuItemConstructorOptions[] {
153189
const viewMenu: Electron.MenuItemConstructorOptions = {
154190
label: 'View',
155191
submenu: [
192+
{
193+
label: 'Dashboard',
194+
accelerator: 'CmdOrCtrl+D',
195+
click: () => mainWindow?.webContents.send('menu:action', 'toggle-dashboard')
196+
},
197+
{ type: 'separator' },
156198
{
157199
label: 'Toggle Browser',
158200
accelerator: 'CmdOrCtrl+1',
@@ -204,6 +246,12 @@ function buildMenu(): Electron.MenuItemConstructorOptions[] {
204246
}
205247
},
206248
{ type: 'separator' },
249+
{
250+
label: 'Toggle Light/Dark',
251+
accelerator: 'CmdOrCtrl+Shift+L',
252+
click: () => mainWindow?.webContents.send('menu:action', 'toggle-variant')
253+
},
254+
{ type: 'separator' },
207255
{ role: 'togglefullscreen' }
208256
]
209257
}
@@ -288,12 +336,7 @@ app.whenReady().then(() => {
288336
}
289337
})
290338

291-
contents.on('before-input-event', (_e, input) => {
292-
if (input.type === 'keyDown' && input.key === 'r' && input.meta && !input.shift) {
293-
_e.preventDefault()
294-
mainWindow?.webContents.send('browser:reload')
295-
}
296-
})
339+
contents.on('before-input-event', handleBeforeInput)
297340
}
298341
})
299342

src/renderer/App.tsx

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { ConflictBanner } from '@/components/git/ConflictBanner'
66
import { useThemeStore } from '@/stores/theme-store'
77
import { useProjectStore } from '@/stores/project-store'
88
import { useEditorStore } from '@/stores/editor-store'
9+
import { useTerminalStore } from '@/stores/terminal-store'
910
import { useLayoutStore } from '@/stores/layout-store'
1011
import { useUpdaterStore } from '@/stores/updater-store'
1112
import { useGitStore } from '@/stores/git-store'
@@ -44,6 +45,9 @@ export function App(): React.ReactElement {
4445
const layoutStore = useLayoutStore.getState()
4546
const activeId = projectStore.activeProjectId
4647

48+
const terminalStore = useTerminalStore.getState()
49+
const themeStore = useThemeStore.getState()
50+
4751
switch (action) {
4852
case 'new-project':
4953
projectStore.addProject()
@@ -63,16 +67,61 @@ export function App(): React.ReactElement {
6367
case 'center-tab-claude':
6468
if (activeId) editorStore.setCenterTab(activeId, 'claude')
6569
break
70+
case 'toggle-dashboard':
71+
if (projectStore.dashboardActive && activeId) {
72+
projectStore.setActiveProject(activeId)
73+
} else {
74+
projectStore.showDashboard()
75+
}
76+
break
77+
case 'toggle-variant':
78+
themeStore.toggleVariant()
79+
break
80+
case 'save-file': {
81+
if (!activeId) break
82+
const filePath = editorStore.statePerProject[activeId]?.activeFilePath
83+
if (filePath) editorStore.saveFile(activeId, filePath)
84+
break
85+
}
86+
case 'close-file-tab': {
87+
if (!activeId) break
88+
const fp = editorStore.statePerProject[activeId]?.activeFilePath
89+
if (fp) editorStore.closeFile(activeId, fp)
90+
break
91+
}
92+
case 'terminal-tab-prev':
93+
case 'terminal-tab-next': {
94+
if (!activeId) break
95+
const tabs = terminalStore.tabs.filter((t) => t.projectId === activeId)
96+
if (tabs.length < 2) break
97+
const currentTabId = terminalStore.activeTabPerProject[activeId]
98+
const idx = tabs.findIndex((t) => t.id === currentTabId)
99+
const next =
100+
action === 'terminal-tab-next'
101+
? (idx + 1) % tabs.length
102+
: (idx - 1 + tabs.length) % tabs.length
103+
terminalStore.setActiveTab(activeId, tabs[next].id)
104+
break
105+
}
106+
default: {
107+
const m = action.match(/^switch-project-(\d)$/)
108+
if (m) {
109+
const i = parseInt(m[1]) - 1
110+
if (i < projectStore.projects.length) {
111+
projectStore.setActiveProject(projectStore.projects[i].id)
112+
}
113+
}
114+
break
115+
}
66116
}
67117
})
68118
}, [])
69119

70120
return (
71-
<>
121+
<div className="flex h-screen flex-col overflow-hidden">
72122
<UpdateBanner />
73-
74123
<ConflictBanner />
75124
<AppLayoutGrid />
76-
</>
125+
</div>
77126
)
78127
}

src/renderer/components/browser/DevTerminalsPanel.tsx

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,17 @@ export function DevTerminalsPanel(): React.ReactElement {
6868

6969
return (
7070
<div className="flex h-full flex-col">
71+
<div className="flex items-center border-b border-zinc-800 bg-zinc-900/50 px-1">
72+
<div className="flex-1" />
73+
<button
74+
onClick={handleAdd}
75+
disabled={projectTabs.length >= 6}
76+
className="rounded p-1.5 text-zinc-500 hover:bg-zinc-800 hover:text-zinc-300 disabled:opacity-30"
77+
title="New terminal"
78+
>
79+
<Plus size={14} />
80+
</button>
81+
</div>
7182
<div className="relative flex-1" style={{ minHeight: 0 }}>
7283
{projectIds.map((pid) => {
7384
const pTabs = tabs.filter((t) => t.projectId === pid)
@@ -130,16 +141,6 @@ export function DevTerminalsPanel(): React.ReactElement {
130141
)
131142
})}
132143
</div>
133-
<div className="flex items-center border-t border-zinc-800 bg-zinc-900/50 px-2 py-0.5">
134-
<button
135-
onClick={handleAdd}
136-
disabled={projectTabs.length >= 6}
137-
className="flex items-center gap-1 rounded px-1.5 py-0.5 text-[10px] text-zinc-500 hover:bg-zinc-800 hover:text-zinc-300 disabled:opacity-30"
138-
>
139-
<Plus size={10} />
140-
Add Terminal
141-
</button>
142-
</div>
143144
</div>
144145
)
145146
}

src/renderer/components/dashboard/Dashboard.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export function Dashboard(): React.ReactElement {
4040
</button>
4141
</div>
4242
) : (
43-
<div className="grid gap-4" style={{ gridTemplateColumns: 'repeat(auto-fill, minmax(600px, 1fr))' }}>
43+
<div className="grid gap-2" style={{ gridTemplateColumns: 'repeat(auto-fill, minmax(600px, 1fr))' }}>
4444
{projects.map((project) => (
4545
<ProjectCard key={project.id} project={project} />
4646
))}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { useEffect, useRef } from 'react'
2+
import { getTerminalInstance } from '@/components/terminal/TerminalInstance'
3+
4+
interface DashboardTerminalProps {
5+
tabId: string
6+
}
7+
8+
export function DashboardTerminal({ tabId }: DashboardTerminalProps): React.ReactElement {
9+
const containerRef = useRef<HTMLDivElement>(null)
10+
11+
useEffect(() => {
12+
const container = containerRef.current
13+
if (!container) return
14+
15+
const entry = getTerminalInstance(tabId)
16+
if (!entry) return
17+
18+
const termEl = entry.terminal.element
19+
if (!termEl) return
20+
21+
const originalParent = termEl.parentElement
22+
23+
container.appendChild(termEl)
24+
entry.terminal.scrollToBottom()
25+
26+
const termWidth = termEl.offsetWidth
27+
const termHeight = termEl.offsetHeight
28+
const containerWidth = container.clientWidth
29+
const containerHeight = container.clientHeight
30+
31+
if (termWidth > 0 && termHeight > 0 && containerWidth > 0 && containerHeight > 0) {
32+
const scaleX = containerWidth / termWidth
33+
const scaleY = containerHeight / termHeight
34+
const scale = Math.min(scaleX, scaleY)
35+
termEl.style.transform = `scale(${scale})`
36+
termEl.style.transformOrigin = 'top left'
37+
}
38+
39+
return () => {
40+
termEl.style.transform = ''
41+
termEl.style.transformOrigin = ''
42+
if (originalParent) {
43+
originalParent.appendChild(termEl)
44+
}
45+
setTimeout(() => {
46+
entry.fitAddon.fit()
47+
entry.terminal.refresh(0, entry.terminal.rows - 1)
48+
}, 50)
49+
}
50+
}, [tabId])
51+
52+
return <div ref={containerRef} className="h-full w-full overflow-hidden rounded bg-zinc-950" />
53+
}

0 commit comments

Comments
 (0)