pindeck 3.0.0-beta.3 → 3.2.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,47 @@
1
+ /* One event stream for all of this browser's PinDeck tabs on one port, behind the front.
2
+
3
+ Browsers open at most 6 connections to one host and port, and each editor tab held one open for its deck's
4
+ events, so a sixth tab never loaded and the others stalled. The tabs now share this worker's stream: each
5
+ tab (a port here) names its deck, and the front (front.mjs, /events) passes that deck's events on, marked
6
+ with the tab. Each tab still gets its deck's events as if it had its own stream, starting with "hello". */
7
+
8
+ const tabs = new Map(); // tab id -> { port, deck }
9
+ const stream = crypto.randomUUID?.() || String(Math.random()).slice(2) + String(Date.now());
10
+ let source = null, seq = 0;
11
+
12
+ const tell = (body) => fetch("/events/subscribe", { method: "POST", headers: { "content-type": "application/json" },
13
+ body: JSON.stringify({ stream, ...body }) }).catch(() => {});
14
+
15
+ function open() {
16
+ source = new EventSource(`/events?stream=${encodeURIComponent(stream)}`);
17
+ // "ready" comes first on every (re)connect: the front has a new stream, with no decks yet
18
+ source.addEventListener("ready", () => { for (const [tab, t] of tabs) tell({ tab, deck: t.deck, on: true }); });
19
+ source.addEventListener("deck", (ev) => {
20
+ const m = JSON.parse(ev.data);
21
+ tabs.get(m.tab)?.port.postMessage({ type: m.type, data: m.data });
22
+ });
23
+ source.onerror = () => { for (const t of tabs.values()) t.port.postMessage({ type: "error" }); };
24
+ }
25
+
26
+ onconnect = (e) => {
27
+ const port = e.ports[0];
28
+ let tab = null;
29
+ const leave = () => {
30
+ if (!tab) return;
31
+ tabs.delete(tab);
32
+ tell({ tab, on: false });
33
+ tab = null;
34
+ if (!tabs.size) { source?.close(); source = null; }
35
+ };
36
+ port.onmessage = ({ data }) => {
37
+ if (data?.subscribe) {
38
+ leave();
39
+ tab = String(++seq);
40
+ tabs.set(tab, { port, deck: String(data.subscribe) });
41
+ if (!source) open();
42
+ else if (source.readyState === EventSource.OPEN) tell({ tab, deck: String(data.subscribe), on: true });
43
+ }
44
+ if (data?.close) leave();
45
+ };
46
+ port.start();
47
+ };
@@ -0,0 +1,14 @@
1
+ /* The front's own pages: the list of decks (a folder without index.html) and why a deck cannot be opened */
2
+ body { margin: 0; background: #f6f6f4; color: #1d1f22; font: 15px/1.5 system-ui, -apple-system, "Segoe UI", sans-serif; }
3
+ .front { max-width: 44em; margin: 3em auto; padding: 0 16px; }
4
+ .front h1 { font-size: 22px; margin: 0 0 .8em; }
5
+ .front h2 { font-size: 12px; font-weight: 600; color: #6b6f76; margin: 1.6em 0 .4em; overflow-wrap: anywhere; }
6
+ .front ul { list-style: none; margin: 0; padding: 0; border: 1px solid #e3e3df; border-radius: 10px; background: #fff; }
7
+ .front li { padding: 9px 14px; border-top: 1px solid #ececea; }
8
+ .front li:first-child { border-top: 0; }
9
+ .front li a { color: #1d1f22; font-weight: 500; text-decoration: none; }
10
+ .front li a:hover { color: #e8710a; }
11
+ .front li small { color: #6b6f76; margin-left: 6px; }
12
+ .front li.off { color: #8a8e94; }
13
+ .front li.off small { display: block; margin: 2px 0 0; font-size: 12px; }
14
+ .front pre { white-space: pre-wrap; background: #fff; border: 1px solid #e3e3df; border-radius: 8px; padding: 10px 12px; font-size: 13px; }
package/public/index.html CHANGED
@@ -35,7 +35,10 @@
35
35
  <button id="prev" class="icon" title="Previous slide (←)" aria-label="Previous slide">‹</button>
36
36
  <span id="slide-pos" class="pos">– / –</span>
37
37
  <button id="next" class="icon" title="Next slide (→)" aria-label="Next slide">›</button>
38
- <span id="deck-name" class="deck-name"></span>
38
+ <span class="menu-wrap deck-wrap">
39
+ <button id="deck-btn" class="deck-btn" aria-haspopup="menu" aria-expanded="false" disabled><span id="deck-name"></span><span class="caret" hidden>▾</span></button>
40
+ <div id="deck-menu" class="menu left deck-menu" role="menu" hidden></div>
41
+ </span>
39
42
  <span id="server-note" class="server-note" hidden></span>
40
43
  </div>
41
44
  <div class="group">
@@ -99,7 +102,7 @@
99
102
  double-click to change the text. A selected element is what the assistant takes “this” to mean.</p>
100
103
  <p>Add figures, audio, a PDF or a screenshot with <strong>+</strong>, or drag and drop or paste a file; the assistant can put it on a slide
101
104
  or, for a paper or report, take the details from it.</p>
102
- <p>The assistant edits <code id="deck-file">index.html</code> and <code>theme.css</code>, checks the layout, and the slide updates here.
105
+ <p>The assistant edits <code id="deck-file">index.html</code>, <code>theme.css</code> and the deck's other files, checks the layout, and the slide updates here.
103
106
  <code>brand.css</code> is locked.</p>
104
107
  </div>
105
108
  </div>
@@ -172,6 +175,11 @@
172
175
  </form>
173
176
  </dialog>
174
177
 
178
+ <dialog id="shot-dialog" class="diff-dialog shot-dialog">
179
+ <div class="dialog-head"><span id="shot-title">Screenshot</span><button class="small" id="shot-close">Close</button></div>
180
+ <img id="shot-img" alt="Screenshot">
181
+ </dialog>
182
+
175
183
  <dialog id="diff-dialog" class="diff-dialog">
176
184
  <div class="dialog-head"><span id="diff-title">Changes</span><button class="small" id="diff-close">Close</button></div>
177
185
  <pre id="diff-body" class="diff"></pre>