Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Q
qjclaw-dmg
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
AI-甘富林
qjclaw-dmg
Commits
362f9163
Commit
362f9163
authored
May 11, 2026
by
edy
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix(ui): restrict home chat attachments to images
parent
105938fc
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
71 additions
and
8 deletions
+71
-8
App.tsx
apps/ui/src/App.tsx
+6
-3
useComposerAttachments.ts
apps/ui/src/features/chat/useComposerAttachments.ts
+65
-5
No files found.
apps/ui/src/App.tsx
View file @
362f9163
...
@@ -91,7 +91,6 @@ import {
...
@@ -91,7 +91,6 @@ import {
toUiChatMessage
toUiChatMessage
}
from
"./lib/chat-utils"
;
}
from
"./lib/chat-utils"
;
import
{
import
{
COMPOSER_ATTACHMENT_ACCEPT
,
EMPTY_SESSION_ID
,
EMPTY_SESSION_ID
,
HOME_CHAT_PROJECT_ID
,
HOME_CHAT_PROJECT_ID
,
SUCCESS_NOTICE_TIMEOUT_MS
SUCCESS_NOTICE_TIMEOUT_MS
...
@@ -322,6 +321,7 @@ export default function App() {
...
@@ -322,6 +321,7 @@ export default function App() {
});
});
const
{
const
{
attachmentInputRef
,
attachmentInputRef
,
attachmentAccept
,
composerAttachments
,
composerAttachments
,
isComposerDragOver
,
isComposerDragOver
,
clearComposerAttachment
,
clearComposerAttachment
,
...
@@ -332,7 +332,10 @@ export default function App() {
...
@@ -332,7 +332,10 @@ export default function App() {
handleComposerDragOver
,
handleComposerDragOver
,
handleComposerDragLeave
,
handleComposerDragLeave
,
handleComposerDrop
handleComposerDrop
}
=
useComposerAttachments
({
setErrorText
});
}
=
useComposerAttachments
({
mode
:
viewMode
===
"chat"
?
"image-only"
:
"all-supported"
,
setErrorText
});
const
{
const
{
isComposerResizeActive
,
isComposerResizeActive
,
composerShellStyle
,
composerShellStyle
,
...
@@ -1222,7 +1225,7 @@ export default function App() {
...
@@ -1222,7 +1225,7 @@ export default function App() {
isComposerDragOver
,
isComposerDragOver
,
isComposerResizeActive
,
isComposerResizeActive
,
composerShellStyle
,
composerShellStyle
,
attachmentAccept
:
COMPOSER_ATTACHMENT_ACCEPT
,
attachmentAccept
,
attachments
:
composerAttachments
,
attachments
:
composerAttachments
,
composerPlaceholder
,
composerPlaceholder
,
sendButtonLabel
,
sendButtonLabel
,
...
...
apps/ui/src/features/chat/useComposerAttachments.ts
View file @
362f9163
import
{
useRef
,
useState
}
from
"react"
;
import
{
use
Effect
,
use
Ref
,
useState
}
from
"react"
;
import
type
{
ChangeEvent
,
DragEvent
as
ReactDragEvent
}
from
"react"
;
import
type
{
ChangeEvent
,
DragEvent
as
ReactDragEvent
}
from
"react"
;
import
type
{
ChatAttachment
}
from
"@qjclaw/shared-types"
;
import
type
{
ChatAttachment
}
from
"@qjclaw/shared-types"
;
import
{
import
{
COMPOSER_ATTACHMENT_ACCEPT
,
IMAGE_ATTACHMENT_EXTENSIONS
,
IMAGE_ATTACHMENT_EXTENSIONS
,
SUPPORTED_ATTACHMENT_EXTENSIONS
SUPPORTED_ATTACHMENT_EXTENSIONS
}
from
"../../lib/constants"
;
}
from
"../../lib/constants"
;
import
{
desktopApi
}
from
"../../lib/desktop-api"
;
import
{
desktopApi
}
from
"../../lib/desktop-api"
;
export
type
ComposerAttachmentMode
=
"image-only"
|
"all-supported"
;
interface
UseComposerAttachmentsOptions
{
interface
UseComposerAttachmentsOptions
{
mode
:
ComposerAttachmentMode
;
setErrorText
:
(
value
:
string
)
=>
void
;
setErrorText
:
(
value
:
string
)
=>
void
;
}
}
export
function
useComposerAttachments
({
setErrorText
}:
UseComposerAttachmentsOptions
)
{
const
COMPOSER_IMAGE_ATTACHMENT_ACCEPT
=
[...
IMAGE_ATTACHMENT_EXTENSIONS
].
join
(
","
);
const
HOME_IMAGE_ATTACHMENT_ERROR
=
"首页对话仅支持上传图片附件。"
;
const
SUPPORTED_ATTACHMENT_ERROR
=
"Supported attachments: images, MP3, PDF, PPT, Excel, Word, CSV, TXT, Markdown, and JSON."
;
function
resolveExtension
(
name
:
string
,
localPath
:
string
):
string
{
const
candidate
=
name
||
localPath
;
const
index
=
candidate
.
lastIndexOf
(
"."
);
return
index
>=
0
?
candidate
.
slice
(
index
).
toLowerCase
()
:
""
;
}
function
isImageAttachment
(
attachment
:
ChatAttachment
):
boolean
{
const
mimeType
=
attachment
.
mimeType
?.
trim
().
toLowerCase
()
??
""
;
const
extension
=
resolveExtension
(
attachment
.
name
,
attachment
.
localPath
);
return
attachment
.
kind
===
"image"
||
mimeType
.
startsWith
(
"image/"
)
||
IMAGE_ATTACHMENT_EXTENSIONS
.
has
(
extension
);
}
export
function
useComposerAttachments
({
mode
,
setErrorText
}:
UseComposerAttachmentsOptions
)
{
const
[
composerAttachments
,
setComposerAttachments
]
=
useState
<
ChatAttachment
[]
>
([]);
const
[
composerAttachments
,
setComposerAttachments
]
=
useState
<
ChatAttachment
[]
>
([]);
const
[
isComposerDragOver
,
setIsComposerDragOver
]
=
useState
(
false
);
const
[
isComposerDragOver
,
setIsComposerDragOver
]
=
useState
(
false
);
const
attachmentInputRef
=
useRef
<
HTMLInputElement
|
null
>
(
null
);
const
attachmentInputRef
=
useRef
<
HTMLInputElement
|
null
>
(
null
);
const
composerDragDepthRef
=
useRef
(
0
);
const
composerDragDepthRef
=
useRef
(
0
);
const
composerModeRef
=
useRef
(
mode
);
const
attachmentAccept
=
mode
===
"image-only"
?
COMPOSER_IMAGE_ATTACHMENT_ACCEPT
:
COMPOSER_ATTACHMENT_ACCEPT
;
useEffect
(()
=>
{
composerModeRef
.
current
=
mode
;
},
[
mode
]);
useEffect
(()
=>
{
if
(
mode
!==
"image-only"
)
{
return
;
}
const
filtered
=
composerAttachments
.
filter
((
attachment
)
=>
isImageAttachment
(
attachment
));
if
(
filtered
.
length
===
composerAttachments
.
length
)
{
return
;
}
setErrorText
(
HOME_IMAGE_ATTACHMENT_ERROR
);
setComposerAttachments
(
filtered
);
},
[
composerAttachments
,
mode
,
setErrorText
]);
function
resolveComposerAttachmentKind
(
file
:
File
,
localPath
:
string
):
ChatAttachment
[
"kind"
]
|
null
{
function
resolveComposerAttachmentKind
(
file
:
File
,
localPath
:
string
):
ChatAttachment
[
"kind"
]
|
null
{
if
(
file
.
type
.
startsWith
(
"image/"
))
{
if
(
file
.
type
.
startsWith
(
"image/"
))
{
return
"image"
;
return
"image"
;
}
}
const
extension
=
(
file
.
name
?
file
.
name
.
slice
(
file
.
name
.
lastIndexOf
(
"."
))
:
localPath
.
slice
(
localPath
.
lastIndexOf
(
"."
))).
toLowerCase
(
);
const
extension
=
resolveExtension
(
file
.
name
,
localPath
);
if
(
IMAGE_ATTACHMENT_EXTENSIONS
.
has
(
extension
))
{
if
(
IMAGE_ATTACHMENT_EXTENSIONS
.
has
(
extension
))
{
return
"image"
;
return
"image"
;
}
}
...
@@ -61,15 +101,20 @@ export function useComposerAttachments({ setErrorText }: UseComposerAttachmentsO
...
@@ -61,15 +101,20 @@ export function useComposerAttachments({ setErrorText }: UseComposerAttachmentsO
}
}
function
acceptComposerAttachmentFile
(
file
:
File
)
{
function
acceptComposerAttachmentFile
(
file
:
File
)
{
const
kind
=
resolveComposerAttachmentKind
(
file
,
file
.
name
);
if
(
mode
===
"image-only"
&&
kind
!==
"image"
)
{
setErrorText
(
HOME_IMAGE_ATTACHMENT_ERROR
);
return
;
}
const
localPath
=
(
file
as
File
&
{
path
?:
string
}).
path
?.
trim
();
const
localPath
=
(
file
as
File
&
{
path
?:
string
}).
path
?.
trim
();
if
(
!
localPath
)
{
if
(
!
localPath
)
{
setErrorText
(
"The desktop client did not provide a local file path, so this attachment cannot be sent into the project workspace."
);
setErrorText
(
"The desktop client did not provide a local file path, so this attachment cannot be sent into the project workspace."
);
return
;
return
;
}
}
const
kind
=
resolveComposerAttachmentKind
(
file
,
localPath
);
if
(
!
kind
)
{
if
(
!
kind
)
{
setErrorText
(
"Supported attachments: images, MP3, PDF, PPT, Excel, Word, CSV, TXT, Markdown, and JSON."
);
setErrorText
(
SUPPORTED_ATTACHMENT_ERROR
);
return
;
return
;
}
}
...
@@ -129,6 +174,20 @@ export function useComposerAttachments({ setErrorText }: UseComposerAttachmentsO
...
@@ -129,6 +174,20 @@ export function useComposerAttachments({ setErrorText }: UseComposerAttachmentsO
async
function
openAttachmentPicker
()
{
async
function
openAttachmentPicker
()
{
if
(
window
.
qjcDesktop
)
{
if
(
window
.
qjcDesktop
)
{
if
(
composerModeRef
.
current
===
"image-only"
)
{
const
attachment
=
await
desktopApi
.
chat
.
pickImageAttachment
();
if
(
!
attachment
)
{
return
;
}
if
(
composerModeRef
.
current
===
"image-only"
&&
!
isImageAttachment
(
attachment
))
{
setErrorText
(
HOME_IMAGE_ATTACHMENT_ERROR
);
return
;
}
setErrorText
(
""
);
appendComposerAttachments
([
attachment
]);
return
;
}
const
attachments
=
await
desktopApi
.
chat
.
pickAttachments
();
const
attachments
=
await
desktopApi
.
chat
.
pickAttachments
();
if
(
!
attachments
.
length
)
{
if
(
!
attachments
.
length
)
{
return
;
return
;
...
@@ -155,6 +214,7 @@ export function useComposerAttachments({ setErrorText }: UseComposerAttachmentsO
...
@@ -155,6 +214,7 @@ export function useComposerAttachments({ setErrorText }: UseComposerAttachmentsO
return
{
return
{
attachmentInputRef
,
attachmentInputRef
,
attachmentAccept
,
composerAttachments
,
composerAttachments
,
isComposerDragOver
,
isComposerDragOver
,
clearComposerAttachment
,
clearComposerAttachment
,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment