Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
B
bit-pm
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
燕伟桐
bit-pm
Commits
ee3d2e7f
提交
ee3d2e7f
authored
4月 21, 2025
作者:
Will Chen
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Provide a rebuild option (restart w/ re-install node modules)
上级
09b3bf3f
隐藏空白字符变更
内嵌
并排
正在显示
5 个修改的文件
包含
125 行增加
和
57 行删除
+125
-57
ChatInput.tsx
src/components/chat/ChatInput.tsx
+0
-13
PreviewPanel.tsx
src/components/preview_panel/PreviewPanel.tsx
+49
-8
useRunApp.ts
src/hooks/useRunApp.ts
+48
-33
app_handlers.ts
src/ipc/handlers/app_handlers.ts
+22
-1
ipc_client.ts
src/ipc/ipc_client.ts
+6
-2
没有找到文件。
src/components/chat/ChatInput.tsx
浏览文件 @
ee3d2e7f
...
...
@@ -283,20 +283,7 @@ export function ChatInput({ chatId }: { chatId?: number }) {
}
function
mapActionToButton
(
action
:
SuggestedAction
)
{
const
{
restartApp
}
=
useRunApp
();
switch
(
action
.
id
)
{
case
"restart-app"
:
return
(
<
Button
variant=
"outline"
size=
"sm"
className=
"rounded-xl"
key=
{
action
.
id
}
onClick=
{
restartApp
}
>
Restart app
</
Button
>
);
default
:
console
.
error
(
`Unsupported action:
${
action
.
id
}
`
);
return
(
...
...
src/components/preview_panel/PreviewPanel.tsx
浏览文件 @
ee3d2e7f
...
...
@@ -15,12 +15,23 @@ import {
ChevronUp
,
Logs
,
RefreshCw
,
MoreVertical
,
Trash2
,
Cog
,
CirclePower
,
Power
,
}
from
"lucide-react"
;
import
{
motion
}
from
"framer-motion"
;
import
{
useEffect
,
useRef
,
useState
,
useCallback
}
from
"react"
;
import
{
PanelGroup
,
Panel
,
PanelResizeHandle
}
from
"react-resizable-panels"
;
import
{
Console
}
from
"./Console"
;
import
{
useRunApp
}
from
"@/hooks/useRunApp"
;
import
{
DropdownMenu
,
DropdownMenuContent
,
DropdownMenuItem
,
DropdownMenuTrigger
,
}
from
"@/components/ui/dropdown-menu"
;
type
PreviewMode
=
"preview"
|
"code"
;
...
...
@@ -28,6 +39,7 @@ interface PreviewHeaderProps {
previewMode
:
PreviewMode
;
setPreviewMode
:
(
mode
:
PreviewMode
)
=>
void
;
onRestart
:
()
=>
void
;
onCleanRestart
:
()
=>
void
;
}
interface
ConsoleHeaderProps
{
...
...
@@ -41,6 +53,7 @@ const PreviewHeader = ({
previewMode
,
setPreviewMode
,
onRestart
,
onCleanRestart
,
}:
PreviewHeaderProps
)
=>
(
<
div
className=
"flex items-center justify-between px-4 py-2 border-b border-border"
>
<
div
className=
"relative flex space-x-2 bg-[var(--background-darkest)] rounded-md p-0.5"
>
...
...
@@ -73,14 +86,37 @@ const PreviewHeader = ({
<
span
>
Code
</
span
>
</
button
>
</
div
>
<
button
onClick=
{
onRestart
}
className=
"flex items-center space-x-1 px-3 py-1 rounded-md text-sm hover:bg-[var(--background-darkest)] transition-colors"
title=
"Restart App"
>
<
RefreshCw
size=
{
16
}
/>
<
span
>
Restart
</
span
>
</
button
>
<
div
className=
"flex items-center"
>
<
button
onClick=
{
onRestart
}
className=
"flex items-center space-x-1 px-3 py-1 rounded-md text-sm hover:bg-[var(--background-darkest)] transition-colors"
title=
"Restart App"
>
<
Power
size=
{
16
}
/>
<
span
>
Restart
</
span
>
</
button
>
<
DropdownMenu
>
<
DropdownMenuTrigger
asChild
>
<
button
className=
"flex items-center justify-center p-1.5 rounded-md text-sm hover:bg-[var(--background-darkest)] transition-colors"
title=
"More options"
>
<
MoreVertical
size=
{
16
}
/>
</
button
>
</
DropdownMenuTrigger
>
<
DropdownMenuContent
align=
"end"
className=
"w-60"
>
<
DropdownMenuItem
onClick=
{
onCleanRestart
}
>
<
Cog
size=
{
16
}
/>
<
div
className=
"flex flex-col"
>
<
span
>
Rebuild
</
span
>
<
span
className=
"text-xs text-muted-foreground"
>
Re-installs node_modules and restarts
</
span
>
</
div
>
</
DropdownMenuItem
>
</
DropdownMenuContent
>
</
DropdownMenu
>
</
div
>
</
div
>
);
...
...
@@ -126,6 +162,10 @@ export function PreviewPanel() {
restartApp
();
},
[
restartApp
]);
const
handleCleanRestart
=
useCallback
(()
=>
{
restartApp
({
removeNodeModules
:
true
});
},
[
restartApp
]);
useEffect
(()
=>
{
const
previousAppId
=
runningAppIdRef
.
current
;
...
...
@@ -176,6 +216,7 @@ export function PreviewPanel() {
previewMode=
{
previewMode
}
setPreviewMode=
{
setPreviewMode
}
onRestart=
{
handleRestart
}
onCleanRestart=
{
handleCleanRestart
}
/>
<
div
className=
"flex-1 overflow-hidden"
>
<
PanelGroup
direction=
"vertical"
>
...
...
src/hooks/useRunApp.ts
浏览文件 @
ee3d2e7f
...
...
@@ -70,41 +70,56 @@ export function useRunApp() {
}
},
[]);
const
restartApp
=
useCallback
(
async
()
=>
{
if
(
appId
===
null
)
{
return
;
}
setLoading
(
true
);
try
{
const
ipcClient
=
IpcClient
.
getInstance
();
console
.
debug
(
"Restarting app"
,
appId
);
const
restartApp
=
useCallback
(
async
({
removeNodeModules
=
false
,
}:
{
removeNodeModules
?:
boolean
}
=
{})
=>
{
if
(
appId
===
null
)
{
return
;
}
setLoading
(
true
);
try
{
const
ipcClient
=
IpcClient
.
getInstance
();
console
.
debug
(
"Restarting app"
,
appId
,
removeNodeModules
?
"with node_modules cleanup"
:
""
);
// Clear the URL and add restart message
setAppUrlObj
({
appUrl
:
null
,
appId
:
null
});
setAppOutput
((
prev
)
=>
[
...
prev
,
{
message
:
"Restarting app..."
,
type
:
"stdout"
,
appId
},
]);
// Clear the URL and add restart message
setAppUrlObj
({
appUrl
:
null
,
appId
:
null
});
setAppOutput
((
prev
)
=>
[
...
prev
,
{
message
:
"Restarting app..."
,
type
:
"stdout"
,
appId
},
]);
const
app
=
await
ipcClient
.
getApp
(
appId
);
setApp
(
app
);
await
ipcClient
.
restartApp
(
appId
,
(
output
)
=>
{
setAppOutput
((
prev
)
=>
[...
prev
,
output
]);
// Check if the output contains a localhost URL
const
urlMatch
=
output
.
message
.
match
(
/
(
https
?
:
\/\/
localhost:
\d
+
\/?)
/
);
if
(
urlMatch
)
{
setAppUrlObj
({
appUrl
:
urlMatch
[
1
],
appId
});
}
});
setError
(
null
);
}
catch
(
error
)
{
console
.
error
(
`Error restarting app
${
appId
}
:`
,
error
);
setError
(
error
instanceof
Error
?
error
:
new
Error
(
String
(
error
)));
}
finally
{
setPreviewPanelKey
((
prevKey
)
=>
prevKey
+
1
);
setLoading
(
false
);
}
},
[]);
const
app
=
await
ipcClient
.
getApp
(
appId
);
setApp
(
app
);
await
ipcClient
.
restartApp
(
appId
,
(
output
)
=>
{
setAppOutput
((
prev
)
=>
[...
prev
,
output
]);
// Check if the output contains a localhost URL
const
urlMatch
=
output
.
message
.
match
(
/
(
https
?
:
\/\/
localhost:
\d
+
\/?)
/
);
if
(
urlMatch
)
{
setAppUrlObj
({
appUrl
:
urlMatch
[
1
],
appId
});
}
},
removeNodeModules
);
setError
(
null
);
}
catch
(
error
)
{
console
.
error
(
`Error restarting app
${
appId
}
:`
,
error
);
setError
(
error
instanceof
Error
?
error
:
new
Error
(
String
(
error
)));
}
finally
{
setPreviewPanelKey
((
prevKey
)
=>
prevKey
+
1
);
setLoading
(
false
);
}
},
[
appId
,
setApp
,
setAppOutput
,
setAppUrlObj
,
setError
,
setPreviewPanelKey
]
);
const
refreshAppIframe
=
useCallback
(
async
()
=>
{
setPreviewPanelKey
((
prevKey
)
=>
prevKey
+
1
);
...
...
src/ipc/handlers/app_handlers.ts
浏览文件 @
ee3d2e7f
...
...
@@ -378,7 +378,10 @@ export function registerAppHandlers() {
"restart-app"
,
async
(
event
:
Electron
.
IpcMainInvokeEvent
,
{
appId
}:
{
appId
:
number
}
{
appId
,
removeNodeModules
,
}:
{
appId
:
number
;
removeNodeModules
?:
boolean
}
)
=>
{
logger
.
log
(
`Restarting app
${
appId
}
`
);
return
withLock
(
appId
,
async
()
=>
{
...
...
@@ -410,6 +413,24 @@ export function registerAppHandlers() {
}
const
appPath
=
getDyadAppPath
(
app
.
path
);
// Remove node_modules if requested
if
(
removeNodeModules
)
{
const
nodeModulesPath
=
path
.
join
(
appPath
,
"node_modules"
);
logger
.
log
(
`Removing node_modules for app
${
appId
}
at
${
nodeModulesPath
}
`
);
if
(
fs
.
existsSync
(
nodeModulesPath
))
{
await
fsPromises
.
rm
(
nodeModulesPath
,
{
recursive
:
true
,
force
:
true
,
});
logger
.
log
(
`Successfully removed node_modules for app
${
appId
}
`
);
}
else
{
logger
.
log
(
`No node_modules directory found for app
${
appId
}
`
);
}
}
logger
.
debug
(
`Executing app
${
appId
}
in path
${
app
.
path
}
after restart request`
);
// Adjusted log
...
...
src/ipc/ipc_client.ts
浏览文件 @
ee3d2e7f
...
...
@@ -317,10 +317,14 @@ export class IpcClient {
// Restart a running app
public
async
restartApp
(
appId
:
number
,
onOutput
:
(
output
:
AppOutput
)
=>
void
onOutput
:
(
output
:
AppOutput
)
=>
void
,
removeNodeModules
?:
boolean
):
Promise
<
{
success
:
boolean
}
>
{
try
{
const
result
=
await
this
.
ipcRenderer
.
invoke
(
"restart-app"
,
{
appId
});
const
result
=
await
this
.
ipcRenderer
.
invoke
(
"restart-app"
,
{
appId
,
removeNodeModules
,
});
this
.
appStreams
.
set
(
appId
,
{
onOutput
});
return
result
;
}
catch
(
error
)
{
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论