Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
B
bit-pm
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
燕伟桐
bit-pm
Commits
f2b157b8
提交
f2b157b8
authored
4月 18, 2025
作者:
Will Chen
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Simplified setup flow: user installs node.js; pnpm is configured
上级
ffdf3e1c
显示空白字符变更
内嵌
并排
正在显示
6 个修改的文件
包含
100 行增加
和
199 行删除
+100
-199
SetupBanner.tsx
src/components/SetupBanner.tsx
+61
-68
node_handlers.ts
src/ipc/handlers/node_handlers.ts
+27
-102
ipc_client.ts
src/ipc/ipc_client.ts
+6
-15
ipc_types.ts
src/ipc/ipc_types.ts
+5
-10
home.tsx
src/pages/home.tsx
+0
-4
preload.ts
src/preload.ts
+1
-0
没有找到文件。
src/components/SetupBanner.tsx
浏览文件 @
f2b157b8
...
@@ -20,29 +20,26 @@ import {
...
@@ -20,29 +20,26 @@ import {
}
from
"@/components/ui/accordion"
;
}
from
"@/components/ui/accordion"
;
import
{
Button
}
from
"@/components/ui/button"
;
import
{
Button
}
from
"@/components/ui/button"
;
import
{
cn
}
from
"@/lib/utils"
;
import
{
cn
}
from
"@/lib/utils"
;
import
{
showError
}
from
"@/lib/toast"
;
import
{
NodeSystemInfo
}
from
"@/ipc/ipc_types"
;
export
function
SetupBanner
()
{
export
function
SetupBanner
()
{
const
navigate
=
useNavigate
();
const
navigate
=
useNavigate
();
const
{
isAnyProviderSetup
}
=
useSettings
();
const
{
isAnyProviderSetup
,
loading
}
=
useSettings
();
const
[
nodeVersion
,
setNodeVersion
]
=
useState
<
string
|
null
>
(
null
);
const
[
nodeSystemInfo
,
setNodeSystemInfo
]
=
useState
<
NodeSystemInfo
|
null
>
(
const
[
pnpmVersion
,
setPnpmVersion
]
=
useState
<
string
|
null
>
(
null
);
null
);
const
[
nodeCheckError
,
setNodeCheckError
]
=
useState
<
boolean
>
(
false
);
const
[
nodeCheckError
,
setNodeCheckError
]
=
useState
<
boolean
>
(
false
);
const
[
nodeInstallError
,
setNodeInstallError
]
=
useState
<
string
|
null
>
(
null
);
const
[
nodeInstallLoading
,
setNodeInstallLoading
]
=
useState
<
boolean
>
(
false
);
const
[
nodeInstallLoading
,
setNodeInstallLoading
]
=
useState
<
boolean
>
(
false
);
const
checkNode
=
useCallback
(
async
()
=>
{
const
checkNode
=
useCallback
(
async
()
=>
{
try
{
try
{
setNodeCheckError
(
false
);
setNodeCheckError
(
false
);
const
status
=
await
IpcClient
.
getInstance
().
getNodejsStatus
();
const
status
=
await
IpcClient
.
getInstance
().
getNodejsStatus
();
setNodeVersion
(
status
.
nodeVersion
);
setNodeSystemInfo
(
status
);
setPnpmVersion
(
status
.
pnpmVersion
);
}
catch
(
error
)
{
}
catch
(
error
)
{
console
.
error
(
"Failed to check Node.js status:"
,
error
);
console
.
error
(
"Failed to check Node.js status:"
,
error
);
setNodeVersion
(
null
);
setNodeSystemInfo
(
null
);
setPnpmVersion
(
null
);
setNodeCheckError
(
true
);
setNodeCheckError
(
true
);
}
}
},
[
setNode
Version
,
setNodeCheckError
]);
},
[
setNode
SystemInfo
,
setNodeCheckError
]);
useEffect
(()
=>
{
useEffect
(()
=>
{
checkNode
();
checkNode
();
...
@@ -57,39 +54,35 @@ export function SetupBanner() {
...
@@ -57,39 +54,35 @@ export function SetupBanner() {
const
handleNodeInstallClick
=
async
()
=>
{
const
handleNodeInstallClick
=
async
()
=>
{
setNodeInstallLoading
(
true
);
setNodeInstallLoading
(
true
);
try
{
IpcClient
.
getInstance
().
openExternalUrl
(
nodeSystemInfo
!
.
nodeDownloadUrl
);
const
result
=
await
IpcClient
.
getInstance
().
installNode
();
if
(
!
result
.
success
)
{
showError
(
result
.
errorMessage
);
setNodeInstallError
(
result
.
errorMessage
||
"Unknown error"
);
}
else
{
setNodeVersion
(
result
.
nodeVersion
);
setPnpmVersion
(
result
.
pnpmVersion
);
}
}
catch
(
error
)
{
showError
(
"Failed to install Node.js. "
+
(
error
as
Error
).
message
);
setNodeInstallError
(
"Failed to install Node.js. "
+
(
error
as
Error
).
message
);
}
finally
{
setNodeInstallLoading
(
false
);
}
};
};
const
isNodeSetupComplete
=
!!
nodeVersion
&&
!!
pnpmVersion
;
const
finishNodeInstallAndRestart
=
()
=>
{
const
isAiProviderSetup
=
isAnyProviderSetup
();
IpcClient
.
getInstance
().
reloadDyad
();
};
const
isNodeSetupComplete
=
Boolean
(
nodeSystemInfo
?.
nodeVersion
&&
nodeSystemInfo
?.
pnpmVersion
);
const
itemsNeedAction
:
string
[]
=
[];
const
itemsNeedAction
:
string
[]
=
[];
if
(
!
isNodeSetupComplete
)
itemsNeedAction
.
push
(
"node-setup"
);
if
(
!
isNodeSetupComplete
&&
nodeSystemInfo
)
{
if
(
isNodeSetupComplete
&&
!
isAiProviderSetup
)
itemsNeedAction
.
push
(
"node-setup"
);
}
if
(
!
isAnyProviderSetup
()
&&
!
loading
)
{
itemsNeedAction
.
push
(
"ai-setup"
);
itemsNeedAction
.
push
(
"ai-setup"
);
}
if
(
itemsNeedAction
.
length
===
0
)
{
if
(
itemsNeedAction
.
length
===
0
)
{
return
null
;
return
(
<
h1
className=
"text-6xl font-bold mb-8 bg-clip-text text-transparent bg-gradient-to-r from-gray-900 to-gray-600 dark:from-gray-100 dark:to-gray-400 tracking-tight"
>
Build your dream app
</
h1
>
);
}
}
const
bannerClasses
=
cn
(
const
bannerClasses
=
cn
(
"w-full mb-
8
border rounded-xl shadow-sm overflow-hidden"
,
"w-full mb-
6
border rounded-xl shadow-sm overflow-hidden"
,
"border-zinc-200 dark:border-zinc-700"
"border-zinc-200 dark:border-zinc-700"
);
);
...
@@ -105,6 +98,10 @@ export function SetupBanner() {
...
@@ -105,6 +98,10 @@ export function SetupBanner() {
};
};
return
(
return
(
<>
<
p
className=
"text-xl text-zinc-700 dark:text-zinc-300 p-4"
>
Follow these steps and you'll be ready to start building with Dyad...
</
p
>
<
div
className=
{
bannerClasses
}
>
<
div
className=
{
bannerClasses
}
>
<
Accordion
<
Accordion
type=
"multiple"
type=
"multiple"
...
@@ -126,45 +123,42 @@ export function SetupBanner() {
...
@@ -126,45 +123,42 @@ export function SetupBanner() {
<
div
className=
"flex items-center gap-3"
>
<
div
className=
"flex items-center gap-3"
>
{
getStatusIcon
(
isNodeSetupComplete
,
nodeCheckError
)
}
{
getStatusIcon
(
isNodeSetupComplete
,
nodeCheckError
)
}
<
span
className=
"font-medium text-sm"
>
<
span
className=
"font-medium text-sm"
>
1. Check Node.js Runtime
1. Install Node.js
</
span
>
</
span
>
</
div
>
</
div
>
</
div
>
</
div
>
</
AccordionTrigger
>
</
AccordionTrigger
>
<
AccordionContent
className=
"px-4 pt-2 pb-4 bg-white dark:bg-zinc-900 border-t border-inherit"
>
<
AccordionContent
className=
"px-4 pt-2 pb-4 bg-white dark:bg-zinc-900 border-t border-inherit"
>
{
nodeInstall
Error
&&
(
{
nodeCheck
Error
&&
(
<
p
className=
"text-sm text-red-600 dark:text-red-400"
>
<
p
className=
"text-sm text-red-600 dark:text-red-400"
>
{
nodeInstallError
}
Error checking Node.js status. Try installing Node.js.
</
p
>
</
p
>
)
}
)
}
{
nodeCheckError
?
(
{
isNodeSetupComplete
?
(
<
p
className=
"text-sm text-red-600 dark:text-red-400"
>
<
p
className=
"text-sm"
>
Error checking Node.js status. Please ensure node.js are
Node.js (
{
nodeSystemInfo
!
.
nodeVersion
}
) installed.
{
" "
}
installed correctly and accessible in your system's PATH.
{
nodeSystemInfo
!
.
pnpmVersion
&&
(
<
span
className=
"text-xs text-gray-500"
>
pnpm (
{
nodeSystemInfo
!
.
pnpmVersion
}
) installed.
</
span
>
)
}
</
p
>
</
p
>
)
:
isNodeSetupComplete
?
(
<
p
className=
"text-sm"
>
Node.js (
{
nodeVersion
}
) installed.
</
p
>
)
:
(
)
:
(
<
div
>
<
div
className=
"text-sm"
>
<
p
className=
"text-sm mb-3"
>
<
p
>
Node.js is required to run apps locally.
</
p
>
Node.js is required to run apps locally. We also use pnpm as
<
p
className=
"mb-3"
>
our package manager as it's faster and more efficient than
After you have installed node.js, click "Finish setup" to
npm
.
restart Dyad
.
</
p
>
</
p
>
<
Button
size=
"sm"
onClick=
{
handleNodeInstallClick
}
disabled=
{
nodeInstallLoading
}
>
{
nodeInstallLoading
?
(
{
nodeInstallLoading
?
(
<>
<
Button
onClick=
{
finishNodeInstallAndRestart
}
>
<
Loader2
className=
"mr-2 h-4 w-4 animate-spin"
/>
Finish setup and restart Dyad
Installing...
</
Button
>
</>
)
:
(
)
:
(
"Install Node.js Runtime"
<
Button
onClick=
{
handleNodeInstallClick
}
>
)
}
Install Node.js Runtime
</
Button
>
</
Button
>
)
}
</
div
>
</
div
>
)
}
)
}
</
AccordionContent
>
</
AccordionContent
>
...
@@ -172,24 +166,20 @@ export function SetupBanner() {
...
@@ -172,24 +166,20 @@ export function SetupBanner() {
<
AccordionItem
<
AccordionItem
value=
"ai-setup"
value=
"ai-setup"
disabled=
{
!
isNodeSetupComplete
}
className=
{
cn
(
className=
{
cn
(
isAiProviderSetup
isAnyProviderSetup
()
?
"bg-green-50 dark:bg-green-900/30"
?
"bg-green-50 dark:bg-green-900/30"
:
"bg-yellow-50 dark:bg-yellow-900/30"
,
:
"bg-yellow-50 dark:bg-yellow-900/30"
!
isNodeSetupComplete
?
"opacity-60"
:
""
)
}
)
}
>
>
<
AccordionTrigger
<
AccordionTrigger
className=
{
cn
(
className=
{
cn
(
"px-4 py-3 transition-colors w-full hover:no-underline"
,
"px-4 py-3 transition-colors w-full hover:no-underline"
!
isNodeSetupComplete
?
"cursor-not-allowed"
:
""
)
}
)
}
onClick=
{
(
e
)
=>
!
isNodeSetupComplete
&&
e
.
preventDefault
()
}
>
>
<
div
className=
"flex items-center justify-between w-full"
>
<
div
className=
"flex items-center justify-between w-full"
>
<
div
className=
"flex items-center gap-3"
>
<
div
className=
"flex items-center gap-3"
>
{
getStatusIcon
(
isAiProviderSetup
)
}
{
getStatusIcon
(
isAnyProviderSetup
()
)
}
<
span
className=
"font-medium text-sm"
>
<
span
className=
"font-medium text-sm"
>
2. Setup AI Model Access
2. Setup AI Model Access
</
span
>
</
span
>
...
@@ -206,7 +196,9 @@ export function SetupBanner() {
...
@@ -206,7 +196,9 @@ export function SetupBanner() {
role=
"button"
role=
"button"
tabIndex=
{
isNodeSetupComplete
?
0
:
-
1
}
tabIndex=
{
isNodeSetupComplete
?
0
:
-
1
}
onKeyDown=
{
(
e
)
=>
onKeyDown=
{
(
e
)
=>
isNodeSetupComplete
&&
e
.
key
===
"Enter"
&&
handleAiSetupClick
()
isNodeSetupComplete
&&
e
.
key
===
"Enter"
&&
handleAiSetupClick
()
}
}
>
>
<
div
className=
"flex items-center justify-between gap-3"
>
<
div
className=
"flex items-center justify-between gap-3"
>
...
@@ -231,5 +223,6 @@ export function SetupBanner() {
...
@@ -231,5 +223,6 @@ export function SetupBanner() {
</
AccordionItem
>
</
AccordionItem
>
</
Accordion
>
</
Accordion
>
</
div
>
</
div
>
</>
);
);
}
}
src/ipc/handlers/node_handlers.ts
浏览文件 @
f2b157b8
import
{
ipcMain
}
from
"electron"
;
import
{
ipcMain
,
app
}
from
"electron"
;
import
{
spawn
}
from
"child_process"
;
import
{
spawn
}
from
"child_process"
;
import
{
platform
}
from
"os"
;
import
{
platform
,
arch
}
from
"os"
;
import
{
InstallNodeResult
}
from
"../ipc_types"
;
import
{
NodeSystemInfo
}
from
"../ipc_types"
;
type
ShellResult
=
|
{
success
:
true
;
output
:
string
;
}
|
{
success
:
false
;
errorMessage
:
string
;
};
function
runShell
(
command
:
string
):
Promise
<
ShellResult
>
{
return
new
Promise
((
resolve
)
=>
{
let
stdout
=
""
;
let
stderr
=
""
;
const
process
=
spawn
(
command
,
{
shell
:
true
,
stdio
:
[
"ignore"
,
"pipe"
,
"pipe"
],
// ignore stdin, pipe stdout/stderr
});
process
.
stdout
?.
on
(
"data"
,
(
data
)
=>
{
stdout
+=
data
.
toString
();
});
process
.
stderr
?.
on
(
"data"
,
(
data
)
=>
{
stderr
+=
data
.
toString
();
});
process
.
on
(
"error"
,
(
error
)
=>
{
console
.
error
(
`Error executing command "
${
command
}
":`
,
error
.
message
);
resolve
({
success
:
false
,
errorMessage
:
error
.
message
});
});
process
.
on
(
"close"
,
(
code
)
=>
{
if
(
code
===
0
)
{
resolve
({
success
:
true
,
output
:
stdout
.
trim
()
});
}
else
{
const
errorMessage
=
stderr
.
trim
()
||
`Command failed with code
${
code
}
`
;
console
.
error
(
`Command "
${
command
}
" failed with code
${
code
}
:
${
stderr
.
trim
()}
`
);
resolve
({
success
:
false
,
errorMessage
});
}
});
});
}
function
checkCommandExists
(
command
:
string
):
Promise
<
string
|
null
>
{
function
checkCommandExists
(
command
:
string
):
Promise
<
string
|
null
>
{
return
new
Promise
((
resolve
)
=>
{
return
new
Promise
((
resolve
)
=>
{
let
output
=
""
;
let
output
=
""
;
const
process
=
spawn
(
command
,
[
"--version"
],
{
const
process
=
spawn
(
command
,
{
shell
:
true
,
shell
:
true
,
stdio
:
[
"ignore"
,
"pipe"
,
"pipe"
],
// ignore stdin, pipe stdout/stderr
stdio
:
[
"ignore"
,
"pipe"
,
"pipe"
],
// ignore stdin, pipe stdout/stderr
});
});
...
@@ -87,64 +41,35 @@ function checkCommandExists(command: string): Promise<string | null> {
...
@@ -87,64 +41,35 @@ function checkCommandExists(command: string): Promise<string | null> {
}
}
export
function
registerNodeHandlers
()
{
export
function
registerNodeHandlers
()
{
ipcMain
.
handle
(
ipcMain
.
handle
(
"nodejs-status"
,
async
():
Promise
<
NodeSystemInfo
>
=>
{
"nodejs-status"
,
async
():
Promise
<
{
nodeVersion
:
string
|
null
;
pnpmVersion
:
string
|
null
;
}
>
=>
{
// Run checks in parallel
// Run checks in parallel
const
[
nodeVersion
,
pnpmVersion
]
=
await
Promise
.
all
([
const
[
nodeVersion
,
pnpmVersion
]
=
await
Promise
.
all
([
checkCommandExists
(
"node"
),
checkCommandExists
(
"node --version"
),
checkCommandExists
(
"pnpm"
),
// First, check if pnpm is installed.
// If not, try to install it using corepack.
// If both fail, then pnpm is not available.
checkCommandExists
(
"pnpm --version || corepack enable pnpm && pnpm --version"
),
]);
]);
return
{
nodeVersion
,
pnpmVersion
};
// Default to mac download url.
}
let
nodeDownloadUrl
=
"https://nodejs.org/dist/v22.14.0/node-v22.14.0.pkg"
;
);
if
(
platform
()
==
"win32"
)
{
if
(
arch
()
===
"arm64"
||
arch
()
===
"arm"
)
{
ipcMain
.
handle
(
"install-node"
,
async
():
Promise
<
InstallNodeResult
>
=>
{
nodeDownloadUrl
=
console
.
log
(
"Installing Node.js..."
);
"https://nodejs.org/dist/v22.14.0/node-v22.14.0-arm64.msi"
;
if
(
platform
()
===
"win32"
)
{
let
result
=
await
runShell
(
"winget install Volta.Volta"
);
if
(
!
result
.
success
)
{
return
{
success
:
false
,
errorMessage
:
result
.
errorMessage
};
}
}
else
{
}
else
{
let
result
=
await
runShell
(
"curl https://get.volta.sh | bash"
);
// x64 is the most common architecture for Windows so it's the
if
(
!
result
.
success
)
{
// default download url.
return
{
success
:
false
,
errorMessage
:
result
.
errorMessage
};
nodeDownloadUrl
=
}
"https://nodejs.org/dist/v22.14.0/node-v22.14.0-x64.msi"
;
}
console
.
log
(
"Installed Volta"
);
process
.
env
.
PATH
=
[
"~/.volta/bin"
,
process
.
env
.
PATH
].
join
(
":"
);
console
.
log
(
"Updated PATH"
);
let
result
=
await
runShell
(
"volta install node"
);
if
(
!
result
.
success
)
{
return
{
success
:
false
,
errorMessage
:
result
.
errorMessage
};
}
console
.
log
(
"Installed Node.js (via Volta)"
);
result
=
await
runShell
(
"node --version"
);
if
(
!
result
.
success
)
{
return
{
success
:
false
,
errorMessage
:
result
.
errorMessage
};
}
}
const
nodeVersion
=
result
.
output
.
trim
();
console
.
log
(
"Node.js is setup with version"
,
nodeVersion
);
result
=
await
runShell
(
"corepack enable pnpm"
);
if
(
!
result
.
success
)
{
return
{
success
:
false
,
errorMessage
:
result
.
errorMessage
};
}
}
console
.
log
(
"Enabled pnpm"
);
return
{
nodeVersion
,
pnpmVersion
,
nodeDownloadUrl
};
});
result
=
await
runShell
(
"pnpm --version"
);
if
(
!
result
.
success
)
{
return
{
success
:
false
,
errorMessage
:
result
.
errorMessage
};
}
const
pnpmVersion
=
result
.
output
.
trim
();
console
.
log
(
"pnpm is setup with version"
,
pnpmVersion
);
return
{
success
:
true
,
nodeVersion
,
pnpmVersion
};
ipcMain
.
handle
(
"reload-dyad"
,
async
():
Promise
<
void
>
=>
{
app
.
relaunch
();
app
.
exit
(
0
);
});
});
}
}
src/ipc/ipc_client.ts
浏览文件 @
f2b157b8
...
@@ -15,6 +15,7 @@ import type {
...
@@ -15,6 +15,7 @@ import type {
CreateAppResult
,
CreateAppResult
,
InstallNodeResult
,
InstallNodeResult
,
ListAppsResponse
,
ListAppsResponse
,
NodeSystemInfo
,
SandboxConfig
,
SandboxConfig
,
Version
,
Version
,
}
from
"./ipc_types"
;
}
from
"./ipc_types"
;
...
@@ -132,6 +133,10 @@ export class IpcClient {
...
@@ -132,6 +133,10 @@ export class IpcClient {
return
IpcClient
.
instance
;
return
IpcClient
.
instance
;
}
}
public
async
reloadDyad
():
Promise
<
void
>
{
await
this
.
ipcRenderer
.
invoke
(
"reload-dyad"
);
}
// Create a new app with an initial chat
// Create a new app with an initial chat
public
async
createApp
(
params
:
CreateAppParams
):
Promise
<
CreateAppResult
>
{
public
async
createApp
(
params
:
CreateAppParams
):
Promise
<
CreateAppResult
>
{
try
{
try
{
...
@@ -493,10 +498,7 @@ export class IpcClient {
...
@@ -493,10 +498,7 @@ export class IpcClient {
}
}
// Check Node.js and npm status
// Check Node.js and npm status
public
async
getNodejsStatus
():
Promise
<
{
public
async
getNodejsStatus
():
Promise
<
NodeSystemInfo
>
{
nodeVersion
:
string
|
null
;
pnpmVersion
:
string
|
null
;
}
>
{
try
{
try
{
const
result
=
await
this
.
ipcRenderer
.
invoke
(
"nodejs-status"
);
const
result
=
await
this
.
ipcRenderer
.
invoke
(
"nodejs-status"
);
return
result
;
return
result
;
...
@@ -506,17 +508,6 @@ export class IpcClient {
...
@@ -506,17 +508,6 @@ export class IpcClient {
}
}
}
}
// Install Node.js and npm
public
async
installNode
():
Promise
<
InstallNodeResult
>
{
try
{
const
result
=
await
this
.
ipcRenderer
.
invoke
(
"install-node"
);
return
result
;
}
catch
(
error
)
{
showError
(
error
);
throw
error
;
}
}
// --- GitHub Device Flow ---
// --- GitHub Device Flow ---
public
startGithubDeviceFlow
(
appId
:
number
|
null
):
void
{
public
startGithubDeviceFlow
(
appId
:
number
|
null
):
void
{
this
.
ipcRenderer
.
invoke
(
"github:start-flow"
,
{
appId
});
this
.
ipcRenderer
.
invoke
(
"github:start-flow"
,
{
appId
});
...
...
src/ipc/ipc_types.ts
浏览文件 @
f2b157b8
...
@@ -66,13 +66,8 @@ export interface SandboxConfig {
...
@@ -66,13 +66,8 @@ export interface SandboxConfig {
entry
:
string
;
entry
:
string
;
}
}
export
type
InstallNodeResult
=
export
interface
NodeSystemInfo
{
|
{
nodeVersion
:
string
|
null
;
success
:
true
;
pnpmVersion
:
string
|
null
;
nodeVersion
:
string
;
nodeDownloadUrl
:
string
;
pnpmVersion
:
string
;
}
}
|
{
success
:
false
;
errorMessage
:
string
;
};
src/pages/home.tsx
浏览文件 @
f2b157b8
...
@@ -84,10 +84,6 @@ export default function HomePage() {
...
@@ -84,10 +84,6 @@ export default function HomePage() {
// Main Home Page Content
// Main Home Page Content
return
(
return
(
<
div
className=
"flex flex-col items-center justify-center max-w-3xl m-auto p-8"
>
<
div
className=
"flex flex-col items-center justify-center max-w-3xl m-auto p-8"
>
<
h1
className=
"text-6xl font-bold mb-12 bg-clip-text text-transparent bg-gradient-to-r from-gray-900 to-gray-600 dark:from-gray-100 dark:to-gray-400 tracking-tight"
>
Build your dream app
</
h1
>
<
SetupBanner
/>
<
SetupBanner
/>
<
div
className=
"w-full"
>
<
div
className=
"w-full"
>
...
...
src/preload.ts
浏览文件 @
f2b157b8
...
@@ -37,6 +37,7 @@ const validInvokeChannels = [
...
@@ -37,6 +37,7 @@ const validInvokeChannels = [
"github:create-repo"
,
"github:create-repo"
,
"github:push"
,
"github:push"
,
"get-app-version"
,
"get-app-version"
,
"reload-dyad"
,
]
as
const
;
]
as
const
;
// Add valid receive channels
// Add valid receive channels
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论