|
|
|
@ -25,64 +25,71 @@ const EL_FIELDS: Partial<Record<MonoDisplay.ElementTypeName, ElFieldMeta[]>> = { |
|
|
|
Image2D: [ |
|
|
|
Image2D: [ |
|
|
|
{ key: "xOffset", label: "X offset", type: "number", default: 0 }, |
|
|
|
{ key: "xOffset", label: "X offset", type: "number", default: 0 }, |
|
|
|
{ key: "yOffset", label: "Y offset", type: "number", default: 0 }, |
|
|
|
{ key: "yOffset", label: "Y offset", type: "number", default: 0 }, |
|
|
|
{ key: "width", label: "Width", type: "number", default: W }, |
|
|
|
{ key: "width", label: "Width", type: "number", default: W }, |
|
|
|
{ key: "height", label: "Height", type: "number", default: H }, |
|
|
|
{ key: "height", label: "Height", type: "number", default: H }, |
|
|
|
], |
|
|
|
], |
|
|
|
Animation: [ |
|
|
|
Animation: [ |
|
|
|
{ key: "xOffset", label: "X offset", type: "number", default: 0 }, |
|
|
|
{ key: "xOffset", label: "X offset", type: "number", default: 0 }, |
|
|
|
{ key: "yOffset", label: "Y offset", type: "number", default: 0 }, |
|
|
|
{ key: "yOffset", label: "Y offset", type: "number", default: 0 }, |
|
|
|
{ key: "width", label: "Width", type: "number", default: W }, |
|
|
|
{ key: "width", label: "Width", type: "number", default: W }, |
|
|
|
{ key: "height", label: "Height", type: "number", default: H }, |
|
|
|
{ key: "height", label: "Height", type: "number", default: H }, |
|
|
|
{ key: "updateInterval", label: "Update interval", type: "number", default: 12 }, |
|
|
|
{ key: "updateInterval", label: "Update interval", type: "number", default: 12 }, |
|
|
|
{ key: "frames", label: "Frames", type: "list", default: [] }, |
|
|
|
{ key: "frames", label: "Frames", type: "list", default: [] }, |
|
|
|
], |
|
|
|
], |
|
|
|
ClippedText: [ |
|
|
|
ClippedText: [ |
|
|
|
{ key: "text", label: "Text", type: "text", default: "Hello, World!", full: true }, |
|
|
|
{ key: "text", label: "Text", type: "text", default: "Hello, World!", full: true }, |
|
|
|
{ key: "xOffset", label: "X offset", type: "number", default: 0 }, |
|
|
|
{ key: "xOffset", label: "X offset", type: "number", default: 0 }, |
|
|
|
{ key: "yOffset", label: "Y offset", type: "number", default: 10 }, |
|
|
|
{ key: "yOffset", label: "Y offset", type: "number", default: 10 }, |
|
|
|
{ key: "width", label: "Width", type: "number", default: 30 }, |
|
|
|
{ key: "width", label: "Width", type: "number", default: 30 }, |
|
|
|
{ key: "height", label: "Height", type: "number", default: 10 }, |
|
|
|
{ key: "height", label: "Height", type: "number", default: 10 }, |
|
|
|
{ key: "fontIndex", label: "Font", type: "font", default: 0 }, |
|
|
|
{ key: "fontIndex", label: "Font", type: "font", default: 0 }, |
|
|
|
], |
|
|
|
], |
|
|
|
HScrollText: [ |
|
|
|
HScrollText: [ |
|
|
|
{ key: "text", label: "Text", type: "text", default: "Scrolling text - ", full: true }, |
|
|
|
{ key: "text", label: "Text", type: "text", default: "Scrolling text - ", full: true }, |
|
|
|
{ key: "xOffset", label: "X offset", type: "number", default: 0 }, |
|
|
|
{ key: "xOffset", label: "X offset", type: "number", default: 0 }, |
|
|
|
{ key: "yOffset", label: "Y offset", type: "number", default: 10 }, |
|
|
|
{ key: "yOffset", label: "Y offset", type: "number", default: 10 }, |
|
|
|
{ key: "width", label: "Width", type: "number", default: 30 }, |
|
|
|
{ key: "width", label: "Width", type: "number", default: 30 }, |
|
|
|
{ key: "height", label: "Height", type: "number", default: 10 }, |
|
|
|
{ key: "height", label: "Height", type: "number", default: 10 }, |
|
|
|
{ key: "scrollSpeed", label: "Scroll speed", type: "number", default: 50 }, |
|
|
|
{ key: "scrollSpeed", label: "Scroll speed", type: "number", default: 50 }, |
|
|
|
{ key: "fontIndex", label: "Font", type: "font", default: 0 }, |
|
|
|
{ key: "fontIndex", label: "Font", type: "font", default: 0 }, |
|
|
|
], |
|
|
|
], |
|
|
|
VScrollText: [ |
|
|
|
VScrollText: [ |
|
|
|
{ key: "text", label: "Text", type: "text", default: "Scrolling text - ", full: true }, |
|
|
|
{ key: "text", label: "Text", type: "text", default: "Scrolling text - ", full: true }, |
|
|
|
{ key: "xOffset", label: "X offset", type: "number", default: 0 }, |
|
|
|
{ key: "xOffset", label: "X offset", type: "number", default: 0 }, |
|
|
|
{ key: "yOffset", label: "Y offset", type: "number", default: 32 }, |
|
|
|
{ key: "yOffset", label: "Y offset", type: "number", default: 32 }, |
|
|
|
{ key: "width", label: "Width", type: "number", default: 30 }, |
|
|
|
{ key: "width", label: "Width", type: "number", default: 30 }, |
|
|
|
{ key: "height", label: "Height", type: "number", default: 10}, |
|
|
|
{ key: "height", label: "Height", type: "number", default: 10 }, |
|
|
|
{ key: "scrollSpeed", label: "Scroll speed", type: "number", default: 50 }, |
|
|
|
{ key: "scrollSpeed", label: "Scroll speed", type: "number", default: 50 }, |
|
|
|
{ key: "fontIndex", label: "Font", type: "font", default: 0 }, |
|
|
|
{ key: "fontIndex", label: "Font", type: "font", default: 0 }, |
|
|
|
|
|
|
|
], |
|
|
|
|
|
|
|
Line: [ |
|
|
|
|
|
|
|
{ key: "xOrigin", label: "X offset", type: "number", default: 0 }, |
|
|
|
|
|
|
|
{ key: "yOrigin", label: "Y offset", type: "number", default: 32 }, |
|
|
|
|
|
|
|
{ key: "xTarget", label: "X offset", type: "number", default: 0 }, |
|
|
|
|
|
|
|
{ key: "yTarget", label: "Y offset", type: "number", default: 32 }, |
|
|
|
|
|
|
|
{ key: "linestyle", label: "Lintylee S", type: "number", default: 0 }, |
|
|
|
], |
|
|
|
], |
|
|
|
CurrentTime: [ |
|
|
|
CurrentTime: [ |
|
|
|
{ key: "xOffset", label: "X offset", type: "number", default: 0 }, |
|
|
|
{ key: "xOffset", label: "X offset", type: "number", default: 0 }, |
|
|
|
{ key: "yOffset", label: "Y offset", type: "number", default: 8 }, |
|
|
|
{ key: "yOffset", label: "Y offset", type: "number", default: 8 }, |
|
|
|
{ key: "width", label: "Width", type: "number", default: W }, |
|
|
|
{ key: "width", label: "Width", type: "number", default: W }, |
|
|
|
{ key: "height", label: "Height", type: "number", default: H }, |
|
|
|
{ key: "height", label: "Height", type: "number", default: H }, |
|
|
|
{ key: "utcOffsetMinutes", label: "UTC offset (min)", type: "number", default: 120 }, |
|
|
|
{ key: "utcOffsetMinutes", label: "UTC offset (min)", type: "number", default: 120 }, |
|
|
|
{ key: "fontIndex", label: "Font", type: "font", default: 0 }, |
|
|
|
{ key: "fontIndex", label: "Font", type: "font", default: 0 }, |
|
|
|
], |
|
|
|
], |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
const EL_FLAGS: Partial<Record<MonoDisplay.ElementTypeName, ElFlagMeta[]>> = { |
|
|
|
const EL_FLAGS: Partial<Record<MonoDisplay.ElementTypeName, ElFlagMeta[]>> = { |
|
|
|
HScrollText: [ |
|
|
|
HScrollText: [ |
|
|
|
{ key: "endless", label: "Endless" }, |
|
|
|
{ key: "endless", label: "Endless" }, |
|
|
|
{ key: "invertDirection", label: "Invert direction" }, |
|
|
|
{ key: "invertDirection", label: "Invert direction" }, |
|
|
|
{ key: "padBefore", label: "Pad Before" }, |
|
|
|
{ key: "padBefore", label: "Pad Before" }, |
|
|
|
{ key: "padAfter", label: "Pad After" }, |
|
|
|
{ key: "padAfter", label: "Pad After" }, |
|
|
|
], |
|
|
|
], |
|
|
|
CurrentTime: [ |
|
|
|
CurrentTime: [ |
|
|
|
{ key: "clock12h", label: "12h mode", default: false }, |
|
|
|
{ key: "clock12h", label: "12h mode", default: false }, |
|
|
|
{ key: "showHours", label: "Show hours", default: true }, |
|
|
|
{ key: "showHours", label: "Show hours", default: true }, |
|
|
|
{ key: "showSeconds", label: "Show seconds", default: true }, |
|
|
|
{ key: "showSeconds", label: "Show seconds", default: true }, |
|
|
|
], |
|
|
|
], |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
@ -99,10 +106,10 @@ function showConfirm( |
|
|
|
): Promise<boolean> { |
|
|
|
): Promise<boolean> { |
|
|
|
return new Promise(resolve => { |
|
|
|
return new Promise(resolve => { |
|
|
|
const overlay = document.getElementById("confirm-overlay"); |
|
|
|
const overlay = document.getElementById("confirm-overlay"); |
|
|
|
const msgEl = document.getElementById("confirm-msg"); |
|
|
|
const msgEl = document.getElementById("confirm-msg"); |
|
|
|
const btnsEl = document.getElementById("confirm-btns"); |
|
|
|
const btnsEl = document.getElementById("confirm-btns"); |
|
|
|
if (msgEl) msgEl.textContent = msg; |
|
|
|
if (msgEl) msgEl.textContent = msg; |
|
|
|
if (btnsEl) btnsEl.innerHTML = ""; |
|
|
|
if (btnsEl) btnsEl.innerHTML = ""; |
|
|
|
buttons.forEach(b => { |
|
|
|
buttons.forEach(b => { |
|
|
|
const btn = document.createElement("button"); |
|
|
|
const btn = document.createElement("button"); |
|
|
|
btn.className = "cb" + (b.primary ? " primary" : ""); |
|
|
|
btn.className = "cb" + (b.primary ? " primary" : ""); |
|
|
|
@ -122,7 +129,7 @@ function saveToStorage() { |
|
|
|
const buf = file.toBuffer(); |
|
|
|
const buf = file.toBuffer(); |
|
|
|
const b64 = btoa(String.fromCharCode(...buf)); |
|
|
|
const b64 = btoa(String.fromCharCode(...buf)); |
|
|
|
localStorage.setItem(STORAGE_KEY, JSON.stringify({ filename: currentFilename, data: b64 })); |
|
|
|
localStorage.setItem(STORAGE_KEY, JSON.stringify({ filename: currentFilename, data: b64 })); |
|
|
|
} catch {} |
|
|
|
} catch { } |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
function loadFromStorage() { |
|
|
|
function loadFromStorage() { |
|
|
|
@ -139,7 +146,7 @@ function loadFromStorage() { |
|
|
|
isDirty = true; |
|
|
|
isDirty = true; |
|
|
|
document.getElementById("filename")?.classList.add("dirty"); |
|
|
|
document.getElementById("filename")?.classList.add("dirty"); |
|
|
|
m.redraw(); |
|
|
|
m.redraw(); |
|
|
|
} catch {} |
|
|
|
} catch { } |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// --- Dirty tracking ----------------------------------------------------------
|
|
|
|
// --- Dirty tracking ----------------------------------------------------------
|
|
|
|
@ -167,7 +174,8 @@ function newSec(): MonoDisplay.MonoFormatElementsAlways { |
|
|
|
clearBuffer: true, |
|
|
|
clearBuffer: true, |
|
|
|
drawBack: true, |
|
|
|
drawBack: true, |
|
|
|
drawFront: true, |
|
|
|
drawFront: true, |
|
|
|
} }; |
|
|
|
} |
|
|
|
|
|
|
|
}; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
function createNewElement(type: MonoDisplay.ElementType): MonoDisplay.MonoFormatElement { |
|
|
|
function createNewElement(type: MonoDisplay.ElementType): MonoDisplay.MonoFormatElement { |
|
|
|
@ -375,8 +383,8 @@ const DEMO: Record<string, () => MonoDisplayFile> = { |
|
|
|
return new MonoDisplayFile([{ |
|
|
|
return new MonoDisplayFile([{ |
|
|
|
sectionType: MonoDisplay.SectionType.ElementsAlways, |
|
|
|
sectionType: MonoDisplay.SectionType.ElementsAlways, |
|
|
|
elements: [ |
|
|
|
elements: [ |
|
|
|
{ type: MonoDisplay.ElementType.CurrentTime, fontIndex: 0, flags: {}, xOffset: 0, yOffset: 8, width: W, height: 16, utcOffsetMinutes: 120 }, |
|
|
|
{ type: MonoDisplay.ElementType.CurrentTime, fontIndex: 0, flags: {}, xOffset: 0, yOffset: 8, width: W, height: 16, utcOffsetMinutes: 120 }, |
|
|
|
{ type: MonoDisplay.ElementType.CurrentTime, fontIndex: 0, flags: { clock12h: true }, xOffset: 40, yOffset: 16, width: W, height: 16, utcOffsetMinutes: 120 }, |
|
|
|
{ type: MonoDisplay.ElementType.CurrentTime, fontIndex: 0, flags: { clock12h: true }, xOffset: 40, yOffset: 16, width: W, height: 16, utcOffsetMinutes: 120 }, |
|
|
|
{ type: MonoDisplay.ElementType.CurrentTime, fontIndex: 0, flags: { clock12h: true, showHours: true }, xOffset: 0, yOffset: 24, width: W, height: 16, utcOffsetMinutes: 120 }, |
|
|
|
{ type: MonoDisplay.ElementType.CurrentTime, fontIndex: 0, flags: { clock12h: true, showHours: true }, xOffset: 0, yOffset: 24, width: W, height: 16, utcOffsetMinutes: 120 }, |
|
|
|
{ type: MonoDisplay.ElementType.CurrentTime, fontIndex: 0, flags: { clock12h: true, showHours: false }, xOffset: 40, yOffset: 32, width: W, height: 16, utcOffsetMinutes: 120 }, |
|
|
|
{ type: MonoDisplay.ElementType.CurrentTime, fontIndex: 0, flags: { clock12h: true, showHours: false }, xOffset: 40, yOffset: 32, width: W, height: 16, utcOffsetMinutes: 120 }, |
|
|
|
{ type: MonoDisplay.ElementType.CurrentTime, fontIndex: 0, flags: { clock12h: true, showSeconds: true }, xOffset: 80, yOffset: 32, width: W, height: 16, utcOffsetMinutes: 120 }, |
|
|
|
{ type: MonoDisplay.ElementType.CurrentTime, fontIndex: 0, flags: { clock12h: true, showSeconds: true }, xOffset: 80, yOffset: 32, width: W, height: 16, utcOffsetMinutes: 120 }, |
|
|
|
@ -496,10 +504,10 @@ const PixelCanvas: m.Component<{ img: MonoDisplay.MonoFormatPixelImage; onpaint: |
|
|
|
function pixelFromEvent(e: MouseEvent): { x: number; y: number } | null { |
|
|
|
function pixelFromEvent(e: MouseEvent): { x: number; y: number } | null { |
|
|
|
const canvas = e.currentTarget as HTMLCanvasElement; |
|
|
|
const canvas = e.currentTarget as HTMLCanvasElement; |
|
|
|
const rect = canvas.getBoundingClientRect(); |
|
|
|
const rect = canvas.getBoundingClientRect(); |
|
|
|
const scaleX = canvas.width / rect.width; |
|
|
|
const scaleX = canvas.width / rect.width; |
|
|
|
const scaleY = canvas.height / rect.height; |
|
|
|
const scaleY = canvas.height / rect.height; |
|
|
|
const x = Math.floor((e.clientX - rect.left) * scaleX / PIXEL_SCALE); |
|
|
|
const x = Math.floor((e.clientX - rect.left) * scaleX / PIXEL_SCALE); |
|
|
|
const y = Math.floor((e.clientY - rect.top) * scaleY / PIXEL_SCALE); |
|
|
|
const y = Math.floor((e.clientY - rect.top) * scaleY / PIXEL_SCALE); |
|
|
|
if (x < 0 || x >= img.width || y < 0 || y >= img.height) return null; |
|
|
|
if (x < 0 || x >= img.width || y < 0 || y >= img.height) return null; |
|
|
|
return { x, y }; |
|
|
|
return { x, y }; |
|
|
|
} |
|
|
|
} |
|
|
|
@ -513,7 +521,7 @@ const PixelCanvas: m.Component<{ img: MonoDisplay.MonoFormatPixelImage; onpaint: |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return m("canvas.pixel-editor", { |
|
|
|
return m("canvas.pixel-editor", { |
|
|
|
width: img.width * PIXEL_SCALE, |
|
|
|
width: img.width * PIXEL_SCALE, |
|
|
|
height: img.height * PIXEL_SCALE, |
|
|
|
height: img.height * PIXEL_SCALE, |
|
|
|
oncreate: ({ dom }: m.VnodeDOM) => drawPixelCanvas(dom as HTMLCanvasElement, img), |
|
|
|
oncreate: ({ dom }: m.VnodeDOM) => drawPixelCanvas(dom as HTMLCanvasElement, img), |
|
|
|
onupdate: ({ dom }: m.VnodeDOM) => drawPixelCanvas(dom as HTMLCanvasElement, img), |
|
|
|
onupdate: ({ dom }: m.VnodeDOM) => drawPixelCanvas(dom as HTMLCanvasElement, img), |
|
|
|
@ -524,7 +532,7 @@ const PixelCanvas: m.Component<{ img: MonoDisplay.MonoFormatPixelImage; onpaint: |
|
|
|
paint(e); |
|
|
|
paint(e); |
|
|
|
}, |
|
|
|
}, |
|
|
|
onmousemove: (e: MouseEvent) => { if (state.drawing) paint(e); }, |
|
|
|
onmousemove: (e: MouseEvent) => { if (state.drawing) paint(e); }, |
|
|
|
onmouseup: () => { state.drawing = false; }, |
|
|
|
onmouseup: () => { state.drawing = false; }, |
|
|
|
onmouseleave: () => { state.drawing = false; }, |
|
|
|
onmouseleave: () => { state.drawing = false; }, |
|
|
|
}); |
|
|
|
}); |
|
|
|
}, |
|
|
|
}, |
|
|
|
@ -535,7 +543,7 @@ const PixelCanvas: m.Component<{ img: MonoDisplay.MonoFormatPixelImage; onpaint: |
|
|
|
const Image2DEditor: m.Component<{ si: number; ei: number; el: MonoDisplay.MonoFormatImage2D }> = { |
|
|
|
const Image2DEditor: m.Component<{ si: number; ei: number; el: MonoDisplay.MonoFormatImage2D }> = { |
|
|
|
view({ attrs: { si, ei, el } }) { |
|
|
|
view({ attrs: { si, ei, el } }) { |
|
|
|
if (!el.image) { |
|
|
|
if (!el.image) { |
|
|
|
const w = (el as any).width || W; |
|
|
|
const w = (el as any).width || W; |
|
|
|
const h = (el as any).height || H; |
|
|
|
const h = (el as any).height || H; |
|
|
|
el.image = { pixels: new Uint8Array(w * h), width: w, height: h }; |
|
|
|
el.image = { pixels: new Uint8Array(w * h), width: w, height: h }; |
|
|
|
} |
|
|
|
} |
|
|
|
@ -554,7 +562,7 @@ const AnimationEditor: m.Component<{ si: number; ei: number; el: MonoDisplay.Mon |
|
|
|
activeFrame: 0, |
|
|
|
activeFrame: 0, |
|
|
|
|
|
|
|
|
|
|
|
view({ attrs: { si, ei, el }, state }) { |
|
|
|
view({ attrs: { si, ei, el }, state }) { |
|
|
|
const w = el.width || W; |
|
|
|
const w = el.width || W; |
|
|
|
const h = el.height || H; |
|
|
|
const h = el.height || H; |
|
|
|
if (!el.frames) el.frames = []; |
|
|
|
if (!el.frames) el.frames = []; |
|
|
|
if (!el.frames.length) { |
|
|
|
if (!el.frames.length) { |
|
|
|
@ -587,8 +595,8 @@ const AnimationEditor: m.Component<{ si: number; ei: number; el: MonoDisplay.Mon |
|
|
|
m("span", fi + 1), |
|
|
|
m("span", fi + 1), |
|
|
|
el.frames.length > 1 |
|
|
|
el.frames.length > 1 |
|
|
|
? m("button.x-btn-sm", { |
|
|
|
? m("button.x-btn-sm", { |
|
|
|
onclick: (e: Event) => { e.stopPropagation(); removeFrame(fi); }, |
|
|
|
onclick: (e: Event) => { e.stopPropagation(); removeFrame(fi); }, |
|
|
|
}, "×") |
|
|
|
}, "×") |
|
|
|
: null, |
|
|
|
: null, |
|
|
|
) |
|
|
|
) |
|
|
|
), |
|
|
|
), |
|
|
|
@ -609,7 +617,7 @@ const ElementItem: m.Component<{ si: number; ei: number; el: MonoDisplay.MonoFor |
|
|
|
const isActive = activeSecIndex === si && activeElIndex === ei; |
|
|
|
const isActive = activeSecIndex === si && activeElIndex === ei; |
|
|
|
const typeStr = MonoDisplay.ElementTypeToString[el.type]; |
|
|
|
const typeStr = MonoDisplay.ElementTypeToString[el.type]; |
|
|
|
const fields = EL_FIELDS[typeStr] || []; |
|
|
|
const fields = EL_FIELDS[typeStr] || []; |
|
|
|
const flags = EL_FLAGS[typeStr] || []; |
|
|
|
const flags = EL_FLAGS[typeStr] || []; |
|
|
|
|
|
|
|
|
|
|
|
return m(".el-item", { class: isActive ? "active" : "" }, |
|
|
|
return m(".el-item", { class: isActive ? "active" : "" }, |
|
|
|
m(".el-item-hdr", { onclick: () => selectElement(si, ei) }, |
|
|
|
m(".el-item-hdr", { onclick: () => selectElement(si, ei) }, |
|
|
|
@ -655,15 +663,15 @@ const ElementItem: m.Component<{ si: number; ei: number; el: MonoDisplay.MonoFor |
|
|
|
) : null, |
|
|
|
) : null, |
|
|
|
el.type === MonoDisplay.ElementType.Image2D |
|
|
|
el.type === MonoDisplay.ElementType.Image2D |
|
|
|
? m(".field.full", |
|
|
|
? m(".field.full", |
|
|
|
m("label", "Pixels"), |
|
|
|
m("label", "Pixels"), |
|
|
|
m(Image2DEditor, { si, ei, el: el as MonoDisplay.MonoFormatImage2D }), |
|
|
|
m(Image2DEditor, { si, ei, el: el as MonoDisplay.MonoFormatImage2D }), |
|
|
|
) |
|
|
|
) |
|
|
|
: el.type === MonoDisplay.ElementType.Animation |
|
|
|
: el.type === MonoDisplay.ElementType.Animation |
|
|
|
? m(".field.full", |
|
|
|
? m(".field.full", |
|
|
|
m("label", "Frames"), |
|
|
|
m("label", "Frames"), |
|
|
|
m(AnimationEditor, { si, ei, el: el as MonoDisplay.MonoFormatAnimation }), |
|
|
|
m(AnimationEditor, { si, ei, el: el as MonoDisplay.MonoFormatAnimation }), |
|
|
|
) |
|
|
|
) |
|
|
|
: null, |
|
|
|
: null, |
|
|
|
), |
|
|
|
), |
|
|
|
); |
|
|
|
); |
|
|
|
}, |
|
|
|
}, |
|
|
|
@ -674,7 +682,7 @@ const SectionCard: m.Component<{ si: number }> = { |
|
|
|
const section = file.sections[si]; |
|
|
|
const section = file.sections[si]; |
|
|
|
if (!section) return null; |
|
|
|
if (!section) return null; |
|
|
|
const isActive = activeSecIndex === si; |
|
|
|
const isActive = activeSecIndex === si; |
|
|
|
const typeStr = MonoDisplay.SectionTypeToString[section.sectionType]; |
|
|
|
const typeStr = MonoDisplay.SectionTypeToString[section.sectionType]; |
|
|
|
|
|
|
|
|
|
|
|
const hdr = m(`.sec-hdr${isActive ? ".active" : ""}`, { onclick: () => toggleSection(si) }, |
|
|
|
const hdr = m(`.sec-hdr${isActive ? ".active" : ""}`, { onclick: () => toggleSection(si) }, |
|
|
|
m("span.sec-arrow", "▶"), |
|
|
|
m("span.sec-arrow", "▶"), |
|
|
|
@ -714,26 +722,26 @@ const SectionCard: m.Component<{ si: number }> = { |
|
|
|
), |
|
|
|
), |
|
|
|
section.sectionType === MonoDisplay.SectionType.ElementsTimespan |
|
|
|
section.sectionType === MonoDisplay.SectionType.ElementsTimespan |
|
|
|
? m(".el-list", |
|
|
|
? m(".el-list", |
|
|
|
([ ["Start Time", "startTimestamp"], ["Stop Time", "endTimestamp"] ] as [string, string][]) |
|
|
|
([["Start Time", "startTimestamp"], ["Stop Time", "endTimestamp"]] as [string, string][]) |
|
|
|
.map(([label, key]) => { |
|
|
|
.map(([label, key]) => { |
|
|
|
let posix: bigint = (section as any)[key]; |
|
|
|
let posix: bigint = (section as any)[key]; |
|
|
|
if (!posix) { |
|
|
|
if (!posix) { |
|
|
|
posix = BigInt(Math.floor(Date.now() / 1000)); |
|
|
|
posix = BigInt(Math.floor(Date.now() / 1000)); |
|
|
|
setSectionField(si, key, posix); |
|
|
|
setSectionField(si, key, posix); |
|
|
|
} |
|
|
|
} |
|
|
|
const dtLocal = new Date(Number(posix) * 1000).toISOString().slice(0, 16); |
|
|
|
const dtLocal = new Date(Number(posix) * 1000).toISOString().slice(0, 16); |
|
|
|
return m(".field", |
|
|
|
return m(".field", |
|
|
|
m("label", label), |
|
|
|
m("label", label), |
|
|
|
m("input[type=datetime-local]", { |
|
|
|
m("input[type=datetime-local]", { |
|
|
|
value: dtLocal, |
|
|
|
value: dtLocal, |
|
|
|
onchange: (e: Event) => { |
|
|
|
onchange: (e: Event) => { |
|
|
|
const ms = new Date((e.target as HTMLInputElement).value).getTime(); |
|
|
|
const ms = new Date((e.target as HTMLInputElement).value).getTime(); |
|
|
|
setSectionField(si, key, BigInt(Math.floor(ms / 1000))); |
|
|
|
setSectionField(si, key, BigInt(Math.floor(ms / 1000))); |
|
|
|
}, |
|
|
|
}, |
|
|
|
}), |
|
|
|
}), |
|
|
|
); |
|
|
|
); |
|
|
|
}) |
|
|
|
}) |
|
|
|
) |
|
|
|
) |
|
|
|
: null, |
|
|
|
: null, |
|
|
|
m(".el-list", |
|
|
|
m(".el-list", |
|
|
|
elSection.elements.map((el, ei) => m(ElementItem, { key: String(ei), si, ei, el })), |
|
|
|
elSection.elements.map((el, ei) => m(ElementItem, { key: String(ei), si, ei, el })), |
|
|
|
@ -760,14 +768,14 @@ const MetaPanel: m.Component = { |
|
|
|
m("label", "Selected section"), |
|
|
|
m("label", "Selected section"), |
|
|
|
section |
|
|
|
section |
|
|
|
? m("select.meta-val.green", { |
|
|
|
? m("select.meta-val.green", { |
|
|
|
value: MonoDisplay.SectionTypeToString[section.sectionType], |
|
|
|
value: MonoDisplay.SectionTypeToString[section.sectionType], |
|
|
|
onchange: (e: Event) => |
|
|
|
onchange: (e: Event) => |
|
|
|
activeSecIndex !== null && setSectionType(activeSecIndex, (e.target as HTMLSelectElement).value), |
|
|
|
activeSecIndex !== null && setSectionType(activeSecIndex, (e.target as HTMLSelectElement).value), |
|
|
|
}, |
|
|
|
}, |
|
|
|
Object.values(MonoDisplay.SectionTypeToString).map(name => |
|
|
|
Object.values(MonoDisplay.SectionTypeToString).map(name => |
|
|
|
m("option", { value: name, selected: name === MonoDisplay.SectionTypeToString[section.sectionType] }, name) |
|
|
|
m("option", { value: name, selected: name === MonoDisplay.SectionTypeToString[section.sectionType] }, name) |
|
|
|
) |
|
|
|
|
|
|
|
) |
|
|
|
) |
|
|
|
|
|
|
|
) |
|
|
|
: m("span.meta-val.green", "-"), |
|
|
|
: m("span.meta-val.green", "-"), |
|
|
|
), |
|
|
|
), |
|
|
|
m(".meta-row", |
|
|
|
m(".meta-row", |
|
|
|
@ -796,30 +804,30 @@ const DemoButtons: m.Component = { |
|
|
|
view() { |
|
|
|
view() { |
|
|
|
return Object.entries(DEMO).map(([name, fn]) => |
|
|
|
return Object.entries(DEMO).map(([name, fn]) => |
|
|
|
m("button.tb-btn", { |
|
|
|
m("button.tb-btn", { |
|
|
|
style: activeDemoName === name ? "color:#33ff66" : "", |
|
|
|
style: activeDemoName === name ? "color:#33ff66" : "", |
|
|
|
onclick: async () => { |
|
|
|
onclick: async () => { |
|
|
|
const ok = await guardDirty(); |
|
|
|
const ok = await guardDirty(); |
|
|
|
if (!ok) return; |
|
|
|
if (!ok) return; |
|
|
|
file = fn(); |
|
|
|
file = fn(); |
|
|
|
activeSecIndex = null; |
|
|
|
activeSecIndex = null; |
|
|
|
activeElIndex = null; |
|
|
|
activeElIndex = null; |
|
|
|
currentFilename = name.toLowerCase(); |
|
|
|
currentFilename = name.toLowerCase(); |
|
|
|
const filenameEl = document.getElementById("filename"); |
|
|
|
const filenameEl = document.getElementById("filename"); |
|
|
|
if (filenameEl) filenameEl.textContent = currentFilename; |
|
|
|
if (filenameEl) filenameEl.textContent = currentFilename; |
|
|
|
activeDemoName = name; |
|
|
|
activeDemoName = name; |
|
|
|
markClean(); |
|
|
|
markClean(); |
|
|
|
triggerPreview(); |
|
|
|
triggerPreview(); |
|
|
|
m.redraw(); |
|
|
|
m.redraw(); |
|
|
|
}, |
|
|
|
}, |
|
|
|
}, name) |
|
|
|
}, name) |
|
|
|
); |
|
|
|
); |
|
|
|
}, |
|
|
|
}, |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// --- Mount --------------------------------------------------------------------
|
|
|
|
// --- Mount --------------------------------------------------------------------
|
|
|
|
m.mount(document.getElementById("sections-wrap")!, SectionsList); |
|
|
|
m.mount(document.getElementById("sections-wrap")!, SectionsList); |
|
|
|
m.mount(document.getElementById("demo-btns")!, DemoButtons); |
|
|
|
m.mount(document.getElementById("demo-btns")!, DemoButtons); |
|
|
|
m.mount(document.getElementById("sec-meta")!, MetaPanel); |
|
|
|
m.mount(document.getElementById("sec-meta")!, MetaPanel); |
|
|
|
|
|
|
|
|
|
|
|
async function clearAll() { |
|
|
|
async function clearAll() { |
|
|
|
if (isDirty && file.sections.length) { |
|
|
|
if (isDirty && file.sections.length) { |
|
|
|
|