Files
moteva/frontend/src/ui/pages/CanvasWorkspace/canvas/frameStyle.ts
T
root 44406b72db Initial commit: img-infinite-canvas AI design workbench MVP
Moteva-style AI design workbench replica. Home prompt creates a project;
the project page provides an infinite canvas with node drag, zoom/pan,
a design chat panel, history replay, image asset upload, and project
save/regenerate.

- Backend: go-zero, DDD layering, sqlc/pgx, optional PostgreSQL; memory
  or Redis cache; asynq job queue; MinIO/S3/R2/OSS object storage;
  sky-valley/pi agent runtime adapter.
- Frontend: Next.js App Router + Vite artifact build, TypeScript, i18n,
  shadcn/ui components, auth (OTP/Turnstile/Google/WeChat).
- Deploy: Docker Compose and k3s manifests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 23:15:37 +08:00

39 lines
1.5 KiB
TypeScript

import type { CSSProperties } from "react";
import type { CanvasNode } from "@/domain/design";
export const defaultFrameFillColor = "#ffffff";
export const defaultFrameStrokeColor = "#2f80ff";
export const defaultFrameStrokeWidth = 1;
export const defaultFrameStrokeStyle = "solid";
export function framePreviewStyle(node: CanvasNode): CSSProperties {
const opacity = typeof node.opacity === "number" ? node.opacity : 1;
return {
background: colorWithOpacity(node.fillColor || defaultFrameFillColor, opacity),
borderColor: node.strokeColor || defaultFrameStrokeColor,
borderWidth: `${node.strokeWidth || defaultFrameStrokeWidth}px`,
borderStyle: normalizeStrokeStyle(node.strokeStyle)
};
}
export function normalizeStrokeStyle(value: string | undefined) {
if (value === "dashed" || value === "dotted") return value;
return defaultFrameStrokeStyle;
}
export function normalizeHexColor(value: string, fallback: string) {
const trimmed = value.trim();
if (/^#[0-9a-f]{6}$/i.test(trimmed)) return trimmed.toUpperCase();
if (/^[0-9a-f]{6}$/i.test(trimmed)) return `#${trimmed.toUpperCase()}`;
return fallback;
}
function colorWithOpacity(color: string, opacity: number) {
if (color === "transparent") return color;
const normalized = normalizeHexColor(color, defaultFrameFillColor);
const r = Number.parseInt(normalized.slice(1, 3), 16);
const g = Number.parseInt(normalized.slice(3, 5), 16);
const b = Number.parseInt(normalized.slice(5, 7), 16);
return `rgba(${r}, ${g}, ${b}, ${Math.min(Math.max(opacity, 0), 1)})`;
}