|
|
|
|
@ -2,10 +2,8 @@ |
|
|
|
|
// Built by: bun build src/browser.ts --target browser --outfile public/mono-display.js
|
|
|
|
|
|
|
|
|
|
import m from "mithril"; |
|
|
|
|
import * as MonoDisplay from "libmonoformat"; |
|
|
|
|
import { MonoDisplayFile, cycleTheme } from "libmonoformat"; |
|
|
|
|
|
|
|
|
|
(globalThis as typeof globalThis & { MonoDisplay: typeof MonoDisplay }).MonoDisplay = MonoDisplay; |
|
|
|
|
|
|
|
|
|
const W = 120, H = 60; |
|
|
|
|
|
|
|
|
|
@ -20,7 +18,7 @@ let isDirty = false; |
|
|
|
|
type ElFieldMeta = { key: string; label: string; type: string; default: any; full?: boolean }; |
|
|
|
|
type ElFlagMeta = { key: string; label: string; default?: boolean }; |
|
|
|
|
|
|
|
|
|
const EL_FIELDS: Partial<Record<MonoDisplay.ElementTypeName, ElFieldMeta[]>> = { |
|
|
|
|
const EL_FIELDS: Partial<Record<ElementTypeName, ElFieldMeta[]>> = { |
|
|
|
|
Image2D: [ |
|
|
|
|
{ key: "xOffset", label: "X offset", type: "number", default: 0 }, |
|
|
|
|
{ key: "yOffset", label: "Y offset", type: "number", default: 0 }, |
|
|
|
|
@ -78,7 +76,7 @@ const EL_FIELDS: Partial<Record<MonoDisplay.ElementTypeName, ElFieldMeta[]>> = { |
|
|
|
|
], |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
const EL_FLAGS: Partial<Record<MonoDisplay.ElementTypeName, ElFlagMeta[]>> = { |
|
|
|
|
const EL_FLAGS: Partial<Record<ElementTypeName, ElFlagMeta[]>> = { |
|
|
|
|
HScrollText: [ |
|
|
|
|
{ key: "endless", label: "Endless" }, |
|
|
|
|
{ key: "invertDirection", label: "Invert direction" }, |
|
|
|
|
@ -92,11 +90,11 @@ const EL_FLAGS: Partial<Record<MonoDisplay.ElementTypeName, ElFlagMeta[]>> = { |
|
|
|
|
], |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
const EL_TYPES: MonoDisplay.ElementType[] = Object.keys(EL_FIELDS).map( |
|
|
|
|
(x) => MonoDisplay.StringToElementType[x as MonoDisplay.ElementTypeName] |
|
|
|
|
const EL_TYPES: ElementType[] = Object.keys(EL_FIELDS).map( |
|
|
|
|
(x) => StringToElementType[x as ElementTypeName] |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
const DEFAULT_FONTS = Object.keys(MonoDisplay.MonoDisplayRenderer.builtinFonts); |
|
|
|
|
const DEFAULT_FONTS = Object.keys(MonoDisplayRenderer.builtinFonts); |
|
|
|
|
|
|
|
|
|
// --- Confirm dialog ----------------------------------------------------------
|
|
|
|
|
function showConfirm( |
|
|
|
|
@ -137,7 +135,7 @@ function loadFromStorage() { |
|
|
|
|
if (!raw) return; |
|
|
|
|
const { filename, data } = JSON.parse(raw); |
|
|
|
|
const bytes = Uint8Array.from(atob(data), c => c.charCodeAt(0)); |
|
|
|
|
const parsed = new MonoDisplay.MonoDisplayParser().parse(bytes.buffer); |
|
|
|
|
const parsed = new MonoDisplayParser().parse(bytes.buffer); |
|
|
|
|
file = new MonoDisplayFile(parsed.sections); |
|
|
|
|
currentFilename = filename; |
|
|
|
|
const filenameEl = document.getElementById("filename"); |
|
|
|
|
@ -167,9 +165,9 @@ async function guardDirty(): Promise<boolean> { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// --- Helpers -----------------------------------------------------------------
|
|
|
|
|
function newSec(): MonoDisplay.MonoFormatElementsAlways { |
|
|
|
|
function newSec(): MonoFormatElementsAlways { |
|
|
|
|
return { |
|
|
|
|
sectionType: MonoDisplay.SectionType.ElementsAlways, elements: [], flags: { |
|
|
|
|
sectionType: SectionType.ElementsAlways, elements: [], flags: { |
|
|
|
|
clearBuffer: true, |
|
|
|
|
drawBack: true, |
|
|
|
|
drawFront: true, |
|
|
|
|
@ -177,47 +175,47 @@ function newSec(): MonoDisplay.MonoFormatElementsAlways { |
|
|
|
|
}; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function createNewElement(type: MonoDisplay.ElementType): MonoDisplay.MonoFormatElement { |
|
|
|
|
function createNewElement(type: ElementType): MonoFormatElement { |
|
|
|
|
const fields: Record<string, any> = {}; |
|
|
|
|
(EL_FIELDS[MonoDisplay.ElementTypeToString[type]] as ElFieldMeta[] || []) |
|
|
|
|
(EL_FIELDS[ElementTypeToString[type]] as ElFieldMeta[] || []) |
|
|
|
|
.forEach(f => (fields[f.key] = f.default)); |
|
|
|
|
const flags: Record<string, any> = {}; |
|
|
|
|
(EL_FLAGS[MonoDisplay.ElementTypeToString[type]] as ElFlagMeta[] || []) |
|
|
|
|
(EL_FLAGS[ElementTypeToString[type]] as ElFlagMeta[] || []) |
|
|
|
|
.forEach(f => (flags[f.key] = f.default ?? false)); |
|
|
|
|
const el: any = { type, ...fields, flags }; |
|
|
|
|
if (type === MonoDisplay.ElementType.Image2D) { |
|
|
|
|
if (type === ElementType.Image2D) { |
|
|
|
|
el.image = { pixels: new Uint8Array(el.width * el.height), width: el.width, height: el.height }; |
|
|
|
|
} |
|
|
|
|
if (type === MonoDisplay.ElementType.Animation) { |
|
|
|
|
if (type === ElementType.Animation) { |
|
|
|
|
el.frames = [{ pixels: new Uint8Array(el.width * el.height), width: el.width, height: el.height }]; |
|
|
|
|
} |
|
|
|
|
return el as MonoDisplay.MonoFormatElement; |
|
|
|
|
return el as MonoFormatElement; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function getSectionByIndex(i: number | null): MonoDisplay.MonoFormatSection | undefined { |
|
|
|
|
function getSectionByIndex(i: number | null): MonoFormatSection | undefined { |
|
|
|
|
return i !== null ? file.sections[i] : undefined; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function getElementByIndex(si: number, ei: number): MonoDisplay.MonoFormatElement | undefined { |
|
|
|
|
function getElementByIndex(si: number, ei: number): MonoFormatElement | undefined { |
|
|
|
|
const s = getSectionByIndex(si); |
|
|
|
|
return s && "elements" in s ? s.elements[ei] : undefined; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function getCustomFonts(): { fontname: string; index: number }[] { |
|
|
|
|
return file.sections |
|
|
|
|
.filter(s => s.sectionType === MonoDisplay.SectionType.CustomFont) |
|
|
|
|
.filter(s => s.sectionType === SectionType.CustomFont) |
|
|
|
|
.map((_, i) => ({ fontname: `CustomFont ${i}`, index: 0x8000 + i })); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function elSummary(el: MonoDisplay.MonoFormatElement): string { |
|
|
|
|
function elSummary(el: MonoFormatElement): string { |
|
|
|
|
switch (el.type) { |
|
|
|
|
case MonoDisplay.ElementType.ClippedText: |
|
|
|
|
case MonoDisplay.ElementType.HScrollText: |
|
|
|
|
case ElementType.ClippedText: |
|
|
|
|
case ElementType.HScrollText: |
|
|
|
|
return el.text.slice(0, 24) + (el.text.length > 24 ? "..." : ""); |
|
|
|
|
case MonoDisplay.ElementType.CurrentTime: |
|
|
|
|
case MonoDisplay.ElementType.Image2D: |
|
|
|
|
case MonoDisplay.ElementType.HorizontalScroll: |
|
|
|
|
case MonoDisplay.ElementType.VerticalScroll: |
|
|
|
|
case ElementType.CurrentTime: |
|
|
|
|
case ElementType.Image2D: |
|
|
|
|
case ElementType.HorizontalScroll: |
|
|
|
|
case ElementType.VerticalScroll: |
|
|
|
|
return `${(el as any).xOffset ?? 0}, ${(el as any).yOffset ?? 0}`; |
|
|
|
|
default: |
|
|
|
|
return el.type.toString(); |
|
|
|
|
@ -263,9 +261,9 @@ function setSectionField(si: number, key: string, val: any) { |
|
|
|
|
function setSectionType(si: number, sectionType: string) { |
|
|
|
|
const sec = getSectionByIndex(si); |
|
|
|
|
if (!sec) return; |
|
|
|
|
const wasElementsSection = sec.sectionType !== MonoDisplay.SectionType.CustomFont; |
|
|
|
|
(sec as any).sectionType = MonoDisplay.StringToSectionType[sectionType as MonoDisplay.SectionTypeName]; |
|
|
|
|
if ((sec as any).sectionType === MonoDisplay.SectionType.CustomFont) { |
|
|
|
|
const wasElementsSection = sec.sectionType !== SectionType.CustomFont; |
|
|
|
|
(sec as any).sectionType = StringToSectionType[sectionType as SectionTypeName]; |
|
|
|
|
if ((sec as any).sectionType === SectionType.CustomFont) { |
|
|
|
|
(sec as any).fontData = new Uint8Array(); |
|
|
|
|
delete (sec as any).elements; |
|
|
|
|
delete (sec as any).flags; |
|
|
|
|
@ -275,7 +273,7 @@ function setSectionType(si: number, sectionType: string) { |
|
|
|
|
(sec as any).flags = {}; |
|
|
|
|
} |
|
|
|
|
delete (sec as any).fontData; |
|
|
|
|
if ((sec as any).sectionType === MonoDisplay.SectionType.ElementsTimespan) { |
|
|
|
|
if ((sec as any).sectionType === SectionType.ElementsTimespan) { |
|
|
|
|
const now = BigInt(Math.floor(Date.now() / 1000)); |
|
|
|
|
(sec as any).startTimestamp = now; |
|
|
|
|
(sec as any).endTimestamp = now + 3600n; |
|
|
|
|
@ -290,7 +288,7 @@ function setSectionType(si: number, sectionType: string) { |
|
|
|
|
function addElement(si: number) { |
|
|
|
|
const s = getSectionByIndex(si); |
|
|
|
|
if (!s || !("elements" in s)) return; |
|
|
|
|
s.elements.push(createNewElement(MonoDisplay.ElementType.HScrollText)); |
|
|
|
|
s.elements.push(createNewElement(ElementType.HScrollText)); |
|
|
|
|
activeElIndex = s.elements.length - 1; |
|
|
|
|
activeSecIndex = si; |
|
|
|
|
markDirty(); triggerPreview(); |
|
|
|
|
@ -313,10 +311,10 @@ function selectElement(si: number, ei: number) { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function changeElType(si: number, ei: number, typeString: MonoDisplay.ElementTypeName) { |
|
|
|
|
function changeElType(si: number, ei: number, typeString: ElementTypeName) { |
|
|
|
|
const el = getElementByIndex(si, ei); |
|
|
|
|
if (!el) return; |
|
|
|
|
const fresh = createNewElement(MonoDisplay.StringToElementType[typeString]); |
|
|
|
|
const fresh = createNewElement(StringToElementType[typeString]); |
|
|
|
|
Object.assign(el, fresh); |
|
|
|
|
markDirty(); triggerPreview(); |
|
|
|
|
} |
|
|
|
|
@ -341,16 +339,16 @@ const DEMO: Record<string, () => MonoDisplayFile> = { |
|
|
|
|
const pixels = new Uint8Array(W * H); |
|
|
|
|
for (let y = 0; y < H; y++) for (let x = 0; x < W; x++) pixels[y * W + x] = (x + y) % 2; |
|
|
|
|
return new MonoDisplayFile([{ |
|
|
|
|
sectionType: MonoDisplay.SectionType.ElementsAlways, |
|
|
|
|
elements: [{ type: MonoDisplay.ElementType.Image2D, image: { pixels, height: H, width: W }, xOffset: 0, yOffset: 0 }], |
|
|
|
|
sectionType: SectionType.ElementsAlways, |
|
|
|
|
elements: [{ type: ElementType.Image2D, image: { pixels, height: H, width: W }, xOffset: 0, yOffset: 0 }], |
|
|
|
|
flags: { clearBuffer: true, drawFront: true, drawBack: true }, |
|
|
|
|
}]); |
|
|
|
|
}, |
|
|
|
|
Blink() { |
|
|
|
|
return new MonoDisplayFile([{ |
|
|
|
|
sectionType: MonoDisplay.SectionType.ElementsAlways, |
|
|
|
|
sectionType: SectionType.ElementsAlways, |
|
|
|
|
elements: [{ |
|
|
|
|
type: MonoDisplay.ElementType.Animation, width: W, height: H, updateInterval: 12, xOffset: 0, yOffset: 0, |
|
|
|
|
type: ElementType.Animation, width: W, height: H, updateInterval: 12, xOffset: 0, yOffset: 0, |
|
|
|
|
frames: [ |
|
|
|
|
{ pixels: new Uint8Array(W * H).fill(1), width: 0, height: 0 }, |
|
|
|
|
{ pixels: new Uint8Array(W * H).fill(0), width: 0, height: 0 }, |
|
|
|
|
@ -361,16 +359,16 @@ const DEMO: Record<string, () => MonoDisplayFile> = { |
|
|
|
|
}, |
|
|
|
|
Text() { |
|
|
|
|
return new MonoDisplayFile([{ |
|
|
|
|
sectionType: MonoDisplay.SectionType.ElementsAlways, |
|
|
|
|
elements: [{ type: MonoDisplay.ElementType.ClippedText, fontIndex: 0, text: "Hello, World!", xOffset: 0, yOffset: 16, width: 60, height: 10 }], |
|
|
|
|
sectionType: SectionType.ElementsAlways, |
|
|
|
|
elements: [{ type: ElementType.ClippedText, fontIndex: 0, text: "Hello, World!", xOffset: 0, yOffset: 16, width: 60, height: 10 }], |
|
|
|
|
flags: { clearBuffer: true, drawFront: true, drawBack: true }, |
|
|
|
|
}]); |
|
|
|
|
}, |
|
|
|
|
Scrolltext() { |
|
|
|
|
return new MonoDisplayFile([{ |
|
|
|
|
sectionType: MonoDisplay.SectionType.ElementsAlways, |
|
|
|
|
sectionType: SectionType.ElementsAlways, |
|
|
|
|
elements: [{ |
|
|
|
|
type: MonoDisplay.ElementType.HScrollText, fontIndex: 0, |
|
|
|
|
type: ElementType.HScrollText, fontIndex: 0, |
|
|
|
|
text: "MONO DISPLAY - scrolling ticker - 🚀 ", |
|
|
|
|
xOffset: 0, yOffset: 32, width: W, height: 16, scrollSpeed: 50, |
|
|
|
|
flags: { endless: true, invertDirection: false, padStart: false, padEnd: false }, |
|
|
|
|
@ -380,13 +378,13 @@ const DEMO: Record<string, () => MonoDisplayFile> = { |
|
|
|
|
}, |
|
|
|
|
Time() { |
|
|
|
|
return new MonoDisplayFile([{ |
|
|
|
|
sectionType: MonoDisplay.SectionType.ElementsAlways, |
|
|
|
|
sectionType: SectionType.ElementsAlways, |
|
|
|
|
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: { 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: 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: ElementType.CurrentTime, fontIndex: 0, flags: {}, xOffset: 0, yOffset: 8, width: W, height: 16, utcOffsetMinutes: 120 }, |
|
|
|
|
{ type: ElementType.CurrentTime, fontIndex: 0, flags: { clock12h: true }, xOffset: 40, yOffset: 16, width: W, height: 16, utcOffsetMinutes: 120 }, |
|
|
|
|
{ type: ElementType.CurrentTime, fontIndex: 0, flags: { clock12h: true, showHours: true }, xOffset: 0, yOffset: 24, width: W, height: 16, utcOffsetMinutes: 120 }, |
|
|
|
|
{ type: ElementType.CurrentTime, fontIndex: 0, flags: { clock12h: true, showHours: false }, xOffset: 40, yOffset: 32, width: W, height: 16, utcOffsetMinutes: 120 }, |
|
|
|
|
{ type: ElementType.CurrentTime, fontIndex: 0, flags: { clock12h: true, showSeconds: true }, xOffset: 80, yOffset: 32, width: W, height: 16, utcOffsetMinutes: 120 }, |
|
|
|
|
], |
|
|
|
|
flags: { clearBuffer: true, drawFront: true, drawBack: true }, |
|
|
|
|
}]); |
|
|
|
|
@ -406,7 +404,7 @@ function triggerPreview() { |
|
|
|
|
function buildPreview() { |
|
|
|
|
if (!window.MonoDisplay) return; |
|
|
|
|
if (!window._mdDriver) |
|
|
|
|
window._mdDriver = new MonoDisplay.MonoDisplayDriver("canvas_root", { onColor: "#EC0", offColor: "#000", fps: 25 }); |
|
|
|
|
window._mdDriver = new MonoDisplayDriver("canvas_root", { onColor: "#EC0", offColor: "#000", fps: 25 }); |
|
|
|
|
window._mdDriver.load(() => Promise.resolve(file.toBuffer())); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -420,7 +418,7 @@ function loadBin(input: HTMLInputElement) { |
|
|
|
|
const reader = new FileReader(); |
|
|
|
|
reader.onload = e => { |
|
|
|
|
try { |
|
|
|
|
const parsed = new window.MonoDisplay.MonoDisplayParser().parse(e.target!.result as ArrayBuffer); |
|
|
|
|
const parsed = new window.MonoDisplayParser().parse(e.target!.result as ArrayBuffer); |
|
|
|
|
file = new MonoDisplayFile(parsed.sections); |
|
|
|
|
activeSecIndex = null; |
|
|
|
|
activeElIndex = null; |
|
|
|
|
@ -447,7 +445,7 @@ function exportBin() { |
|
|
|
|
|
|
|
|
|
// --- Mithril Components -------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
function FieldInput(si: number, ei: number, field: ElFieldMeta, el: MonoDisplay.MonoFormatElement): m.Vnode { |
|
|
|
|
function FieldInput(si: number, ei: number, field: ElFieldMeta, el: MonoFormatElement): m.Vnode { |
|
|
|
|
const val = (el as any)[field.key] ?? field.default; |
|
|
|
|
switch (field.type) { |
|
|
|
|
case "text": |
|
|
|
|
@ -475,11 +473,11 @@ function FieldInput(si: number, ei: number, field: ElFieldMeta, el: MonoDisplay. |
|
|
|
|
|
|
|
|
|
const PIXEL_SCALE = 4; |
|
|
|
|
|
|
|
|
|
function getPixelIndex(img: MonoDisplay.MonoFormatPixelImage, x: number, y: number): number { |
|
|
|
|
function getPixelIndex(img: MonoFormatPixelImage, x: number, y: number): number { |
|
|
|
|
return y * img.width + x; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function drawPixelCanvas(canvas: HTMLCanvasElement, img: MonoDisplay.MonoFormatPixelImage) { |
|
|
|
|
function drawPixelCanvas(canvas: HTMLCanvasElement, img: MonoFormatPixelImage) { |
|
|
|
|
const ctx = canvas.getContext("2d")!; |
|
|
|
|
ctx.fillStyle = "#000"; |
|
|
|
|
ctx.fillRect(0, 0, canvas.width, canvas.height); |
|
|
|
|
@ -495,7 +493,7 @@ function drawPixelCanvas(canvas: HTMLCanvasElement, img: MonoDisplay.MonoFormatP |
|
|
|
|
|
|
|
|
|
interface PixelCanvasState { drawing: boolean; drawValue: number; } |
|
|
|
|
|
|
|
|
|
const PixelCanvas: m.Component<{ img: MonoDisplay.MonoFormatPixelImage; onpaint: () => void }, PixelCanvasState> = { |
|
|
|
|
const PixelCanvas: m.Component<{ img: MonoFormatPixelImage; onpaint: () => void }, PixelCanvasState> = { |
|
|
|
|
drawing: false, |
|
|
|
|
drawValue: 1, |
|
|
|
|
|
|
|
|
|
@ -586,7 +584,7 @@ const PixelCanvas: m.Component<{ img: MonoDisplay.MonoFormatPixelImage; onpaint: |
|
|
|
|
|
|
|
|
|
// --- Image2D editor -----------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
const Image2DEditor: m.Component<{ si: number; ei: number; el: MonoDisplay.MonoFormatImage2D }> = { |
|
|
|
|
const Image2DEditor: m.Component<{ si: number; ei: number; el: MonoFormatImage2D }> = { |
|
|
|
|
view({ attrs: { si, ei, el } }) { |
|
|
|
|
if (!el.image) { |
|
|
|
|
const w = (el as any).width || W; |
|
|
|
|
@ -604,7 +602,7 @@ const Image2DEditor: m.Component<{ si: number; ei: number; el: MonoDisplay.MonoF |
|
|
|
|
|
|
|
|
|
interface AnimationEditorState { activeFrame: number; } |
|
|
|
|
|
|
|
|
|
const AnimationEditor: m.Component<{ si: number; ei: number; el: MonoDisplay.MonoFormatAnimation }, AnimationEditorState> = { |
|
|
|
|
const AnimationEditor: m.Component<{ si: number; ei: number; el: MonoFormatAnimation }, AnimationEditorState> = { |
|
|
|
|
activeFrame: 0, |
|
|
|
|
|
|
|
|
|
view({ attrs: { si, ei, el }, state }) { |
|
|
|
|
@ -658,10 +656,10 @@ const AnimationEditor: m.Component<{ si: number; ei: number; el: MonoDisplay.Mon |
|
|
|
|
}, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
const ElementItem: m.Component<{ si: number; ei: number; el: MonoDisplay.MonoFormatElement }> = { |
|
|
|
|
const ElementItem: m.Component<{ si: number; ei: number; el: MonoFormatElement }> = { |
|
|
|
|
view({ attrs: { si, ei, el } }) { |
|
|
|
|
const isActive = activeSecIndex === si && activeElIndex === ei; |
|
|
|
|
const typeStr = MonoDisplay.ElementTypeToString[el.type]; |
|
|
|
|
const typeStr = ElementTypeToString[el.type]; |
|
|
|
|
const fields = EL_FIELDS[typeStr] || []; |
|
|
|
|
const flags = EL_FLAGS[typeStr] || []; |
|
|
|
|
|
|
|
|
|
@ -678,10 +676,10 @@ const ElementItem: m.Component<{ si: number; ei: number; el: MonoDisplay.MonoFor |
|
|
|
|
m(".field.full", |
|
|
|
|
m("label", "Type"), |
|
|
|
|
m("select", { |
|
|
|
|
onchange: (e: Event) => changeElType(si, ei, (e.target as HTMLSelectElement).value as MonoDisplay.ElementTypeName), |
|
|
|
|
onchange: (e: Event) => changeElType(si, ei, (e.target as HTMLSelectElement).value as ElementTypeName), |
|
|
|
|
}, |
|
|
|
|
EL_TYPES.map(t => |
|
|
|
|
m("option", { value: MonoDisplay.ElementTypeToString[t], selected: t === el.type }, MonoDisplay.ElementTypeToString[t]) |
|
|
|
|
m("option", { value: ElementTypeToString[t], selected: t === el.type }, ElementTypeToString[t]) |
|
|
|
|
) |
|
|
|
|
), |
|
|
|
|
), |
|
|
|
|
@ -707,15 +705,15 @@ const ElementItem: m.Component<{ si: number; ei: number; el: MonoDisplay.MonoFor |
|
|
|
|
) |
|
|
|
|
), |
|
|
|
|
) : null, |
|
|
|
|
el.type === MonoDisplay.ElementType.Image2D |
|
|
|
|
el.type === ElementType.Image2D |
|
|
|
|
? m(".field.full", |
|
|
|
|
m("label", "Pixels"), |
|
|
|
|
m(Image2DEditor, { si, ei, el: el as MonoDisplay.MonoFormatImage2D }), |
|
|
|
|
m(Image2DEditor, { si, ei, el: el as MonoFormatImage2D }), |
|
|
|
|
) |
|
|
|
|
: el.type === MonoDisplay.ElementType.Animation |
|
|
|
|
: el.type === ElementType.Animation |
|
|
|
|
? m(".field.full", |
|
|
|
|
m("label", "Frames"), |
|
|
|
|
m(AnimationEditor, { si, ei, el: el as MonoDisplay.MonoFormatAnimation }), |
|
|
|
|
m(AnimationEditor, { si, ei, el: el as MonoFormatAnimation }), |
|
|
|
|
) |
|
|
|
|
: null, |
|
|
|
|
), |
|
|
|
|
@ -728,7 +726,7 @@ const SectionCard: m.Component<{ si: number }> = { |
|
|
|
|
const section = file.sections[si]; |
|
|
|
|
if (!section) return null; |
|
|
|
|
const isActive = activeSecIndex === si; |
|
|
|
|
const typeStr = MonoDisplay.SectionTypeToString[section.sectionType]; |
|
|
|
|
const typeStr = SectionTypeToString[section.sectionType]; |
|
|
|
|
|
|
|
|
|
const hdr = m(`.sec-hdr${isActive ? ".active" : ""}`, { onclick: () => toggleSection(si) }, |
|
|
|
|
m("span.sec-arrow", "▶"), |
|
|
|
|
@ -739,7 +737,7 @@ const SectionCard: m.Component<{ si: number }> = { |
|
|
|
|
}, "X"), |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
if (section.sectionType === MonoDisplay.SectionType.CustomFont) { |
|
|
|
|
if (section.sectionType === SectionType.CustomFont) { |
|
|
|
|
return m(`.sec-card.open${isActive ? ".active" : ""}`, |
|
|
|
|
m(`.sec-hdr${isActive ? ".active" : ""}`, { onclick: () => toggleSection(si) }, |
|
|
|
|
m("span.sec-arrow", "▶"), |
|
|
|
|
@ -754,7 +752,7 @@ const SectionCard: m.Component<{ si: number }> = { |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const elSection = section as MonoDisplay.MonoFormatElementsAlways | MonoDisplay.MonoFormatElementsTimespan; |
|
|
|
|
const elSection = section as MonoFormatElementsAlways | MonoFormatElementsTimespan; |
|
|
|
|
|
|
|
|
|
return m(`.sec-card.open${isActive ? ".active" : ""}`, |
|
|
|
|
m(`.sec-hdr${isActive ? ".active" : ""}`, { onclick: () => toggleSection(si) }, |
|
|
|
|
@ -766,7 +764,7 @@ const SectionCard: m.Component<{ si: number }> = { |
|
|
|
|
onclick: (e: Event) => { e.stopPropagation(); removeSection(si); }, |
|
|
|
|
}, "X"), |
|
|
|
|
), |
|
|
|
|
section.sectionType === MonoDisplay.SectionType.ElementsTimespan |
|
|
|
|
section.sectionType === SectionType.ElementsTimespan |
|
|
|
|
? m(".el-list", |
|
|
|
|
([["Start Time", "startTimestamp"], ["Stop Time", "endTimestamp"]] as [string, string][]) |
|
|
|
|
.map(([label, key]) => { |
|
|
|
|
@ -814,12 +812,12 @@ const MetaPanel: m.Component = { |
|
|
|
|
m("label", "Selected section"), |
|
|
|
|
section |
|
|
|
|
? m("select.meta-val.green", { |
|
|
|
|
value: MonoDisplay.SectionTypeToString[section.sectionType], |
|
|
|
|
value: SectionTypeToString[section.sectionType], |
|
|
|
|
onchange: (e: Event) => |
|
|
|
|
activeSecIndex !== null && setSectionType(activeSecIndex, (e.target as HTMLSelectElement).value), |
|
|
|
|
}, |
|
|
|
|
Object.values(MonoDisplay.SectionTypeToString).map(name => |
|
|
|
|
m("option", { value: name, selected: name === MonoDisplay.SectionTypeToString[section.sectionType] }, name) |
|
|
|
|
Object.values(SectionTypeToString).map(name => |
|
|
|
|
m("option", { value: name, selected: name === SectionTypeToString[section.sectionType] }, name) |
|
|
|
|
) |
|
|
|
|
) |
|
|
|
|
: m("span.meta-val.green", "-"), |
|
|
|
|
|