feat(canvas): 新增画布设置入口、区域选择滚轮平移与提交流程快捷键
Some checks failed
Publish container images / Quality checks (push) Has been cancelled
Publish container images / Resolve build version (push) Has been cancelled
Publish container images / Build open-ai-canvas-backend (amd64) (push) Has been cancelled
Publish container images / Build open-ai-canvas-backend (arm64) (push) Has been cancelled
Publish container images / Build open-ai-canvas-web (multi) (push) Has been cancelled
Publish container images / Publish open-ai-canvas-backend manifest (push) Has been cancelled
Publish container images / Publish open-ai-canvas-web manifest (push) Has been cancelled
Publish container images / Publish release assets (push) Has been cancelled
Some checks failed
Publish container images / Quality checks (push) Has been cancelled
Publish container images / Resolve build version (push) Has been cancelled
Publish container images / Build open-ai-canvas-backend (amd64) (push) Has been cancelled
Publish container images / Build open-ai-canvas-backend (arm64) (push) Has been cancelled
Publish container images / Build open-ai-canvas-web (multi) (push) Has been cancelled
Publish container images / Publish open-ai-canvas-backend manifest (push) Has been cancelled
Publish container images / Publish open-ai-canvas-web manifest (push) Has been cancelled
Publish container images / Publish release assets (push) Has been cancelled
This commit is contained in:
@@ -2589,3 +2589,7 @@ SSE 现在按 revision 变化发送紧凑快照,无变化只 heartbeat;客
|
||||
入口精简与工具草稿复测:移除左侧工具菜单及图片节点提示词模板入口,保留图片风格 / 预设和其他节点模板入口。工具引用列表读取草稿改用空值判断,明确清空时不再从原始生成提示词回读旧标签。风格和预设的选择、替换、连续编辑、标签移除、全文清空及保存重载覆盖有图/空节点两种情况;5 个专项文件共 58 项通过,TypeScript 类型检查通过。实际页面仍需确认普通/放大编辑器均无图片提示词模板入口,以及风格和预设清空后重新聚焦不回填。
|
||||
|
||||
主分支提交前复测:前端 `bun run test` 全量 1925 项通过,生产构建、类型检查、UI 退场 lint 通过;构建仍有大分块提示。同步了技能/工具标签结构与多行连接菜单的源码守护断言。后端 `go test ./...` 全量通过,工具、迁移、资源引用、handler 及工具预览权限/项目封面专项通过。本轮未执行真实模型调用、PostgreSQL 实库迁移或登录态浏览器验收,这些项目继续保留待验收。
|
||||
|
||||
画布页本轮改动:区域选择(底部工具栏胶囊切到「区域选择」)下滚轮改为上下平移画布,缩放保留给 Command/Ctrl + 滚轮(含触控板捏合);带 `data-canvas-wheel-scroll` 的节点内部区域仍保持自身滚动。顶栏分享按钮右侧新增「账户与设置」按钮,点击复用画布内「去设置」的 `navigateToSettings({ section: "channels", continueCreation: true })`,进入 `/settings?section=channels&continue=1`(顶部有「返回创作 / 保存并返回」,与模型缺失时的跳转一致,不新增全局配置弹窗)。曾尝试在画布内用抽屉内嵌设置页,因嵌套 Router 报错已移除。类型检查通过;待登录态浏览器验收:区域选择的滚轮方向与实际滚动手感,设置按钮跳转后返回画布时画布内容与滚动位置是否保持,以及设置项保存后画布生成默认值立即生效。
|
||||
|
||||
节点面板快捷键:`[data-canvas-node-panel]` 面板内任意位置按 ⌘/Ctrl + Enter 触发面板主按钮(目前只有创作面板的生成按钮带 `data-canvas-panel-submit`),生成中或提示词为空时按钮禁用,快捷键不拦截按键。待验收:文本框内按下不插入换行、输入法组合中不触发、面板内其他输入框与配置面板不被误触发。
|
||||
|
||||
@@ -387,6 +387,7 @@ export function CanvasNodePromptPanel({ projectId, node, isRunning, onPromptChan
|
||||
<Button
|
||||
type="text"
|
||||
className={`canvas-node-composer-submit canvas-node-composer-submit-canvas ${showCost ? "has-cost" : ""}`}
|
||||
data-canvas-panel-submit
|
||||
disabled={isRunning || isSubmitDisabled}
|
||||
style={
|
||||
{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { motion, useReducedMotion } from "motion/react";
|
||||
import { useLayoutEffect, useRef, useState, type CSSProperties, type ReactNode, type RefObject } from "react";
|
||||
import { useLayoutEffect, useRef, useState, type CSSProperties, type KeyboardEvent as ReactKeyboardEvent, type ReactNode, type RefObject } from "react";
|
||||
import { Clapperboard, Image as ImageIcon, List, Music2, Pencil, Table2, Video, WandSparkles, Workflow as WorkflowIcon } from "lucide-react";
|
||||
|
||||
import { useCanvasOverlayLayer } from "@/components/canvas/canvas-overlay-layer";
|
||||
@@ -160,6 +160,19 @@ export function CanvasNodePanelOverlay({
|
||||
};
|
||||
}, [containerRef, dragOffset?.x, dragOffset?.y, isDragging, node.height, node.id, node.position.x, node.position.y, node.width, panelHeight, panelWidth, viewport]);
|
||||
|
||||
// 面板内任意位置按下 ⌘/Ctrl + Enter 时直接提交,避免先把手移回输入框或生成按钮。
|
||||
// 只有面板自己声明了主按钮([data-canvas-panel-submit])才生效,且按钮禁用时不拦截按键。
|
||||
const handleSubmitShortcut = (event: ReactKeyboardEvent<HTMLDivElement>) => {
|
||||
if (event.key !== "Enter" || event.shiftKey || event.altKey) return;
|
||||
if (!(event.ctrlKey || event.metaKey)) return;
|
||||
if (event.nativeEvent.isComposing) return;
|
||||
const submit = panelRef.current?.querySelector<HTMLButtonElement>("[data-canvas-panel-submit]");
|
||||
if (!submit || submit.disabled || submit.getAttribute("aria-disabled") === "true") return;
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
submit.click();
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={panelRef}
|
||||
@@ -167,6 +180,7 @@ export function CanvasNodePanelOverlay({
|
||||
data-canvas-node-panel
|
||||
className={`thin-scrollbar absolute max-w-[calc(100%_-_24px)] ${allowOverflow ? "overflow-visible" : "overflow-y-auto"}`}
|
||||
style={{ left: 0, top: 0, transform: `translate3d(${initialPosition.left}px, ${initialPosition.top}px, 0)`, width: initialWidth, maxHeight: allowOverflow ? "none" : "calc(100% - 84px)", zIndex }}
|
||||
onKeyDownCapture={handleSubmitShortcut}
|
||||
onMouseDown={(event) => event.stopPropagation()}
|
||||
onPointerDownCapture={bringToFront}
|
||||
onFocusCapture={bringToFront}
|
||||
|
||||
@@ -198,6 +198,18 @@ export function InfiniteCanvas({ interactive = true, containerRef, viewport, app
|
||||
event.preventDefault();
|
||||
interactingRef.current = true;
|
||||
const current = viewportRef.current;
|
||||
|
||||
// 区域选择模式下滚轮用于上下浏览画布,缩放保留给 Ctrl/Meta + 滚轮(触控板捏合)。
|
||||
if (boxSelectEnabled && !isPinchZoom) {
|
||||
const shiftHorizontal = event.shiftKey && absX < 1;
|
||||
scheduleViewportChange({
|
||||
x: current.x - (shiftHorizontal ? deltaY : deltaX),
|
||||
y: current.y - (shiftHorizontal ? 0 : deltaY),
|
||||
k: current.k,
|
||||
}, true);
|
||||
return;
|
||||
}
|
||||
|
||||
const rawAbsY = Math.abs(event.deltaY);
|
||||
const looksLikeMouseWheel = event.deltaMode !== 0 || (rawAbsY >= 80 && Math.abs(rawAbsY - Math.round(rawAbsY / 100) * 100) < 1);
|
||||
const looksLikeTrackpadPan = !isPinchZoom && (event.shiftKey || absX > 0 || (!looksLikeMouseWheel && absY > 0));
|
||||
@@ -228,7 +240,7 @@ export function InfiniteCanvas({ interactive = true, containerRef, viewport, app
|
||||
k: newScale,
|
||||
}, true);
|
||||
},
|
||||
[containerRef, scheduleViewportChange],
|
||||
[boxSelectEnabled, containerRef, scheduleViewportChange],
|
||||
);
|
||||
|
||||
const handlePointerDown = (event: React.PointerEvent<HTMLDivElement>) => {
|
||||
|
||||
@@ -109,8 +109,8 @@ export const CANVAS_SHORTCUTS: CanvasShortcutItem[] = [
|
||||
id: "box-select-tool",
|
||||
category: "selection",
|
||||
title: "使用区域选择工具",
|
||||
description: "底部工具栏的胶囊开关切到区域选择后,空白处拖动可连续框选",
|
||||
keys: [["区域选择", "拖动"]],
|
||||
description: "底部工具栏的胶囊开关切到区域选择后,空白处拖动可连续框选;滚轮上下移动画布,缩放请用 Command/Ctrl + 滚轮",
|
||||
keys: [["区域选择", "拖动"], ["区域选择", "滚轮"]],
|
||||
keywords: ["工具栏", "多选", "selection"],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useEffect, useRef, useState, type CSSProperties, type ReactNode } from "react";
|
||||
import { Link } from "react-router";
|
||||
import { Clapperboard, CloudDownload, CloudUpload, CopyPlus, Focus, FolderKanban, Gauge, History, Home, LayoutGrid, LoaderCircle, Menu, Pencil, Plus, Redo2, Save, Search, Share2, Trash2, Undo2, Upload } from "lucide-react";
|
||||
import { Clapperboard, CloudDownload, CloudUpload, CopyPlus, Focus, FolderKanban, Gauge, History, Home, LayoutGrid, LoaderCircle, Menu, Pencil, Plus, Redo2, Save, Search, Settings, Share2, Trash2, Undo2, Upload } from "lucide-react";
|
||||
import { Button, Dropdown, Tooltip } from "antd";
|
||||
|
||||
import { WorkspaceCreditGiftMark } from "@/components/layout/workspace-credit-gift-mark";
|
||||
@@ -10,6 +10,7 @@ import { canvasDockStyle } from "@/lib/canvas/canvas-aceternity-style";
|
||||
import type { CanvasContextSummary } from "@/lib/canvas/canvas-context-summary";
|
||||
import type { CanvasShortDramaProgress } from "@/lib/canvas/canvas-short-drama";
|
||||
import { canvasThemes } from "@/lib/canvas-theme";
|
||||
import { navigateToSettings } from "@/lib/settings-navigation";
|
||||
import { useCanvasThemeStore } from "@/stores/canvas/use-canvas-theme-store";
|
||||
import { useUserStore } from "@/stores/use-user-store";
|
||||
import type { CanvasMediaPerformanceMode } from "@/types/canvas";
|
||||
@@ -288,6 +289,9 @@ export function CanvasTopBar({
|
||||
<CanvasTopBarTooltip label="分享画布">
|
||||
<Button type="text" className="canvas-topbar-action !h-10 !w-10 !min-w-10 !rounded-xl !p-0" style={{ color: theme.node.text }} icon={<Share2 className="size-4" />} onClick={onShare} aria-label="分享画布" />
|
||||
</CanvasTopBarTooltip>
|
||||
<CanvasTopBarTooltip label="账户与设置">
|
||||
<Button type="text" className="canvas-topbar-action !h-10 !w-10 !min-w-10 !rounded-xl !p-0" style={{ color: theme.node.text }} icon={<Settings className="size-4" />} onClick={() => navigateToSettings({ section: "channels", continueCreation: true })} aria-label="账户与设置" />
|
||||
</CanvasTopBarTooltip>
|
||||
</div>
|
||||
</div>
|
||||
<CanvasShortcutsModal open={shortcutsOpen} onClose={() => setShortcutsOpen(false)} />
|
||||
|
||||
Reference in New Issue
Block a user