Mirror of design_agent_front/design-agent/ for shipping alongside backend.
Vite plugin (vitePluginPhaseZApi) endpoints :
- POST /api/run — spawn `python -m src.phase_z2_pipeline` with overrides
- GET /api/sample-mdx?mdx=03/04/05 — fixed sample MDX
- GET /frame-preview/{n} — figma preview thumbnails
- GET /data/runs/{run_id}/{path} — pipeline artifacts (final.html, step*.json, ...)
Env toggle forward (보고용) :
PHASE_Z_ALLOW_RESTRUCTURE / PHASE_Z_ALLOW_REJECT / PHASE_Z_MAX_RANK=32
Components :
- LeftMdxPanel (03/04/05 fix list + section tree)
- SlideCanvas (iframe + slideOverrideCss prop for inline CSS inject)
- FramePanel (label priority + confidence sort)
- LayoutPanel
README with mermaid diagrams covering the 5-step demo flow.
node_modules / dist / .manus-logs / .env excluded via .gitignore.
82 lines
2.3 KiB
TypeScript
82 lines
2.3 KiB
TypeScript
import { useRef } from "react";
|
|
import { usePersistFn } from "./usePersistFn";
|
|
|
|
export interface UseCompositionReturn<
|
|
T extends HTMLInputElement | HTMLTextAreaElement,
|
|
> {
|
|
onCompositionStart: React.CompositionEventHandler<T>;
|
|
onCompositionEnd: React.CompositionEventHandler<T>;
|
|
onKeyDown: React.KeyboardEventHandler<T>;
|
|
isComposing: () => boolean;
|
|
}
|
|
|
|
export interface UseCompositionOptions<
|
|
T extends HTMLInputElement | HTMLTextAreaElement,
|
|
> {
|
|
onKeyDown?: React.KeyboardEventHandler<T>;
|
|
onCompositionStart?: React.CompositionEventHandler<T>;
|
|
onCompositionEnd?: React.CompositionEventHandler<T>;
|
|
}
|
|
|
|
type TimerResponse = ReturnType<typeof setTimeout>;
|
|
|
|
export function useComposition<
|
|
T extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement,
|
|
>(options: UseCompositionOptions<T> = {}): UseCompositionReturn<T> {
|
|
const {
|
|
onKeyDown: originalOnKeyDown,
|
|
onCompositionStart: originalOnCompositionStart,
|
|
onCompositionEnd: originalOnCompositionEnd,
|
|
} = options;
|
|
|
|
const c = useRef(false);
|
|
const timer = useRef<TimerResponse | null>(null);
|
|
const timer2 = useRef<TimerResponse | null>(null);
|
|
|
|
const onCompositionStart = usePersistFn((e: React.CompositionEvent<T>) => {
|
|
if (timer.current) {
|
|
clearTimeout(timer.current);
|
|
timer.current = null;
|
|
}
|
|
if (timer2.current) {
|
|
clearTimeout(timer2.current);
|
|
timer2.current = null;
|
|
}
|
|
c.current = true;
|
|
originalOnCompositionStart?.(e);
|
|
});
|
|
|
|
const onCompositionEnd = usePersistFn((e: React.CompositionEvent<T>) => {
|
|
// 使用两层 setTimeout 来处理 Safari 浏览器中 compositionEnd 先于 onKeyDown 触发的问题
|
|
timer.current = setTimeout(() => {
|
|
timer2.current = setTimeout(() => {
|
|
c.current = false;
|
|
});
|
|
});
|
|
originalOnCompositionEnd?.(e);
|
|
});
|
|
|
|
const onKeyDown = usePersistFn((e: React.KeyboardEvent<T>) => {
|
|
// 在 composition 状态下,阻止 ESC 和 Enter(非 shift+Enter)事件的冒泡
|
|
if (
|
|
c.current &&
|
|
(e.key === "Escape" || (e.key === "Enter" && !e.shiftKey))
|
|
) {
|
|
e.stopPropagation();
|
|
return;
|
|
}
|
|
originalOnKeyDown?.(e);
|
|
});
|
|
|
|
const isComposing = usePersistFn(() => {
|
|
return c.current;
|
|
});
|
|
|
|
return {
|
|
onCompositionStart,
|
|
onCompositionEnd,
|
|
onKeyDown,
|
|
isComposing,
|
|
};
|
|
}
|