PageSourceSearch

https://smartcone.ai/src/shell/types/stickies/exporter.js

js smartcone.ai collected 2026-10-05 15:07:52 UTC 5,883 bytes, 172 lines download raw bytes

1// /src/containers/types/stickies/exporter.js
2// Creates a framework-agnostic snapshot of a stickies container: { html, css, js }
3// Also provides Preview + Full-screen helpers.
4// NOTE: This file is complete (your uploaded version was truncated). [1](https://thesmartcone-my.sharepoint.com/personal/jason_thesmartcone_com/Documents/Microsoft%20Copilot%20Chat%20Files/login.html)
5
6export function exportBundle(containerEl) {
7  const body = containerEl.querySelector(".shell-tb");
8  const stickies = Array.from(body.querySelectorAll(":scope > .comp.sticky"));
9
10  const htmlChildren = stickies.map((n) => {
11    const w = n.offsetWidth || parseFloat(n.style.width) || 220;
12    const h = n.offsetHeight || parseFloat(n.style.height) || 180;
13    const l = n.offsetLeft ?? parseFloat(n.style.left) ?? 0;
14    const t = n.offsetTop ?? parseFloat(n.style.top) ?? 0;
15
16    const bg = getComputedStyle(n).backgroundColor || "#fef08a";
17    const title = n.querySelector(".title-st")?.textContent || "";
18    const text = n.querySelector(".body-st")?.textContent || "";
19    const done = n.dataset.done === "true";
20    const assignee = n.querySelector(".assignee-st")?.value || "";
21    const due = n.querySelector(".date-st")?.value || "";
22
23    return `
24<div class="note${done ? " done" : ""}" style="left:${Math.round(l)}px;top:${Math.round(t)}px;width:${Math.round(w)}px;min-height:${Math.round(h)}px;background:${escapeAttr(bg)}">
25  <div class="note-title">${escapeHTML(title)}</div>
26  <div class="note-body">${escapeHTML(text)}</div>
27  <div class="note-meta">
28    ${assignee ? `<div class="note-assignee">👤 ${escapeHTML(assignee)}</div>` : ""}
29    ${due ? `<div class="note-due">📅 ${escapeHTML(due)}</div>` : ""}
30    ${done ? `<div class="note-badge">DONE</div>` : ""}
31  </div>
32</div>`.trim();
33  }).join("\n");
34
35  const html = `
36<div class="notes-board">
37${htmlChildren}
38</div>`.trim();
39
40  const css = `
41:root { color-scheme: light; }
42body { margin:0; font-family:system-ui, -apple-system, Segoe UI, Roboto, sans-serif; }
43.notes-board{
44  position:relative;
45  width:100%;
46  min-height:100vh;
47  background:#fafafa;
48  overflow:auto;
49}
50.note{
51  position:absolute;
52  box-sizing:border-box;
53  border:1px solid rgba(0,0,0,.06);
54  border-radius:10px;
55  padding:10px;
56  color:#0f172a;
57  box-shadow:0 10px 25px rgba(0,0,0,.15), inset 0 1px 0 rgba(255,255,255,.4);
58  transform: rotate(-0.35deg);
59}
60.note.done{ opacity:.9; }
61.note-title{
62  font:700 14px/1.2 system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
63  margin-bottom:6px;
64}
65.note-body{
66  font:13px/1.4 system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
67  white-space:pre-wrap;
68  word-break:break-word;
69}
70.note-meta{
71  margin-top:8px;
72  display:flex;
73  gap:10px;
74  align-items:center;
75  color:#334155;
76  font:600 11px system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
77}
78.note-badge{
79  margin-left:auto;
80  font:700 10px system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
81  color:#10b981;
82}
83`.trim();
84
85  const js = ``;
86
87  return { html, css, js };
88}
89
90export function openPreviewModal(bundle, title = "Stickies Preview") {
91  const { html, css, js } = bundle ?? { html: "", css: "", js: "" };
92
93  const back = document.createElement("div");
94  back.style.cssText =
95    "position:fixed;inset:0;background:rgba(17,24,39,.68);z-index:9999;display:flex;align-items:center;justify-content:center;";
96
97  const card = document.createElement("div");
98  card.style.cssText =
99    "width:min(1200px,92vw);height:min(800px,86vh);background:#0b1220;border:1px solid #1f2937;border-radius:12px;overflow:hidden;box-shadow:0 25px 60px rgba(0,0,0,.35);display:grid;grid-template-rows:44px 1fr;";
100
101  const bar = document.createElement("div");
102  bar.style.cssText =
103    "display:flex;align-items:center;justify-content:space-between;padding:8px 10px;background:#0f172a;color:#e5e7eb;border-bottom:1px solid #1e293b;font:600 12px system-ui;";
104  bar.textContent = title;
105
106  const actions = document.createElement("div");
107  const btnOpen = Object.assign(document.createElement("button"), { textContent: "Open in Tab" });
108  const btnClose = Object.assign(document.createElement("button"), { textContent: "Close" });
109  [btnOpen, btnClose].forEach((b) => {
110    b.style.cssText =
111      "background:#111827;color:#e5e7eb;border:1px solid #374151;border-radius:7px;padding:6px 10px;cursor:pointer;font:600 12px system-ui;margin-left:6px;";
112  });
113  actions.append(btnOpen, btnClose);
114  bar.append(actions);
115
116  const iframe = document.createElement("iframe");
117  iframe.style.cssText = "width:100%;height:100%;border:0;background:#fff";
118  iframe.setAttribute("sandbox", "allow-scripts allow-modals allow-popups");
119
120  card.append(bar, iframe);
121  back.appendChild(card);
122  document.body.appendChild(back);
123
124  const close = () => back.remove();
125  btnClose.addEventListener("click", close);
126  back.addEventListener("click", (e) => { if (e.target === back) close(); });
127
128  const doc = `
129<!doctype html>
130<html>
131<head>
132<meta charset="utf-8"/>
133<meta name="viewport" content="width=device-width,initial-scale=1"/>
134<style>${css}</style>
135</head>
136<body>
137${html}
138<script>${js}</script>
139</body>
140</html>`;
141
142  iframe.srcdoc = doc;
143
144  btnOpen.addEventListener("click", () => {
145    const w = window.open("", "_blank");
146    if (!w) return;
147    w.document.open();
148    w.document.write(doc);
149    w.document.close();
150  });
151}
152
153export function openFullscreen(bundle) {
154  // For now, fullscreen uses the same modal but with a different title.
155  // (You can extend this later if you want a true fullscreen overlay.)
156  openPreviewModal(bundle, "Stickies Full Screen");
157}
158
159function escapeHTML(s) {
160  return String(s ?? "").replace(/[&<>"']/g, (c) => ({
161    "&": "&amp;",
162    "<": "&lt;",
163    ">": "&gt;",
164    '"': "&quot;",
165    "'": "&#39;",
166  })[c]);
167}
168
169function escapeAttr(s) {
170  // Keep it simple: remove quotes to avoid breaking style=""
171  return String(s ?? "").replace(/["']/g, "");
172}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.