Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
B
bit-pm
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
燕伟桐
bit-pm
Commits
4e0f93d2
提交
4e0f93d2
authored
4月 18, 2025
作者:
Will Chen
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Load (canned) proposal from IPC
上级
7aec3ef4
隐藏空白字符变更
内嵌
并排
正在显示
8 个修改的文件
包含
191 行增加
和
45 行删除
+191
-45
ChatInput.tsx
src/components/chat/ChatInput.tsx
+61
-45
schema.ts
src/db/schema.ts
+3
-0
useProposal.ts
src/hooks/useProposal.ts
+44
-0
proposal_handlers.ts
src/ipc/handlers/proposal_handlers.ts
+47
-0
ipc_client.ts
src/ipc/ipc_client.ts
+13
-0
ipc_host.ts
src/ipc/ipc_host.ts
+2
-0
schemas.ts
src/lib/schemas.ts
+20
-0
preload.ts
src/preload.ts
+1
-0
没有找到文件。
src/components/chat/ChatInput.tsx
浏览文件 @
4e0f93d2
...
@@ -23,6 +23,15 @@ import { useLoadApp } from "@/hooks/useLoadApp";
...
@@ -23,6 +23,15 @@ import { useLoadApp } from "@/hooks/useLoadApp";
import
{
Button
}
from
"@/components/ui/button"
;
import
{
Button
}
from
"@/components/ui/button"
;
import
{
Label
}
from
"@/components/ui/label"
;
import
{
Label
}
from
"@/components/ui/label"
;
import
{
Switch
}
from
"@/components/ui/switch"
;
import
{
Switch
}
from
"@/components/ui/switch"
;
import
{
useProposal
}
from
"@/hooks/useProposal"
;
import
{
Proposal
}
from
"@/lib/schemas"
;
interface
ChatInputActionsProps
{
proposal
:
Proposal
;
onApprove
:
()
=>
void
;
onReject
:
()
=>
void
;
isApprovable
:
boolean
;
// Can be used to enable/disable buttons
}
interface
ChatInputProps
{
interface
ChatInputProps
{
chatId
?:
number
;
chatId
?:
number
;
...
@@ -38,6 +47,13 @@ export function ChatInput({ chatId, onSubmit }: ChatInputProps) {
...
@@ -38,6 +47,13 @@ export function ChatInput({ chatId, onSubmit }: ChatInputProps) {
const
[
selectedAppId
]
=
useAtom
(
selectedAppIdAtom
);
const
[
selectedAppId
]
=
useAtom
(
selectedAppIdAtom
);
const
[
showError
,
setShowError
]
=
useState
(
true
);
const
[
showError
,
setShowError
]
=
useState
(
true
);
// Use the hook to fetch the proposal
const
{
proposal
,
isLoading
:
isProposalLoading
,
error
:
proposalError
,
}
=
useProposal
(
chatId
);
const
adjustHeight
=
()
=>
{
const
adjustHeight
=
()
=>
{
const
textarea
=
textareaRef
.
current
;
const
textarea
=
textareaRef
.
current
;
if
(
textarea
)
{
if
(
textarea
)
{
...
@@ -86,6 +102,16 @@ export function ChatInput({ chatId, onSubmit }: ChatInputProps) {
...
@@ -86,6 +102,16 @@ export function ChatInput({ chatId, onSubmit }: ChatInputProps) {
setShowError
(
false
);
setShowError
(
false
);
};
};
const
handleApprove
=
()
=>
{
console
.
log
(
"Approve clicked"
);
// Add approve logic here
};
const
handleReject
=
()
=>
{
console
.
log
(
"Reject clicked"
);
// Add reject logic here
};
if
(
!
settings
)
{
if
(
!
settings
)
{
return
null
;
// Or loading state
return
null
;
// Or loading state
}
}
...
@@ -105,9 +131,28 @@ export function ChatInput({ chatId, onSubmit }: ChatInputProps) {
...
@@ -105,9 +131,28 @@ export function ChatInput({ chatId, onSubmit }: ChatInputProps) {
</
div
>
</
div
>
</
div
>
</
div
>
)
}
)
}
{
/* Display loading or error state for proposal */
}
{
isProposalLoading
&&
(
<
div
className=
"p-4 text-sm text-muted-foreground"
>
Loading proposal...
</
div
>
)
}
{
proposalError
&&
(
<
div
className=
"p-4 text-sm text-red-600"
>
Error loading proposal:
{
proposalError
}
</
div
>
)
}
<
div
className=
"p-4"
>
<
div
className=
"p-4"
>
<
div
className=
"flex flex-col space-y-2 border border-border rounded-lg bg-(--background-lighter) shadow-sm"
>
<
div
className=
"flex flex-col space-y-2 border border-border rounded-lg bg-(--background-lighter) shadow-sm"
>
<
ChatInputActions
/>
{
/* Only render ChatInputActions if proposal is loaded */
}
{
proposal
&&
(
<
ChatInputActions
proposal=
{
proposal
}
onApprove=
{
handleApprove
}
onReject=
{
handleReject
}
isApprovable=
{
!
isProposalLoading
&&
!!
proposal
}
/>
)
}
<
div
className=
"flex items-start space-x-2 "
>
<
div
className=
"flex items-start space-x-2 "
>
<
textarea
<
textarea
ref=
{
textareaRef
}
ref=
{
textareaRef
}
...
@@ -151,47 +196,16 @@ export function ChatInput({ chatId, onSubmit }: ChatInputProps) {
...
@@ -151,47 +196,16 @@ export function ChatInput({ chatId, onSubmit }: ChatInputProps) {
);
);
}
}
function
ChatInputActions
()
{
// Update ChatInputActions to accept props
function
ChatInputActions
({
proposal
,
onApprove
,
onReject
,
isApprovable
,
}:
ChatInputActionsProps
)
{
const
[
autoApprove
,
setAutoApprove
]
=
useState
(
false
);
const
[
autoApprove
,
setAutoApprove
]
=
useState
(
false
);
const
[
isDetailsVisible
,
setIsDetailsVisible
]
=
useState
(
false
);
const
[
isDetailsVisible
,
setIsDetailsVisible
]
=
useState
(
false
);
const
handleApprove
=
()
=>
{
console
.
log
(
"Approve clicked"
);
// Add approve logic here
};
const
handleReject
=
()
=>
{
console
.
log
(
"Reject clicked"
);
// Add reject logic here
};
// Placeholder data
const
securityRisks
=
[
{
type
:
"warning"
,
title
:
"Potential XSS Vulnerability"
,
description
:
"User input is directly rendered without sanitization."
,
},
{
type
:
"danger"
,
title
:
"Hardcoded API Key"
,
description
:
"API key found in plain text in configuration file."
,
},
];
const
filesChanged
=
[
{
name
:
"ChatInput.tsx"
,
path
:
"src/components/chat/ChatInput.tsx"
,
summary
:
"Added review actions and details section."
,
},
{
name
:
"api.ts"
,
path
:
"src/lib/api.ts"
,
summary
:
"Refactored API call structure."
,
},
];
return
(
return
(
<
div
className=
"border-b border-border"
>
<
div
className=
"border-b border-border"
>
<
div
className=
"p-2"
>
<
div
className=
"p-2"
>
...
@@ -206,9 +220,9 @@ function ChatInputActions() {
...
@@ -206,9 +220,9 @@ function ChatInputActions() {
)
:
(
)
:
(
<
ChevronDown
size=
{
16
}
className=
"mr-1"
/>
<
ChevronDown
size=
{
16
}
className=
"mr-1"
/>
)
}
)
}
Review:
foo bar changes
Review:
{
proposal
.
title
}
</
button
>
</
button
>
{
securityRisks
.
length
>
0
&&
(
{
proposal
.
securityRisks
.
length
>
0
&&
(
<
span
className=
"bg-red-100 text-red-700 text-xs font-medium px-2 py-0.5 rounded-full"
>
<
span
className=
"bg-red-100 text-red-700 text-xs font-medium px-2 py-0.5 rounded-full"
>
Security risks found
Security risks found
</
span
>
</
span
>
...
@@ -221,7 +235,8 @@ function ChatInputActions() {
...
@@ -221,7 +235,8 @@ function ChatInputActions() {
className=
"px-8"
className=
"px-8"
size=
"sm"
size=
"sm"
variant=
"outline"
variant=
"outline"
onClick=
{
handleApprove
}
onClick=
{
onApprove
}
disabled=
{
!
isApprovable
}
>
>
<
Check
size=
{
16
}
className=
"mr-1"
/>
<
Check
size=
{
16
}
className=
"mr-1"
/>
Approve
Approve
...
@@ -230,7 +245,8 @@ function ChatInputActions() {
...
@@ -230,7 +245,8 @@ function ChatInputActions() {
className=
"px-8"
className=
"px-8"
size=
"sm"
size=
"sm"
variant=
"outline"
variant=
"outline"
onClick=
{
handleReject
}
onClick=
{
onReject
}
disabled=
{
!
isApprovable
}
>
>
<
X
size=
{
16
}
className=
"mr-1"
/>
<
X
size=
{
16
}
className=
"mr-1"
/>
Reject
Reject
...
@@ -256,7 +272,7 @@ function ChatInputActions() {
...
@@ -256,7 +272,7 @@ function ChatInputActions() {
<
div
className=
"mb-3"
>
<
div
className=
"mb-3"
>
<
h4
className=
"font-semibold mb-1"
>
Security Risks
</
h4
>
<
h4
className=
"font-semibold mb-1"
>
Security Risks
</
h4
>
<
ul
className=
"space-y-1"
>
<
ul
className=
"space-y-1"
>
{
securityRisks
.
map
((
risk
,
index
)
=>
(
{
proposal
.
securityRisks
.
map
((
risk
,
index
)
=>
(
<
li
key=
{
index
}
className=
"flex items-start space-x-2"
>
<
li
key=
{
index
}
className=
"flex items-start space-x-2"
>
{
risk
.
type
===
"warning"
?
(
{
risk
.
type
===
"warning"
?
(
<
AlertTriangle
<
AlertTriangle
...
@@ -281,7 +297,7 @@ function ChatInputActions() {
...
@@ -281,7 +297,7 @@ function ChatInputActions() {
<
div
>
<
div
>
<
h4
className=
"font-semibold mb-1"
>
Files Changed
</
h4
>
<
h4
className=
"font-semibold mb-1"
>
Files Changed
</
h4
>
<
ul
className=
"space-y-1"
>
<
ul
className=
"space-y-1"
>
{
filesChanged
.
map
((
file
,
index
)
=>
(
{
proposal
.
filesChanged
.
map
((
file
,
index
)
=>
(
<
li
key=
{
index
}
className=
"flex items-center space-x-2"
>
<
li
key=
{
index
}
className=
"flex items-center space-x-2"
>
<
FileText
<
FileText
size=
{
16
}
size=
{
16
}
...
...
src/db/schema.ts
浏览文件 @
4e0f93d2
...
@@ -34,6 +34,9 @@ export const messages = sqliteTable("messages", {
...
@@ -34,6 +34,9 @@ export const messages = sqliteTable("messages", {
.
references
(()
=>
chats
.
id
,
{
onDelete
:
"cascade"
}),
.
references
(()
=>
chats
.
id
,
{
onDelete
:
"cascade"
}),
role
:
text
(
"role"
,
{
enum
:
[
"user"
,
"assistant"
]
}).
notNull
(),
role
:
text
(
"role"
,
{
enum
:
[
"user"
,
"assistant"
]
}).
notNull
(),
content
:
text
(
"content"
).
notNull
(),
content
:
text
(
"content"
).
notNull
(),
approvalState
:
text
(
"approval_state"
,
{
enum
:
[
"approved"
,
"rejected"
,
"pending"
],
}),
createdAt
:
integer
(
"created_at"
,
{
mode
:
"timestamp"
})
createdAt
:
integer
(
"created_at"
,
{
mode
:
"timestamp"
})
.
notNull
()
.
notNull
()
.
default
(
sql
`(unixepoch())`
),
.
default
(
sql
`(unixepoch())`
),
...
...
src/hooks/useProposal.ts
0 → 100644
浏览文件 @
4e0f93d2
import
{
useState
,
useEffect
}
from
"react"
;
import
{
IpcClient
}
from
"@/ipc/ipc_client"
;
import
type
{
Proposal
}
from
"@/lib/schemas"
;
// Import Proposal type
export
function
useProposal
(
chatId
:
number
|
undefined
)
{
const
[
proposal
,
setProposal
]
=
useState
<
Proposal
|
null
>
(
null
);
const
[
isLoading
,
setIsLoading
]
=
useState
<
boolean
>
(
false
);
const
[
error
,
setError
]
=
useState
<
string
|
null
>
(
null
);
useEffect
(()
=>
{
if
(
chatId
===
undefined
)
{
setProposal
(
null
);
setIsLoading
(
false
);
setError
(
null
);
return
;
}
const
fetchProposal
=
async
()
=>
{
setIsLoading
(
true
);
setError
(
null
);
try
{
const
fetchedProposal
=
await
IpcClient
.
getInstance
().
getProposal
(
chatId
);
setProposal
(
fetchedProposal
);
}
catch
(
err
:
any
)
{
console
.
error
(
"Error fetching proposal:"
,
err
);
setError
(
err
.
message
||
"Failed to fetch proposal"
);
setProposal
(
null
);
// Clear proposal on error
}
finally
{
setIsLoading
(
false
);
}
};
fetchProposal
();
// Cleanup function if needed (e.g., for aborting requests)
// return () => {
// // Abort logic here
// };
},
[
chatId
]);
// Re-run effect if chatId changes
return
{
proposal
,
isLoading
,
error
};
}
src/ipc/handlers/proposal_handlers.ts
0 → 100644
浏览文件 @
4e0f93d2
import
{
ipcMain
,
type
IpcMainInvokeEvent
}
from
"electron"
;
import
type
{
Proposal
}
from
"@/lib/schemas"
;
// Placeholder Proposal data
const
placeholderProposal
:
Proposal
=
{
title
:
"Review: Example Refactoring (from IPC)"
,
securityRisks
:
[
{
type
:
"warning"
,
title
:
"Potential XSS Vulnerability"
,
description
:
"User input is directly rendered without sanitization."
,
},
{
type
:
"danger"
,
title
:
"Hardcoded API Key"
,
description
:
"API key found in plain text in configuration file."
,
},
],
filesChanged
:
[
{
name
:
"ChatInput.tsx"
,
path
:
"src/components/chat/ChatInput.tsx"
,
summary
:
"Added review actions and details section."
,
},
{
name
:
"api.ts"
,
path
:
"src/lib/api.ts"
,
summary
:
"Refactored API call structure."
,
},
],
};
const
getProposalHandler
=
async
(
_event
:
IpcMainInvokeEvent
,
{
chatId
}:
{
chatId
:
number
}
):
Promise
<
Proposal
>
=>
{
console
.
log
(
`IPC: get-proposal called for chatId:
${
chatId
}
`
);
// Simulate async operation
await
new
Promise
((
resolve
)
=>
setTimeout
(
resolve
,
500
));
// 500ms delay
return
placeholderProposal
;
};
// Function to register proposal-related handlers
export
function
registerProposalHandlers
()
{
ipcMain
.
handle
(
"get-proposal"
,
getProposalHandler
);
console
.
log
(
"Registered proposal IPC handlers"
);
}
src/ipc/ipc_client.ts
浏览文件 @
4e0f93d2
...
@@ -17,6 +17,7 @@ import type {
...
@@ -17,6 +17,7 @@ import type {
NodeSystemInfo
,
NodeSystemInfo
,
Version
,
Version
,
}
from
"./ipc_types"
;
}
from
"./ipc_types"
;
import
type
{
Proposal
}
from
"@/lib/schemas"
;
import
{
showError
}
from
"@/lib/toast"
;
import
{
showError
}
from
"@/lib/toast"
;
export
interface
ChatStreamCallbacks
{
export
interface
ChatStreamCallbacks
{
...
@@ -614,6 +615,18 @@ export class IpcClient {
...
@@ -614,6 +615,18 @@ export class IpcClient {
}
}
}
}
// Get proposal details
public
async
getProposal
(
chatId
:
number
):
Promise
<
Proposal
>
{
try
{
const
data
=
await
this
.
ipcRenderer
.
invoke
(
"get-proposal"
,
{
chatId
});
// Assuming the main process returns data matching the Proposal interface
return
data
as
Proposal
;
}
catch
(
error
)
{
showError
(
error
);
throw
error
;
}
}
// Example methods for listening to events (if needed)
// Example methods for listening to events (if needed)
// public on(channel: string, func: (...args: any[]) => void): void {
// public on(channel: string, func: (...args: any[]) => void): void {
}
}
src/ipc/ipc_host.ts
浏览文件 @
4e0f93d2
...
@@ -6,6 +6,7 @@ import { registerShellHandlers } from "./handlers/shell_handler";
...
@@ -6,6 +6,7 @@ import { registerShellHandlers } from "./handlers/shell_handler";
import
{
registerDependencyHandlers
}
from
"./handlers/dependency_handlers"
;
import
{
registerDependencyHandlers
}
from
"./handlers/dependency_handlers"
;
import
{
registerGithubHandlers
}
from
"./handlers/github_handlers"
;
import
{
registerGithubHandlers
}
from
"./handlers/github_handlers"
;
import
{
registerNodeHandlers
}
from
"./handlers/node_handlers"
;
import
{
registerNodeHandlers
}
from
"./handlers/node_handlers"
;
import
{
registerProposalHandlers
}
from
"./handlers/proposal_handlers"
;
export
function
registerIpcHandlers
()
{
export
function
registerIpcHandlers
()
{
// Register all IPC handlers by category
// Register all IPC handlers by category
...
@@ -17,4 +18,5 @@ export function registerIpcHandlers() {
...
@@ -17,4 +18,5 @@ export function registerIpcHandlers() {
registerDependencyHandlers
();
registerDependencyHandlers
();
registerGithubHandlers
();
registerGithubHandlers
();
registerNodeHandlers
();
registerNodeHandlers
();
registerProposalHandlers
();
}
}
src/lib/schemas.ts
浏览文件 @
4e0f93d2
...
@@ -97,3 +97,23 @@ export const UserSettingsSchema = z.object({
...
@@ -97,3 +97,23 @@ export const UserSettingsSchema = z.object({
* Type derived from the UserSettingsSchema
* Type derived from the UserSettingsSchema
*/
*/
export
type
UserSettings
=
z
.
infer
<
typeof
UserSettingsSchema
>
;
export
type
UserSettings
=
z
.
infer
<
typeof
UserSettingsSchema
>
;
// Define interfaces for the props
export
interface
SecurityRisk
{
type
:
"warning"
|
"danger"
;
title
:
string
;
description
:
string
;
}
export
interface
FileChange
{
name
:
string
;
path
:
string
;
summary
:
string
;
}
// New Proposal interface
export
interface
Proposal
{
title
:
string
;
securityRisks
:
SecurityRisk
[];
filesChanged
:
FileChange
[];
}
src/preload.ts
浏览文件 @
4e0f93d2
...
@@ -38,6 +38,7 @@ const validInvokeChannels = [
...
@@ -38,6 +38,7 @@ const validInvokeChannels = [
"github:push"
,
"github:push"
,
"get-app-version"
,
"get-app-version"
,
"reload-env-path"
,
"reload-env-path"
,
"get-proposal"
,
]
as
const
;
]
as
const
;
// Add valid receive channels
// Add valid receive channels
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论