fb-slides 0.9.1 → 0.10.0

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.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,14 @@ this — which makes it worth being able to see what arrived.
7
7
  Entries are written as the work lands, under **Unreleased**; the release commit
8
8
  that stamps the version renames that heading to the version and its date.
9
9
 
10
+ ## 0.10.0 — 2026-09-08
11
+
12
+ - **A file in the assets panel opens on click.** The name on a row carries the
13
+ full path in its tooltip, and clicking it shows the file: an image comes up
14
+ right there, over the deck — full size, the path under it, a click anywhere
15
+ or Escape puts it away — and everything else opens in a new tab, where the
16
+ browser plays the clip, renders the PDF, and offers to save the rest.
17
+
10
18
  ## 0.9.1 — 2026-09-08
11
19
 
12
20
  - **The assets panel shows the folder, not just what you dropped.** The list
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fb-slides",
3
- "version": "0.9.1",
3
+ "version": "0.10.0",
4
4
  "type": "module",
5
5
  "description": "Markdown-driven reveal.js decks: live demo embeds, annotation, mermaid, and a zero-config dev server",
6
6
  "keywords": [
package/runtime/assets.js CHANGED
@@ -167,6 +167,32 @@ export const createAssets = ({ paste, active }) => {
167
167
  const wordInput = confirmBox.querySelector('.deck-assets-word');
168
168
  const confirmButton = confirmBox.querySelector('.deck-assets-confirm-delete');
169
169
 
170
+ // ---- the image preview --------------------------------------------------
171
+ // A click on an image's name opens the image itself, over everything — the
172
+ // drawer is narrow and a thumbnail would say less than the file name does.
173
+ // A click anywhere on it, or Escape, puts it away. Every other kind of file
174
+ // goes to a new tab instead: the browser knows a clip and a PDF better.
175
+
176
+ const preview = document.createElement('div');
177
+ preview.className = 'deck-assets-preview';
178
+ preview.hidden = true;
179
+ preview.innerHTML = `<figure><img alt="" /><figcaption><code></code></figcaption></figure>`;
180
+ const previewImage = preview.querySelector('img');
181
+
182
+ const openPreview = (path) => {
183
+ previewImage.src = path;
184
+ preview.querySelector('figcaption code').textContent = path;
185
+ preview.hidden = false;
186
+ };
187
+
188
+ const closePreview = () => {
189
+ preview.hidden = true;
190
+ // A 20 MB screenshot should stop arriving the moment nobody is looking.
191
+ previewImage.removeAttribute('src');
192
+ };
193
+
194
+ preview.addEventListener('click', closePreview);
195
+
170
196
  // ---- the full-page sheet ----------------------------------------------
171
197
 
172
198
  const overlay = document.createElement('div');
@@ -266,6 +292,7 @@ export const createAssets = ({ paste, active }) => {
266
292
  nameRow.hidden = true;
267
293
  waiting.hidden = true;
268
294
  closeConfirm();
295
+ closePreview();
269
296
  };
270
297
 
271
298
  // ---- the rows ---------------------------------------------------------
@@ -309,10 +336,20 @@ export const createAssets = ({ paste, active }) => {
309
336
  `<button type="button" class="deck-assets-insert" title="Write it into the slide">${INSERT_ICON}</button>` +
310
337
  `<button type="button" class="deck-assets-trash" title="Delete the file">${TRASH_ICON}</button>` +
311
338
  `</span>`;
312
- row.querySelector('code').textContent = file.path;
339
+ const name = row.querySelector('code');
340
+ name.textContent = file.path;
313
341
  // The drawer is narrow and a path can be long: the row truncates, and
314
- // hovering it says the whole thing.
315
- row.title = file.path;
342
+ // hovering the name says the whole thing. Clicking it shows the file: an
343
+ // image right here, over the deck; anything else in a new tab, where the
344
+ // browser plays the clip, renders the PDF, and offers to save the rest.
345
+ name.title = file.path;
346
+ name.classList.add('is-viewable');
347
+ if (IMAGE.test(file.path)) {
348
+ name.classList.add('is-image');
349
+ name.addEventListener('click', () => openPreview(file.path));
350
+ } else {
351
+ name.addEventListener('click', () => window.open(file.path, '_blank', 'noopener'));
352
+ }
316
353
  row.querySelector('.deck-assets-size').textContent = bytes(file.size);
317
354
  row.querySelector('.deck-assets-copy').addEventListener('click', (event) =>
318
355
  copy(file.path, event.currentTarget),
@@ -535,7 +572,7 @@ export const createAssets = ({ paste, active }) => {
535
572
  drop([...event.dataTransfer.files], target?.dataset.dir ?? chosen);
536
573
  });
537
574
 
538
- document.body.append(overlay);
575
+ document.body.append(overlay, preview);
539
576
 
540
577
  return {
541
578
  element,
@@ -546,6 +583,7 @@ export const createAssets = ({ paste, active }) => {
546
583
  // One layer per press, innermost first — the drawer asks this before it
547
584
  // decides whether Escape was meant for it.
548
585
  escape: () => {
586
+ if (!preview.hidden) return closePreview(), true;
549
587
  if (!confirmBox.hidden) return closeConfirm(), true;
550
588
  if (!element.hidden) return close(), true;
551
589
  return false;
@@ -1683,6 +1683,12 @@ body.edit-open #deck-edit { transform: none; }
1683
1683
  text-overflow: ellipsis;
1684
1684
  white-space: nowrap;
1685
1685
  }
1686
+ /* A file's name shows the file — an image over the deck, the rest in a new
1687
+ tab; the cursor and the underline are the whole affordance, the row stays a
1688
+ row. */
1689
+ .deck-assets-list code.is-viewable { cursor: pointer; }
1690
+ .deck-assets-list code.is-image { cursor: zoom-in; }
1691
+ .deck-assets-list code.is-viewable:hover { color: var(--accent); text-decoration: underline; }
1686
1692
  .deck-assets-file {
1687
1693
  min-width: 0;
1688
1694
  font: 400 12px/1.5 var(--sans);
@@ -1869,6 +1875,40 @@ body.edit-open #deck-edit { transform: none; }
1869
1875
  transform: scale(1.04);
1870
1876
  }
1871
1877
 
1878
+ /* The image preview — over everything the way the drop sheet is: the image is
1879
+ the whole point while it is up, and a click anywhere puts it away. */
1880
+ .deck-assets-preview {
1881
+ position: fixed;
1882
+ inset: 0;
1883
+ z-index: 72;
1884
+ display: grid;
1885
+ place-items: center;
1886
+ background: color-mix(in srgb, var(--bg) 74%, transparent);
1887
+ backdrop-filter: blur(8px);
1888
+ cursor: zoom-out;
1889
+ }
1890
+ .deck-assets-preview[hidden] { display: none; }
1891
+ .deck-assets-preview figure {
1892
+ margin: 0;
1893
+ display: flex;
1894
+ flex-direction: column;
1895
+ align-items: center;
1896
+ gap: 12px;
1897
+ }
1898
+ .deck-assets-preview img {
1899
+ max-width: min(1100px, 84vw);
1900
+ max-height: 78vh;
1901
+ border: 1px solid var(--line);
1902
+ border-radius: 10px;
1903
+ /* A card behind it, so a transparent PNG reads as an image and not a hole. */
1904
+ background: var(--card);
1905
+ box-shadow: 0 18px 60px color-mix(in srgb, #000 45%, transparent);
1906
+ }
1907
+ .deck-assets-preview figcaption code {
1908
+ font: 500 12.5px/1.4 var(--mono);
1909
+ color: var(--accent-2);
1910
+ }
1911
+
1872
1912
  /* The open drawer's button wears the accent — it draws in nothing, like the
1873
1913
  pointer's; the id outweighs the generic armed-tool rule. */
1874
1914
  #tool-edit[aria-pressed='true'] {