class FilePreview extends Window { _visible = false _file = null _url = null open(file, url) { this._file = file this._url = url this._visible = true this.rerender() } close() { this._visible = false this.rerender() } render() { this.style.position = "fixed" this.style.inset = "0" this.style.zIndex = "300" this.style.pointerEvents = this._visible ? "all" : "none" if (!this._visible) return const x = this.getX() const y = this.getY() const w = this.getWidth() const h = this.getHeight() const type = this._file?.type ?? "" const isImage = type.startsWith("image/") const isPDF = type === "application/pdf" const isOffice = [ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/vnd.openxmlformats-officedocument.presentationml.presentation', 'application/msword', 'application/vnd.ms-excel', 'application/vnd.ms-powerpoint', 'text/plain', ].includes(type) const displayName = this._file?.original_name ?? this._file?.name ?? "File" VStack(() => { const panel = VStack(() => { // Header HStack(() => { HStack(() => { div().attr({ class: "tl tl-close" }).onClick((done) => { if (done) this.close() }) div().attr({ class: "tl tl-min" }) div().attr({ class: "tl tl-max" }) }) .attr({ class: "traffic-lights" }) .flexShrink(0) p(displayName) .margin(0) .fontSize(0.88, em) .fontWeight("600") .color("var(--headertext)") .overflow("hidden") .whiteSpace("nowrap") .textOverflow("ellipsis") .flex(1) .textAlign("center") div().width("52px").flexShrink(0) }) .paddingHorizontal(1, em) .paddingVertical(0.75, em) .alignItems("center") .gap(1, em) .background("var(--darkaccent)") .borderBottom("1px solid var(--divider)") .width(100, pct) .boxSizing("border-box") .flexShrink(0) // Content if (isImage) { img(this._url, "auto", "auto") .display("block") .maxWidth(100, pct) .maxHeight(h - 48, px) .margin("0 auto") } else if (isPDF) { const wrap = div() .flex(1) .width(100, pct) .overflow("hidden") wrap.innerHTML = `` } else { const icon = { 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': "📄", 'application/msword': "📄", 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': "📊", 'application/vnd.ms-excel': "📊", 'application/vnd.openxmlformats-officedocument.presentationml.presentation': "📋", 'application/vnd.ms-powerpoint': "📋", 'text/plain': "📄", }[type] ?? "📎" VStack(() => { p(icon).fontSize(3, em).margin(0) p(displayName) .margin(0) .fontSize(0.9, em) .color("var(--headertext)") .textAlign("center") a(this._url, "Download") .attr({ download: displayName }) .fontSize(0.85, em) .color("var(--quillred)") .fontWeight("600") .fontFamily("Arial") .textDecoration("none") .cursor("pointer") .marginTop(0.25, em) .onHover(function(hovering) { this.style.textDecoration = hovering ? "underline" : "none" }) }) .gap(0.5, em) .alignItems("center") .padding(2, em) } }) .background("var(--window)") .backdropFilter("blur(18px)") .border("0.5px solid var(--window-border)") .borderRadius(12, px) .overflow("hidden") .width(100, pct) .boxSizing("border-box") if (isPDF) panel.height(h, px) else panel.maxHeight(h, px) panel.onClick((done, e) => { e.stopPropagation() }) }) .position("fixed") .x(x, px) .y(y, px) .width(w, px) .height(h, px) .justifyContent(isPDF ? "flex-start" : "center") .alignItems("center") .onClick((done) => { if (done) this.close() }) .onEvent("resize", () => this.rerender()) } } register(FilePreview)