Moxie-Multimedia / frontend /src /lib /timeline-node-size.ts
turtle89431's picture
Upload folder using huggingface_hub
7312c42 verified
Raw History Blame Contribute Delete
6.83 kB
const TIMELINE_NODE_NAME = new Set(['easy timelineEditor', 'easy multiTrackEditor'])
const TIMELINE_HEIGHT_PROPERTY = 'easyMediaTimelineHeight'
const TIMELINE_WIDGET_RESIZE_GUARD = '__easyMediaTimelineWidgetResizeGuard'
const TIMELINE_HEIGHT_RESTORE_VERSION = '__easyMediaTimelineHeightRestoreVersion'
const TIMELINE_WIDGET_DEFAULT_SIZES: Record<string, [number, number]> = {
'easy timelineEditor': [520, 430],
'easy multiTrackEditor': [800, 700],
}
const WIDGET_RESIZE_GUARD_MS = 500
type NodeSize = [number, number]
type ResizeGuard = { height: number; width?: number; expiresAt: number }
function readSize(size: unknown): NodeSize | null {
if (Array.isArray(size) && size.length >= 2) {
const width = Number(size[0])
const height = Number(size[1])
return Number.isFinite(width) && Number.isFinite(height) ? [width, height] : null
}
if (ArrayBuffer.isView(size) && 'length' in size && Number(size.length) >= 2) {
const arrayLikeSize = size as unknown as ArrayLike<number>
const width = Number(arrayLikeSize[0])
const height = Number(arrayLikeSize[1])
return Number.isFinite(width) && Number.isFinite(height) ? [width, height] : null
}
if (!size || typeof size !== 'object') return null
const sizeObject = size as Record<string, unknown>
const width = Number(sizeObject.width ?? sizeObject[0])
const height = Number(sizeObject.height ?? sizeObject[1])
return Number.isFinite(width) && Number.isFinite(height) ? [width, height] : null
}
function readStoredHeight(node: any, serialisedNode?: any): number | null {
const propertyHeight = Number(
node?.properties?.[TIMELINE_HEIGHT_PROPERTY]
?? serialisedNode?.properties?.[TIMELINE_HEIGHT_PROPERTY],
)
if (Number.isFinite(propertyHeight) && propertyHeight > 0) return propertyHeight
return readSize(serialisedNode?.size)?.[1] ?? null
}
function preserveHeight(node: any, height: unknown) {
const numericHeight = Number(height)
if (!Number.isFinite(numericHeight) || numericHeight <= 0) return
node.properties ??= {}
node.properties[TIMELINE_HEIGHT_PROPERTY] = numericHeight
}
function applyHeight(node: any, height: number, fallbackWidth?: number) {
const currentSize = readSize(node.size)
if (!currentSize || Math.abs(currentSize[1] - height) < 1) return
const width = currentSize[0] || fallbackWidth || 480
if (typeof node.setSize === 'function') {
node.setSize([width, height])
} else {
node.size = [width, height]
}
node.setDirtyCanvas?.(true, true)
}
function invalidatePendingHeightRestores(node: any) {
const currentVersion = Number(node?.[TIMELINE_HEIGHT_RESTORE_VERSION])
node[TIMELINE_HEIGHT_RESTORE_VERSION] = Number.isFinite(currentVersion) ? currentVersion + 1 : 1
}
export function adjustMultiTrackEditorNodeHeight(node: any, heightDelta: number) {
const currentSize = readSize(node?.size)
if (!currentSize || !Number.isFinite(heightDelta) || heightDelta === 0) return
const nextHeight = Math.max(1, currentSize[1] + heightDelta)
invalidatePendingHeightRestores(node)
delete node[TIMELINE_WIDGET_RESIZE_GUARD]
preserveHeight(node, nextHeight)
applyHeight(node, nextHeight, currentSize[0])
}
function restoreHeight(node: any, height: number | null, fallbackWidth?: number) {
if (!Number.isFinite(height) || height === null || height <= 0) return
const restoreVersion = Number(node?.[TIMELINE_HEIGHT_RESTORE_VERSION]) || 0
globalThis.setTimeout(() => {
if ((Number(node?.[TIMELINE_HEIGHT_RESTORE_VERSION]) || 0) !== restoreVersion) return
applyHeight(node, height, fallbackWidth)
}, 100)
}
function readWidgetResizeGuard(node: any): ResizeGuard | null {
const guard = node?.[TIMELINE_WIDGET_RESIZE_GUARD] as ResizeGuard | undefined
if (!guard || !Number.isFinite(guard.height) || guard.height <= 0) return null
if (Date.now() > guard.expiresAt) {
delete node[TIMELINE_WIDGET_RESIZE_GUARD]
return null
}
return guard
}
function beginWidgetResizeGuard(node: any) {
const currentSize = readSize(node?.size)
const height = readStoredHeight(node) ?? currentSize?.[1] ?? null
if (!Number.isFinite(height) || height === null || height <= 0) return
node.properties ??= {}
node.properties[TIMELINE_HEIGHT_PROPERTY] = height
node[TIMELINE_WIDGET_RESIZE_GUARD] = {
height,
width: currentSize?.[0],
expiresAt: Date.now() + WIDGET_RESIZE_GUARD_MS,
} satisfies ResizeGuard
}
export function preserveTimelineEditorNodeSize(nodeType: any, nodeData: { name?: string }) {
if (!TIMELINE_NODE_NAME.has(nodeData.name || '')) return
const originalOnNodeCreated = nodeType.prototype.onNodeCreated
const originalOnConfigure = nodeType.prototype.onConfigure
const originalOnResize = nodeType.prototype.onResize
const originalOnSerialize = nodeType.prototype.onSerialize
const originalOnWidgetChanged = nodeType.prototype.onWidgetChanged
nodeType.prototype.onNodeCreated = function () {
originalOnNodeCreated?.call(this)
const nodeName = nodeData.name || ''
const defaultSize = TIMELINE_WIDGET_DEFAULT_SIZES[nodeName]
if (!defaultSize) return
this.size[0] = defaultSize[0]
this.size[1] = defaultSize[1]
this.setDirtyCanvas?.(true, true)
}
nodeType.prototype.onConfigure = function (serialisedNode: any) {
originalOnConfigure?.call(this, serialisedNode)
const savedHeight = readStoredHeight(this, serialisedNode)
invalidatePendingHeightRestores(this)
if (savedHeight !== null) preserveHeight(this, savedHeight)
restoreHeight(this, savedHeight, readSize(serialisedNode?.size)?.[0])
}
nodeType.prototype.onResize = function (size: unknown) {
originalOnResize?.call(this, size)
const nextSize = readSize(size)
if (!nextSize) return
const resizeGuard = readWidgetResizeGuard(this)
if (resizeGuard) {
preserveHeight(this, resizeGuard.height)
restoreHeight(this, resizeGuard.height, resizeGuard.width ?? nextSize[0])
return
}
preserveHeight(this, nextSize[1])
}
nodeType.prototype.onWidgetChanged = function (
name: string,
value: unknown,
oldValue: unknown,
widget: unknown,
) {
beginWidgetResizeGuard(this)
originalOnWidgetChanged?.call(this, name, value, oldValue, widget)
const resizeGuard = readWidgetResizeGuard(this)
if (resizeGuard) restoreHeight(this, resizeGuard.height, resizeGuard.width)
}
nodeType.prototype.onSerialize = function (serialisedNode: any) {
originalOnSerialize?.call(this, serialisedNode)
const currentSize = readSize(this.size)
if (currentSize) preserveHeight(this, currentSize[1])
serialisedNode.properties ??= {}
serialisedNode.properties[TIMELINE_HEIGHT_PROPERTY] = this.properties?.[TIMELINE_HEIGHT_PROPERTY]
}
}
export const preserveTimelineEditorNodeHeight = preserveTimelineEditorNodeSize