Compare commits

..

4 Commits

Author SHA1 Message Date
Dayuan Jiang
651238529a Merge pull request #570 from DayuanJiang/chore/add-opencode-to-gitignore
chore: add opencode.json to gitignore
2026-01-11 11:05:45 +09:00
dayuan.jiang
35ab222343 chore: add opencode.json to gitignore 2026-01-11 11:02:34 +09:00
Maifee Ul Asad
b7eaf46555 [Feature] Add setting for Enter/Ctrl+Enter to send messages (#550)
* i18n: add translations for send shortcut setting

* feat: configurable keyboard shortcut for sending messages

* refactor,review: using storage key for send shortcut

* Increase the width of the trigger in the settings dialog. Previously, at 160px, it hide the letter “d” from the word “Send.”

* Update components/chat-input.tsx

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* fix: from review, ctrl send support for mac

* refactor: from review, reduce local storage read

* fix: make send shortcut setting reactive without page refresh

---------

Co-authored-by: Biki Kalita <86558912+Biki-dev@users.noreply.github.com>
Co-authored-by: Dayuan Jiang <34411969+DayuanJiang@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: dayuan.jiang <jdy.toh@gmail.com>
2026-01-11 10:54:32 +09:00
Dayuan Jiang
5eb797b191 chore: reduce Renovate noise - monthly schedule, group major updates (#569) 2026-01-10 23:31:01 +09:00
7 changed files with 87 additions and 2 deletions

3
.gitignore vendored
View File

@@ -67,4 +67,5 @@ CLAUDE.md
.spec-workflow
# edgeone
.edgeone
.edgeone
opencode.json

View File

@@ -24,6 +24,7 @@ import { useDiagram } from "@/contexts/diagram-context"
import { useDictionary } from "@/hooks/use-dictionary"
import { formatMessage } from "@/lib/i18n/utils"
import { isPdfFile, isTextFile } from "@/lib/pdf-utils"
import { STORAGE_KEYS } from "@/lib/storage"
import type { FlattenedModel } from "@/lib/types/model-config"
import { extractUrlContent, type UrlData } from "@/lib/url-utils"
import { FilePreviewList } from "./file-preview-list"
@@ -192,6 +193,7 @@ export function ChatInput({
const [showHistory, setShowHistory] = useState(false)
const [showUrlDialog, setShowUrlDialog] = useState(false)
const [isExtractingUrl, setIsExtractingUrl] = useState(false)
const [sendShortcut, setSendShortcut] = useState("ctrl-enter")
// Allow retry when there's an error (even if status is still "streaming" or "submitted")
const isDisabled =
(status === "streaming" || status === "submitted") && !error
@@ -208,13 +210,36 @@ export function ChatInput({
adjustTextareaHeight()
}, [input, adjustTextareaHeight])
// Load send shortcut preference from localStorage and listen for changes
useEffect(() => {
const stored = localStorage.getItem(STORAGE_KEYS.sendShortcut)
if (stored) setSendShortcut(stored)
const handleChange = (e: CustomEvent<string>) =>
setSendShortcut(e.detail)
window.addEventListener(
"sendShortcutChange",
handleChange as EventListener,
)
return () =>
window.removeEventListener(
"sendShortcutChange",
handleChange as EventListener,
)
}, [])
const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
onChange(e)
adjustTextareaHeight()
}
const handleKeyDown = (e: React.KeyboardEvent) => {
if ((e.metaKey || e.ctrlKey) && e.key === "Enter") {
const shouldSend =
sendShortcut === "enter"
? e.key === "Enter" && !e.shiftKey && !e.ctrlKey && !e.metaKey
: (e.metaKey || e.ctrlKey) && e.key === "Enter"
if (shouldSend) {
e.preventDefault()
const form = e.currentTarget.closest("form")
if (form && input.trim() && !isDisabled) {

View File

@@ -25,6 +25,7 @@ import { Switch } from "@/components/ui/switch"
import { useDictionary } from "@/hooks/use-dictionary"
import { getApiEndpoint } from "@/lib/base-path"
import { i18n, type Locale } from "@/lib/i18n/config"
import { STORAGE_KEYS } from "@/lib/storage"
// Reusable setting item component for consistent layout
function SettingItem({
@@ -103,6 +104,7 @@ function SettingsContent({
() => getStoredAccessCodeRequired() ?? false,
)
const [currentLang, setCurrentLang] = useState("en")
const [sendShortcut, setSendShortcut] = useState("ctrl-enter")
// Proxy settings state (Electron only)
const [httpProxy, setHttpProxy] = useState("")
@@ -155,6 +157,11 @@ function SettingsContent({
// Default to true if not set
setCloseProtection(storedCloseProtection !== "false")
const storedSendShortcut = localStorage.getItem(
STORAGE_KEYS.sendShortcut,
)
setSendShortcut(storedSendShortcut || "ctrl-enter")
setError("")
// Load proxy settings (Electron only)
@@ -425,6 +432,43 @@ function SettingsContent({
</div>
</SettingItem>
{/* Send Shortcut */}
<SettingItem
label={dict.settings.sendShortcut}
description={dict.settings.sendShortcutDescription}
>
<Select
value={sendShortcut}
onValueChange={(value) => {
setSendShortcut(value)
localStorage.setItem(
STORAGE_KEYS.sendShortcut,
value,
)
window.dispatchEvent(
new CustomEvent("sendShortcutChange", {
detail: value,
}),
)
}}
>
<SelectTrigger
id="send-shortcut-select"
className="w-[170px] h-9 rounded-xl"
>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="enter">
{dict.settings.enterToSend}
</SelectItem>
<SelectItem value="ctrl-enter">
{dict.settings.ctrlEnterToSend}
</SelectItem>
</SelectContent>
</Select>
</SettingItem>
{/* Proxy Settings - Electron only */}
{typeof window !== "undefined" &&
window.electronAPI?.isElectron && (

View File

@@ -104,6 +104,10 @@
"closeProtectionDescription": "Show confirmation when leaving the page.",
"diagramStyle": "Diagram Style",
"diagramStyleDescription": "Toggle between minimal and styled diagram output.",
"sendShortcut": "Send Shortcut",
"sendShortcutDescription": "Choose how to send messages.",
"enterToSend": "Enter to send",
"ctrlEnterToSend": "Cmd/Ctrl+Enter to send",
"diagramActions": "Diagram Actions",
"diagramActionsDescription": "Manage diagram history and exports",
"history": "History",

View File

@@ -104,6 +104,10 @@
"closeProtectionDescription": "ページを離れる際に確認を表示します。",
"diagramStyle": "ダイアグラムスタイル",
"diagramStyleDescription": "ミニマルとスタイル付きの出力を切り替えます。",
"sendShortcut": "送信ショートカット",
"sendShortcutDescription": "メッセージの送信方法を選択します。",
"enterToSend": "Enterで送信",
"ctrlEnterToSend": "Cmd/Ctrl+Enterで送信",
"diagramActions": "ダイアグラム操作",
"diagramActionsDescription": "ダイアグラムの履歴とエクスポートを管理",
"history": "履歴",

View File

@@ -104,6 +104,10 @@
"closeProtectionDescription": "离开页面时显示确认。",
"diagramStyle": "图表样式",
"diagramStyleDescription": "切换简约与精致图表输出模式。",
"sendShortcut": "发送快捷键",
"sendShortcutDescription": "选择发送消息的方式。",
"enterToSend": "回车发送",
"ctrlEnterToSend": "Cmd/Ctrl+回车发送",
"diagramActions": "图表操作",
"diagramActionsDescription": "管理图表历史记录和导出",
"history": "历史记录",

View File

@@ -22,4 +22,7 @@ export const STORAGE_KEYS = {
// Multi-model configuration
modelConfigs: "next-ai-draw-io-model-configs",
selectedModelId: "next-ai-draw-io-selected-model-id",
// Chat input preferences
sendShortcut: "next-ai-draw-io-send-shortcut",
} as const