Compare commits

...

2 Commits

Author SHA1 Message Date
clawdbot
3ea6aa8fb3 fix-ignore-editor-hotkeys-in-inputs 2026-01-29 07:24:48 +08:00
clawdbot
b252a80a8e feat(editor): inspector rect x/y/w/h 2026-01-29 06:50:03 +08:00
3 changed files with 69 additions and 4 deletions

View File

@ -563,6 +563,7 @@ export function EditorApp() {
<Inspector
selected={selected}
onUpdateRect={(id, patch) => dispatch({ type: 'updateWidgetRect', id, rect: patch })}
onUpdateTextProps={(id, props) =>
dispatch({ type: 'updateWidgetProps', widgetType: 'text', id, props })
}

View File

@ -1,4 +1,4 @@
import { Input, InputNumber, Select, Space, Typography } from 'antd';
import { Divider, Input, InputNumber, Select, Space, Typography } from 'antd';
import { assertNever, type WidgetNode, type TextWidgetNode, type WidgetNodeByType } from '@astralview/sdk';
type ImageWidgetNode = WidgetNodeByType['image'];
@ -7,6 +7,7 @@ type VideoWidgetNode = WidgetNodeByType['video'];
export function Inspector(props: {
selected?: WidgetNode;
onUpdateRect: (id: string, patch: Partial<WidgetNode['rect']>) => void;
onUpdateTextProps: (id: string, patch: Partial<TextWidgetNode['props']>) => void;
onUpdateImageProps: (id: string, patch: Partial<ImageWidgetNode['props']>) => void;
onUpdateIframeProps: (id: string, patch: Partial<IframeWidgetNode['props']>) => void;
@ -18,6 +19,60 @@ export function Inspector(props: {
return <Typography.Paragraph style={{ color: '#666' }}>No selection.</Typography.Paragraph>;
}
const rectDisabled = !!node.locked;
const RectSection = (
<div style={{ marginBottom: 12 }}>
<Typography.Text type="secondary">Rect</Typography.Text>
<Space style={{ width: '100%', marginTop: 6 }} size={8} wrap>
<InputNumber
size="small"
value={node.rect.x}
controls={false}
disabled={rectDisabled}
onChange={(v) => props.onUpdateRect(node.id, { x: typeof v === 'number' ? v : node.rect.x })}
style={{ width: 90 }}
addonBefore="X"
/>
<InputNumber
size="small"
value={node.rect.y}
controls={false}
disabled={rectDisabled}
onChange={(v) => props.onUpdateRect(node.id, { y: typeof v === 'number' ? v : node.rect.y })}
style={{ width: 90 }}
addonBefore="Y"
/>
<InputNumber
size="small"
value={node.rect.w}
controls={false}
disabled={rectDisabled}
min={1}
onChange={(v) => props.onUpdateRect(node.id, { w: typeof v === 'number' ? v : node.rect.w })}
style={{ width: 96 }}
addonBefore="W"
/>
<InputNumber
size="small"
value={node.rect.h}
controls={false}
disabled={rectDisabled}
min={1}
onChange={(v) => props.onUpdateRect(node.id, { h: typeof v === 'number' ? v : node.rect.h })}
style={{ width: 96 }}
addonBefore="H"
/>
</Space>
{rectDisabled ? (
<Typography.Text style={{ display: 'block', marginTop: 4, color: '#64748b', fontSize: 12 }}>
Locked layer: rect editing disabled.
</Typography.Text>
) : null}
<Divider style={{ margin: '12px 0', borderColor: 'rgba(255,255,255,0.08)' }} />
</div>
);
switch (node.type) {
case 'image':
return (
@ -26,6 +81,8 @@ export function Inspector(props: {
Image
</Typography.Title>
{RectSection}
<Typography.Text type="secondary">Source</Typography.Text>
<Input
value={node.props.src}
@ -65,6 +122,8 @@ export function Inspector(props: {
Iframe
</Typography.Title>
{RectSection}
<Typography.Text type="secondary">Source</Typography.Text>
<Input
value={node.props.src}
@ -90,6 +149,8 @@ export function Inspector(props: {
Video
</Typography.Title>
{RectSection}
<Typography.Text type="secondary">Source</Typography.Text>
<Input
value={node.props.src}
@ -173,6 +234,8 @@ export function Inspector(props: {
Text
</Typography.Title>
{RectSection}
<Typography.Text type="secondary">Content</Typography.Text>
<Input
value={node.props.text}

View File

@ -21,15 +21,16 @@ export function bindEditorHotkeys(getShift: () => boolean, dispatch: (a: EditorA
const onKeyDown = (e: KeyboardEvent) => {
const ctrl = e.ctrlKey || e.metaKey;
// Do not steal common hotkeys while typing in an input/editor.
// (Including Escape: inputs often use it to revert/blur.)
if (isEditableTarget(e.target)) return;
// Esc: clear selection (and closes context menu via selection parity effect).
if (e.key === 'Escape') {
dispatch({ type: 'selectSingle', id: undefined });
return;
}
// Do not steal common hotkeys while typing in an input/editor.
if (isEditableTarget(e.target)) return;
// Undo/redo
if (ctrl && e.key.toLowerCase() === 'z') {
e.preventDefault();