summaryrefslogtreecommitdiff
path: root/apps/web/src/preview/overlays.ts
diff options
context:
space:
mode:
authorMaze Winther <mazewinther@gmail.com>2026-04-20 11:31:17 +0200
committerMaze Winther <mazewinther@gmail.com>2026-04-20 11:31:17 +0200
commit3e89d2998588f8e35c524e523ba2ee853f0f6475 (patch)
tree57221f96c1259b3b07e3ddf8c1adefc502b9d8eb /apps/web/src/preview/overlays.ts
parent729d10592f4e802f91c555c309ff3ba4b119f843 (diff)
downloadopencut-3e89d2998588f8e35c524e523ba2ee853f0f6475.tar.gz
refactor: restructure files to their domains, new preview overlay system, and dep graph
Diffstat (limited to 'apps/web/src/preview/overlays.ts')
-rw-r--r--apps/web/src/preview/overlays.ts108
1 files changed, 108 insertions, 0 deletions
diff --git a/apps/web/src/preview/overlays.ts b/apps/web/src/preview/overlays.ts
new file mode 100644
index 00000000..6e5b0816
--- /dev/null
+++ b/apps/web/src/preview/overlays.ts
@@ -0,0 +1,108 @@
+import type { ReactNode } from "react";
+
+export type PreviewOverlayHudAnchor =
+ | "top-left"
+ | "top-right"
+ | "bottom-left"
+ | "bottom-right";
+
+export type PreviewOverlayMount =
+ | {
+ kind: "hud";
+ anchor: PreviewOverlayHudAnchor;
+ order?: number;
+ }
+ | {
+ kind: "scene";
+ x?: number;
+ y?: number;
+ width?: number;
+ height?: number;
+ }
+ | {
+ kind: "viewport";
+ x?: number;
+ y?: number;
+ width?: number;
+ height?: number;
+ };
+
+export type PreviewOverlayPlane = "under-interaction" | "over-interaction";
+
+export interface PreviewOverlayRenderContext {
+ sceneHeight: number;
+ sceneWidth: number;
+}
+
+export interface PreviewOverlayInstance {
+ id: string;
+ mount: PreviewOverlayMount;
+ plane?: PreviewOverlayPlane;
+ pointerEvents?: "none" | "auto";
+ zIndex?: number;
+ render: (context: PreviewOverlayRenderContext) => ReactNode;
+}
+
+export interface PreviewOverlayDefinition {
+ id: string;
+ label: string;
+ defaultVisible?: boolean;
+}
+
+export interface PreviewOverlayControl extends PreviewOverlayDefinition {
+ isVisible: boolean;
+}
+
+export interface PreviewOverlaySourceResult {
+ definitions: PreviewOverlayDefinition[];
+ instances: PreviewOverlayInstance[];
+}
+
+export const EMPTY_PREVIEW_OVERLAY_SOURCE_RESULT: PreviewOverlaySourceResult = {
+ definitions: [],
+ instances: [],
+};
+
+export function isPreviewOverlayVisible({
+ overlay,
+ overlays,
+}: {
+ overlay: PreviewOverlayDefinition;
+ overlays: Record<string, boolean>;
+}): boolean {
+ return overlays[overlay.id] ?? overlay.defaultVisible ?? true;
+}
+
+export function createPreviewOverlayControl({
+ overlay,
+ overlays,
+}: {
+ overlay: PreviewOverlayDefinition;
+ overlays: Record<string, boolean>;
+}): PreviewOverlayControl {
+ return {
+ ...overlay,
+ isVisible: isPreviewOverlayVisible({ overlay, overlays }),
+ };
+}
+
+export function mergePreviewOverlaySources({
+ sources,
+}: {
+ sources: PreviewOverlaySourceResult[];
+}): PreviewOverlaySourceResult {
+ const definitionsById = new Map<string, PreviewOverlayDefinition>();
+ const instances: PreviewOverlayInstance[] = [];
+
+ for (const source of sources) {
+ for (const definition of source.definitions) {
+ definitionsById.set(definition.id, definition);
+ }
+ instances.push(...source.instances);
+ }
+
+ return {
+ definitions: [...definitionsById.values()],
+ instances,
+ };
+}