Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
B
bit-pm
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
燕伟桐
bit-pm
Commits
4bc961ff
Unverified
提交
4bc961ff
authored
8月 14, 2025
作者:
Will Chen
提交者:
GitHub
8月 14, 2025
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Fix preset value (#933)
上级
b8362a74
显示空白字符变更
内嵌
并排
正在显示
4 个修改的文件
包含
81 行增加
和
8 行删除
+81
-8
ChatInput.tsx
src/components/chat/ChatInput.tsx
+1
-0
HomeChatInput.tsx
src/components/chat/HomeChatInput.tsx
+1
-0
LexicalChatInput.tsx
src/components/chat/LexicalChatInput.tsx
+75
-6
parse_mention_apps.ts
src/shared/parse_mention_apps.ts
+4
-2
没有找到文件。
src/components/chat/ChatInput.tsx
浏览文件 @
4bc961ff
...
...
@@ -293,6 +293,7 @@ export function ChatInput({ chatId }: { chatId?: number }) {
onSubmit=
{
handleSubmit
}
onPaste=
{
handlePaste
}
placeholder=
"Ask Dyad to build..."
excludeCurrentApp=
{
true
}
/>
{
isStreaming
?
(
...
...
src/components/chat/HomeChatInput.tsx
浏览文件 @
4bc961ff
...
...
@@ -83,6 +83,7 @@ export function HomeChatInput({
onPaste=
{
handlePaste
}
placeholder=
"Ask Dyad to build..."
disabled=
{
isStreaming
}
excludeCurrentApp=
{
false
}
/>
{
/* File attachment dropdown */
}
...
...
src/components/chat/LexicalChatInput.tsx
浏览文件 @
4bc961ff
import
React
,
{
useCallback
,
useEffect
,
useState
}
from
"react"
;
import
{
$getRoot
,
$createParagraphNode
,
EditorState
}
from
"lexical"
;
import
{
$getRoot
,
$createParagraphNode
,
$createTextNode
,
EditorState
,
}
from
"lexical"
;
import
{
LexicalComposer
}
from
"@lexical/react/LexicalComposer"
;
import
{
PlainTextPlugin
}
from
"@lexical/react/LexicalPlainTextPlugin"
;
import
{
ContentEditable
}
from
"@lexical/react/LexicalContentEditable"
;
...
...
@@ -10,6 +15,7 @@ import { LexicalErrorBoundary } from "@lexical/react/LexicalErrorBoundary";
import
{
BeautifulMentionsPlugin
,
BeautifulMentionNode
,
$createBeautifulMentionNode
,
type
BeautifulMentionsTheme
,
type
BeautifulMentionsMenuItemProps
,
}
from
"lexical-beautiful-mentions"
;
...
...
@@ -18,7 +24,7 @@ import { useLoadApps } from "@/hooks/useLoadApps";
import
{
forwardRef
}
from
"react"
;
import
{
useAtomValue
}
from
"jotai"
;
import
{
selectedAppIdAtom
}
from
"@/atoms/appAtoms"
;
import
{
parseAppMentions
}
from
"@/shared/parse_mention_apps"
;
import
{
MENTION_REGEX
,
parseAppMentions
}
from
"@/shared/parse_mention_apps"
;
// Define the theme for mentions
const
beautifulMentionsTheme
:
BeautifulMentionsTheme
=
{
...
...
@@ -129,6 +135,68 @@ function ClearEditorPlugin({
return
null
;
}
// Plugin to sync external value prop into the editor
function
ExternalValueSyncPlugin
({
value
}:
{
value
:
string
})
{
const
[
editor
]
=
useLexicalComposerContext
();
useEffect
(()
=>
{
// Derive the display text that should appear in the editor (@Name) from the
// internal value representation (@app:Name)
const
displayText
=
(
value
||
""
).
replace
(
MENTION_REGEX
,
"@$1"
);
const
currentText
=
editor
.
getEditorState
().
read
(()
=>
{
const
root
=
$getRoot
();
return
root
.
getTextContent
();
});
// If the editor already reflects the same display text, do nothing to avoid loops
if
(
currentText
===
displayText
)
return
;
editor
.
update
(()
=>
{
const
root
=
$getRoot
();
root
.
clear
();
const
paragraph
=
$createParagraphNode
();
// Build nodes from the internal value, turning @app:Name into a mention node
const
mentionRegex
=
/@app:
([
a-zA-Z0-9_-
]
+
)
/g
;
let
lastIndex
=
0
;
let
match
:
RegExpExecArray
|
null
;
while
((
match
=
mentionRegex
.
exec
(
value
))
!==
null
)
{
const
[
full
,
name
]
=
match
;
const
start
=
match
.
index
;
// Append any text before the mention
if
(
start
>
lastIndex
)
{
const
textBefore
=
value
.
slice
(
lastIndex
,
start
);
if
(
textBefore
)
paragraph
.
append
(
$createTextNode
(
textBefore
));
}
// Append the actual mention node (@ trigger with value = Name)
paragraph
.
append
(
$createBeautifulMentionNode
(
"@"
,
name
));
lastIndex
=
start
+
full
.
length
;
}
// Append any trailing text after the last mention
if
(
lastIndex
<
value
.
length
)
{
const
trailing
=
value
.
slice
(
lastIndex
);
if
(
trailing
)
paragraph
.
append
(
$createTextNode
(
trailing
));
}
// If there were no mentions at all, just append the raw value as text
if
(
value
&&
paragraph
.
getTextContent
()
===
""
)
{
paragraph
.
append
(
$createTextNode
(
value
));
}
root
.
append
(
paragraph
);
paragraph
.
selectEnd
();
});
},
[
editor
,
value
]);
return
null
;
}
interface
LexicalChatInputProps
{
value
:
string
;
onChange
:
(
value
:
string
)
=>
void
;
...
...
@@ -136,6 +204,7 @@ interface LexicalChatInputProps {
onPaste
?:
(
e
:
React
.
ClipboardEvent
)
=>
void
;
placeholder
?:
string
;
disabled
?:
boolean
;
excludeCurrentApp
:
boolean
;
}
function
onError
(
error
:
Error
)
{
...
...
@@ -147,6 +216,7 @@ export function LexicalChatInput({
onChange
,
onSubmit
,
onPaste
,
excludeCurrentApp
,
placeholder
=
"Ask Dyad to build..."
,
disabled
=
false
,
}:
LexicalChatInputProps
)
{
...
...
@@ -168,7 +238,7 @@ export function LexicalChatInput({
// Filter out current app and already mentioned apps
const
filteredApps
=
apps
.
filter
((
app
)
=>
{
// Exclude current app
if
(
app
.
name
===
currentAppName
)
return
false
;
if
(
excludeCurrentApp
&&
app
.
name
===
currentAppName
)
return
false
;
// Exclude already mentioned apps (case-insensitive comparison)
if
(
...
...
@@ -185,7 +255,7 @@ export function LexicalChatInput({
return
{
"@"
:
appMentions
,
};
},
[
apps
,
selectedAppId
,
value
]);
},
[
apps
,
selectedAppId
,
value
,
excludeCurrentApp
]);
const
initialConfig
=
{
namespace
:
"ChatInput"
,
...
...
@@ -203,7 +273,6 @@ export function LexicalChatInput({
const
root
=
$getRoot
();
let
textContent
=
root
.
getTextContent
();
console
.
time
(
"handleEditorChange"
);
// Transform @AppName mentions to @app:AppName format
// This regex matches @AppName where AppName is one of our actual app names
...
...
@@ -223,7 +292,6 @@ export function LexicalChatInput({
textContent
=
textContent
.
replace
(
mentionRegex
,
"@app:$1"
);
}
}
console
.
timeEnd
(
"handleEditorChange"
);
onChange
(
textContent
);
});
},
...
...
@@ -275,6 +343,7 @@ export function LexicalChatInput({
<
OnChangePlugin
onChange=
{
handleEditorChange
}
/>
<
HistoryPlugin
/>
<
EnterKeyPlugin
onSubmit=
{
handleSubmit
}
/>
<
ExternalValueSyncPlugin
value=
{
value
}
/>
<
ClearEditorPlugin
shouldClear=
{
shouldClear
}
onCleared=
{
handleCleared
}
...
...
src/shared/parse_mention_apps.ts
浏览文件 @
4bc961ff
export
const
MENTION_REGEX
=
/@app:
([
a-zA-Z0-9_-
]
+
)
/g
;
// Helper function to parse app mentions from prompt
export
function
parseAppMentions
(
prompt
:
string
):
string
[]
{
// Match @app:AppName patterns in the prompt (supports letters, digits, underscores, and hyphens, but NOT spaces)
const
mentionRegex
=
/@app:
([
a-zA-Z0-9_-
]
+
)
/g
;
const
mentions
:
string
[]
=
[];
let
match
;
while
((
match
=
mentionRegex
.
exec
(
prompt
))
!==
null
)
{
while
((
match
=
MENTION_REGEX
.
exec
(
prompt
))
!==
null
)
{
mentions
.
push
(
match
[
1
]);
}
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论