Monorepo for Aesthetic.Computer
aesthetic.computer
1(function initACMediaModal(global) {
2 if (global.ACMediaModal) return;
3
4 function ensureStyles() {
5 if (document.getElementById("ac-media-modal-styles")) return;
6 const style = document.createElement("style");
7 style.id = "ac-media-modal-styles";
8 style.textContent = `
9 .ac-media-modal {
10 position: fixed;
11 inset: 0;
12 z-index: 9999;
13 display: none;
14 }
15 .ac-media-modal.active {
16 display: block;
17 }
18 .ac-media-modal-backdrop {
19 position: absolute;
20 inset: 0;
21 background: rgba(0, 0, 0, 0.75);
22 }
23 .ac-media-modal-panel {
24 position: relative;
25 z-index: 1;
26 width: min(1000px, calc(100vw - 2rem));
27 max-height: calc(100vh - 2rem);
28 margin: 1rem auto;
29 overflow: hidden;
30 background: #fff;
31 color: #000;
32 border: 2px solid rgba(205, 92, 155, 0.55);
33 border-radius: 8px;
34 display: flex;
35 flex-direction: column;
36 box-shadow: 0 24px 80px rgba(0, 0, 0, 0.5);
37 }
38 .ac-media-modal-header {
39 padding: 0.9rem 1rem;
40 border-bottom: 1px solid rgba(205, 92, 155, 0.25);
41 background: rgba(205, 92, 155, 0.1);
42 }
43 .ac-media-modal-title {
44 margin: 0;
45 font-size: 1rem;
46 line-height: 1.3;
47 }
48 .ac-media-modal-subtitle {
49 margin-top: 0.3rem;
50 opacity: 0.7;
51 font-size: 0.8rem;
52 }
53 .ac-media-modal-close {
54 position: absolute;
55 top: 0.45rem;
56 right: 0.55rem;
57 border: 1px solid rgba(205, 92, 155, 0.45);
58 border-radius: 4px;
59 background: #fff;
60 color: rgb(205, 92, 155);
61 font-family: monospace;
62 font-size: 0.8rem;
63 cursor: pointer;
64 padding: 0.25rem 0.5rem;
65 }
66 .ac-media-modal-main {
67 padding: 0.9rem 1rem 1rem;
68 overflow: auto;
69 display: grid;
70 gap: 0.8rem;
71 }
72 .ac-media-modal-body {
73 font-size: 0.85rem;
74 line-height: 1.5;
75 }
76 .ac-media-modal-iframe-wrap {
77 border: 1px solid rgba(205, 92, 155, 0.25);
78 border-radius: 6px;
79 overflow: hidden;
80 min-height: 320px;
81 }
82 .ac-media-modal-iframe {
83 width: 100%;
84 height: min(60vh, 560px);
85 border: none;
86 display: block;
87 background: #fff;
88 }
89 .ac-media-modal-actions {
90 display: flex;
91 flex-wrap: wrap;
92 gap: 0.5rem;
93 }
94 .ac-media-modal-action {
95 display: inline-block;
96 padding: 0.38rem 0.65rem;
97 border-radius: 4px;
98 border: 1px solid rgba(205, 92, 155, 0.45);
99 background: rgba(205, 92, 155, 0.12);
100 color: rgb(205, 92, 155);
101 text-decoration: none;
102 font-size: 0.78rem;
103 font-family: monospace;
104 }
105 .ac-media-modal-action:hover {
106 background: rgba(205, 92, 155, 0.2);
107 }
108 @media (prefers-color-scheme: dark) {
109 .ac-media-modal-panel {
110 background: rgb(42, 37, 50);
111 color: rgba(255, 255, 255, 0.92);
112 border-color: rgba(205, 92, 155, 0.6);
113 }
114 .ac-media-modal-header {
115 background: rgba(205, 92, 155, 0.15);
116 }
117 .ac-media-modal-close {
118 background: rgba(255, 255, 255, 0.08);
119 }
120 .ac-media-modal-iframe-wrap {
121 border-color: rgba(255, 255, 255, 0.12);
122 }
123 }
124 `;
125 document.head.appendChild(style);
126 }
127
128 function ensureModal() {
129 let modal = document.getElementById("ac-media-modal");
130 if (modal) return modal;
131
132 modal = document.createElement("div");
133 modal.id = "ac-media-modal";
134 modal.className = "ac-media-modal";
135 modal.innerHTML = `
136 <div class="ac-media-modal-backdrop"></div>
137 <div class="ac-media-modal-panel" role="dialog" aria-modal="true">
138 <button class="ac-media-modal-close" type="button" aria-label="Close">Close</button>
139 <div class="ac-media-modal-header">
140 <h3 class="ac-media-modal-title"></h3>
141 <div class="ac-media-modal-subtitle"></div>
142 </div>
143 <div class="ac-media-modal-main">
144 <div class="ac-media-modal-body"></div>
145 <div class="ac-media-modal-iframe-wrap" hidden>
146 <iframe class="ac-media-modal-iframe" loading="lazy" referrerpolicy="no-referrer"></iframe>
147 </div>
148 <div class="ac-media-modal-actions"></div>
149 </div>
150 </div>
151 `;
152
153 const close = function close() {
154 modal.classList.remove("active");
155 const iframe = modal.querySelector(".ac-media-modal-iframe");
156 if (iframe) iframe.src = "";
157 document.body.style.overflow = "";
158 document.removeEventListener("keydown", onEscape);
159 };
160
161 const onEscape = function onEscape(event) {
162 if (event.key === "Escape") close();
163 };
164
165 modal.querySelector(".ac-media-modal-close").addEventListener("click", close);
166 modal
167 .querySelector(".ac-media-modal-backdrop")
168 .addEventListener("click", close);
169
170 modal.__acClose = close;
171 modal.__acOnEscape = onEscape;
172
173 document.body.appendChild(modal);
174 return modal;
175 }
176
177 function open(options) {
178 const opts = options || {};
179 ensureStyles();
180 const modal = ensureModal();
181
182 const titleEl = modal.querySelector(".ac-media-modal-title");
183 const subtitleEl = modal.querySelector(".ac-media-modal-subtitle");
184 const bodyEl = modal.querySelector(".ac-media-modal-body");
185 const iframeWrap = modal.querySelector(".ac-media-modal-iframe-wrap");
186 const iframeEl = modal.querySelector(".ac-media-modal-iframe");
187 const actionsEl = modal.querySelector(".ac-media-modal-actions");
188
189 titleEl.textContent = opts.title || "Media";
190 subtitleEl.textContent = opts.subtitle || "";
191 bodyEl.innerHTML = opts.bodyHtml || "";
192
193 const iframeUrl = typeof opts.iframeUrl === "string" ? opts.iframeUrl : "";
194 if (iframeUrl) {
195 iframeEl.src = iframeUrl;
196 iframeWrap.hidden = false;
197 } else {
198 iframeEl.src = "";
199 iframeWrap.hidden = true;
200 }
201
202 const actions = Array.isArray(opts.actions) ? opts.actions : [];
203 actionsEl.innerHTML = actions
204 .filter((action) => action && typeof action.url === "string" && action.url)
205 .map((action) => {
206 const label = String(action.label || "Open");
207 const href = String(action.url);
208 return `<a class="ac-media-modal-action" href="${href}" target="_blank" rel="noopener noreferrer">${label}</a>`;
209 })
210 .join("");
211
212 modal.classList.add("active");
213 document.body.style.overflow = "hidden";
214 document.removeEventListener("keydown", modal.__acOnEscape);
215 document.addEventListener("keydown", modal.__acOnEscape);
216 }
217
218 function close() {
219 const modal = document.getElementById("ac-media-modal");
220 if (!modal || !modal.__acClose) return;
221 modal.__acClose();
222 }
223
224 global.ACMediaModal = { open, close };
225})(window);