hono-decks 0.4.4 → 0.5.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.
@@ -200,7 +200,7 @@ interface DeckManifest {
200
200
  }
201
201
  //#endregion
202
202
  //#region src/renderer/control-icons.d.ts
203
- type DeckControlIconName = "deck-list" | "home" | "viewer" | "projection" | "presenter" | "previous" | "next" | "fullscreen" | "print" | "details" | "export-pdf" | "export-png";
203
+ type DeckControlIconName = "deck-list" | "home" | "viewer" | "projection" | "presenter" | "previous" | "next" | "fullscreen" | "external-link" | "print" | "details" | "export-pdf" | "export-png";
204
204
  declare function renderControlIcon(name: DeckControlIconName, className?: string): DeckRenderable;
205
205
  declare function renderControlIconHtml(name: DeckControlIconName, className?: string): string;
206
206
  declare function controlIconLabel(name: DeckControlIconName): string;
package/dist/advanced.js CHANGED
@@ -545,6 +545,7 @@ function controlIconLabel(name) {
545
545
  case "previous": return "Previous slide";
546
546
  case "next": return "Next slide";
547
547
  case "fullscreen": return "Toggle fullscreen";
548
+ case "external-link": return "Open link in new tab";
548
549
  case "print": return "Print view";
549
550
  case "details": return "Details";
550
551
  case "export-pdf": return "Export PDF";
@@ -561,6 +562,7 @@ function controlIconPathHtml(name) {
561
562
  case "previous": return "<path d=\"M15 6l-6 6 6 6\" />";
562
563
  case "next": return "<path d=\"M9 6l6 6-6 6\" />";
563
564
  case "fullscreen": return "<path d=\"M8 3H5a2 2 0 0 0-2 2v3\" /><path d=\"M16 3h3a2 2 0 0 1 2 2v3\" /><path d=\"M8 21H5a2 2 0 0 1-2-2v-3\" /><path d=\"M16 21h3a2 2 0 0 0 2-2v-3\" />";
565
+ case "external-link": return "<path d=\"M15 3h6v6\" /><path d=\"M10 14 21 3\" /><path d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\" />";
564
566
  case "print": return "<path d=\"M6 9V3h12v6\" /><path d=\"M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2\" /><path d=\"M6 14h12v7H6z\" />";
565
567
  case "details": return "<circle cx=\"12\" cy=\"12\" r=\"9\" /><path d=\"M12 11v5\" /><path d=\"M12 8h.01\" />";
566
568
  case "export-pdf": return "<path d=\"M14 3v4a2 2 0 0 0 2 2h4\" /><path d=\"M5 3h9l6 6v12H5z\" /><path d=\"M8 15h8\" /><path d=\"M8 18h5\" />";
@@ -602,6 +604,11 @@ function controlIconPaths(name) {
602
604
  jsx("path", { d: "M8 21H5a2 2 0 0 1-2-2v-3" }),
603
605
  jsx("path", { d: "M16 21h3a2 2 0 0 0 2-2v-3" })
604
606
  ];
607
+ case "external-link": return [
608
+ jsx("path", { d: "M15 3h6v6" }),
609
+ jsx("path", { d: "M10 14 21 3" }),
610
+ jsx("path", { d: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" })
611
+ ];
605
612
  case "print": return [
606
613
  jsx("path", { d: "M6 9V3h12v6" }),
607
614
  jsx("path", { d: "M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2" }),
@@ -700,6 +707,7 @@ function renderPresentationScript(nonce) {
700
707
  let isTransitioning = false;
701
708
  let pendingNavigation = null;
702
709
  let transitionToken = 0;
710
+ let touchStart = null;
703
711
 
704
712
  function fitDeck() {
705
713
  if (!(stage instanceof HTMLElement) || !(deck instanceof HTMLElement)) return;
@@ -1032,6 +1040,33 @@ function renderPresentationScript(nonce) {
1032
1040
  if (action === "overview") toggleOverview();
1033
1041
  }
1034
1042
 
1043
+ function isInteractiveTouchTarget(target) {
1044
+ return target instanceof Element && Boolean(target.closest("a,button,input,select,textarea,[contenteditable],[role='button']"));
1045
+ }
1046
+
1047
+ function handleTouchStart(event) {
1048
+ if (event.touches.length !== 1 || isInteractiveTouchTarget(event.target)) {
1049
+ touchStart = null;
1050
+ return;
1051
+ }
1052
+ const touch = event.touches[0];
1053
+ touchStart = { x: touch.clientX, y: touch.clientY };
1054
+ }
1055
+
1056
+ function handleTouchEnd(event) {
1057
+ if (!touchStart || event.changedTouches.length !== 1) {
1058
+ touchStart = null;
1059
+ return;
1060
+ }
1061
+ const touch = event.changedTouches[0];
1062
+ const deltaX = touch.clientX - touchStart.x;
1063
+ const deltaY = touch.clientY - touchStart.y;
1064
+ touchStart = null;
1065
+ const threshold = Math.max(48, window.innerWidth * 0.08);
1066
+ if (Math.abs(deltaX) < threshold || Math.abs(deltaX) <= Math.abs(deltaY) * 1.25) return;
1067
+ handleCommand(deltaX < 0 ? "next" : "previous");
1068
+ }
1069
+
1035
1070
  async function toggleFullscreen() {
1036
1071
  if (document.fullscreenElement) {
1037
1072
  await document.exitFullscreen?.();
@@ -1055,6 +1090,9 @@ function renderPresentationScript(nonce) {
1055
1090
  if (event.key === "p") handleCommand("presenter");
1056
1091
  if (event.key === "o") handleCommand("overview");
1057
1092
  });
1093
+ document.addEventListener("touchstart", handleTouchStart, { passive: true });
1094
+ document.addEventListener("touchend", handleTouchEnd, { passive: true });
1095
+ document.addEventListener("touchcancel", () => { touchStart = null; }, { passive: true });
1058
1096
 
1059
1097
  window.addEventListener("message", (event) => {
1060
1098
  const message = event.data;
@@ -2462,10 +2500,25 @@ async function renderDeckExternalEmbedResponse(input) {
2462
2500
  mountPath,
2463
2501
  title: deck.meta.title ?? slug
2464
2502
  }, input.document, input.options.document);
2503
+ const viewerOptions = typeof input.options.viewer === "function" ? await input.options.viewer(input.context) : input.options.viewer;
2504
+ const controls = viewerOptions?.controls === void 0 ? { items: (_defaults, context) => [{
2505
+ type: "link",
2506
+ key: "open-viewer",
2507
+ href: context.meta.paths.viewer,
2508
+ label: "Open full viewer in new tab",
2509
+ icon: "external-link",
2510
+ attributes: {
2511
+ "aria-label": "Open full viewer in new tab",
2512
+ "data-hono-decks-viewer-link": true,
2513
+ target: "_blank",
2514
+ rel: "noreferrer"
2515
+ }
2516
+ }] } : viewerOptions.controls;
2465
2517
  const viewer = await createDeckViewerEmbed({
2466
2518
  deck,
2467
2519
  mountPath,
2468
- ...typeof input.options.viewer === "function" ? await input.options.viewer(input.context) : input.options.viewer,
2520
+ ...viewerOptions,
2521
+ controls,
2469
2522
  availablePages: input.availablePages,
2470
2523
  nonce: document.nonce
2471
2524
  });
package/dist/mod.d.ts CHANGED
@@ -153,7 +153,7 @@ interface DeckManifest {
153
153
  }
154
154
  //#endregion
155
155
  //#region src/renderer/control-icons.d.ts
156
- type DeckControlIconName = "deck-list" | "home" | "viewer" | "projection" | "presenter" | "previous" | "next" | "fullscreen" | "print" | "details" | "export-pdf" | "export-png";
156
+ type DeckControlIconName = "deck-list" | "home" | "viewer" | "projection" | "presenter" | "previous" | "next" | "fullscreen" | "external-link" | "print" | "details" | "export-pdf" | "export-png";
157
157
  //#endregion
158
158
  //#region src/server/paths.d.ts
159
159
  /** All public URLs for one deck. */
package/dist/mod.js CHANGED
@@ -333,6 +333,7 @@ function controlIconLabel(name) {
333
333
  case "previous": return "Previous slide";
334
334
  case "next": return "Next slide";
335
335
  case "fullscreen": return "Toggle fullscreen";
336
+ case "external-link": return "Open link in new tab";
336
337
  case "print": return "Print view";
337
338
  case "details": return "Details";
338
339
  case "export-pdf": return "Export PDF";
@@ -349,6 +350,7 @@ function controlIconPathHtml(name) {
349
350
  case "previous": return "<path d=\"M15 6l-6 6 6 6\" />";
350
351
  case "next": return "<path d=\"M9 6l6 6-6 6\" />";
351
352
  case "fullscreen": return "<path d=\"M8 3H5a2 2 0 0 0-2 2v3\" /><path d=\"M16 3h3a2 2 0 0 1 2 2v3\" /><path d=\"M8 21H5a2 2 0 0 1-2-2v-3\" /><path d=\"M16 21h3a2 2 0 0 0 2-2v-3\" />";
353
+ case "external-link": return "<path d=\"M15 3h6v6\" /><path d=\"M10 14 21 3\" /><path d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\" />";
352
354
  case "print": return "<path d=\"M6 9V3h12v6\" /><path d=\"M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2\" /><path d=\"M6 14h12v7H6z\" />";
353
355
  case "details": return "<circle cx=\"12\" cy=\"12\" r=\"9\" /><path d=\"M12 11v5\" /><path d=\"M12 8h.01\" />";
354
356
  case "export-pdf": return "<path d=\"M14 3v4a2 2 0 0 0 2 2h4\" /><path d=\"M5 3h9l6 6v12H5z\" /><path d=\"M8 15h8\" /><path d=\"M8 18h5\" />";
@@ -390,6 +392,11 @@ function controlIconPaths(name) {
390
392
  jsx("path", { d: "M8 21H5a2 2 0 0 1-2-2v-3" }),
391
393
  jsx("path", { d: "M16 21h3a2 2 0 0 0 2-2v-3" })
392
394
  ];
395
+ case "external-link": return [
396
+ jsx("path", { d: "M15 3h6v6" }),
397
+ jsx("path", { d: "M10 14 21 3" }),
398
+ jsx("path", { d: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" })
399
+ ];
393
400
  case "print": return [
394
401
  jsx("path", { d: "M6 9V3h12v6" }),
395
402
  jsx("path", { d: "M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2" }),
package/dist/node.d.ts CHANGED
@@ -331,7 +331,7 @@ interface LocalDevSlidesApp {
331
331
  declare function createLocalDevSlidesApp(input: CreateLocalDevSlidesAppInput): Promise<LocalDevSlidesApp>;
332
332
  //#endregion
333
333
  //#region src/renderer/control-icons.d.ts
334
- type DeckControlIconName = "deck-list" | "home" | "viewer" | "projection" | "presenter" | "previous" | "next" | "fullscreen" | "print" | "details" | "export-pdf" | "export-png";
334
+ type DeckControlIconName = "deck-list" | "home" | "viewer" | "projection" | "presenter" | "previous" | "next" | "fullscreen" | "external-link" | "print" | "details" | "export-pdf" | "export-png";
335
335
  //#endregion
336
336
  //#region src/server/paths.d.ts
337
337
  /** All public URLs for one deck. */
package/dist/node.js CHANGED
@@ -2968,6 +2968,7 @@ function controlIconLabel(name) {
2968
2968
  case "previous": return "Previous slide";
2969
2969
  case "next": return "Next slide";
2970
2970
  case "fullscreen": return "Toggle fullscreen";
2971
+ case "external-link": return "Open link in new tab";
2971
2972
  case "print": return "Print view";
2972
2973
  case "details": return "Details";
2973
2974
  case "export-pdf": return "Export PDF";
@@ -2984,6 +2985,7 @@ function controlIconPathHtml(name) {
2984
2985
  case "previous": return "<path d=\"M15 6l-6 6 6 6\" />";
2985
2986
  case "next": return "<path d=\"M9 6l6 6-6 6\" />";
2986
2987
  case "fullscreen": return "<path d=\"M8 3H5a2 2 0 0 0-2 2v3\" /><path d=\"M16 3h3a2 2 0 0 1 2 2v3\" /><path d=\"M8 21H5a2 2 0 0 1-2-2v-3\" /><path d=\"M16 21h3a2 2 0 0 0 2-2v-3\" />";
2988
+ case "external-link": return "<path d=\"M15 3h6v6\" /><path d=\"M10 14 21 3\" /><path d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\" />";
2987
2989
  case "print": return "<path d=\"M6 9V3h12v6\" /><path d=\"M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2\" /><path d=\"M6 14h12v7H6z\" />";
2988
2990
  case "details": return "<circle cx=\"12\" cy=\"12\" r=\"9\" /><path d=\"M12 11v5\" /><path d=\"M12 8h.01\" />";
2989
2991
  case "export-pdf": return "<path d=\"M14 3v4a2 2 0 0 0 2 2h4\" /><path d=\"M5 3h9l6 6v12H5z\" /><path d=\"M8 15h8\" /><path d=\"M8 18h5\" />";
@@ -3025,6 +3027,11 @@ function controlIconPaths(name) {
3025
3027
  jsx("path", { d: "M8 21H5a2 2 0 0 1-2-2v-3" }),
3026
3028
  jsx("path", { d: "M16 21h3a2 2 0 0 0 2-2v-3" })
3027
3029
  ];
3030
+ case "external-link": return [
3031
+ jsx("path", { d: "M15 3h6v6" }),
3032
+ jsx("path", { d: "M10 14 21 3" }),
3033
+ jsx("path", { d: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" })
3034
+ ];
3028
3035
  case "print": return [
3029
3036
  jsx("path", { d: "M6 9V3h12v6" }),
3030
3037
  jsx("path", { d: "M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2" }),
@@ -3123,6 +3130,7 @@ function renderPresentationScript(nonce) {
3123
3130
  let isTransitioning = false;
3124
3131
  let pendingNavigation = null;
3125
3132
  let transitionToken = 0;
3133
+ let touchStart = null;
3126
3134
 
3127
3135
  function fitDeck() {
3128
3136
  if (!(stage instanceof HTMLElement) || !(deck instanceof HTMLElement)) return;
@@ -3455,6 +3463,33 @@ function renderPresentationScript(nonce) {
3455
3463
  if (action === "overview") toggleOverview();
3456
3464
  }
3457
3465
 
3466
+ function isInteractiveTouchTarget(target) {
3467
+ return target instanceof Element && Boolean(target.closest("a,button,input,select,textarea,[contenteditable],[role='button']"));
3468
+ }
3469
+
3470
+ function handleTouchStart(event) {
3471
+ if (event.touches.length !== 1 || isInteractiveTouchTarget(event.target)) {
3472
+ touchStart = null;
3473
+ return;
3474
+ }
3475
+ const touch = event.touches[0];
3476
+ touchStart = { x: touch.clientX, y: touch.clientY };
3477
+ }
3478
+
3479
+ function handleTouchEnd(event) {
3480
+ if (!touchStart || event.changedTouches.length !== 1) {
3481
+ touchStart = null;
3482
+ return;
3483
+ }
3484
+ const touch = event.changedTouches[0];
3485
+ const deltaX = touch.clientX - touchStart.x;
3486
+ const deltaY = touch.clientY - touchStart.y;
3487
+ touchStart = null;
3488
+ const threshold = Math.max(48, window.innerWidth * 0.08);
3489
+ if (Math.abs(deltaX) < threshold || Math.abs(deltaX) <= Math.abs(deltaY) * 1.25) return;
3490
+ handleCommand(deltaX < 0 ? "next" : "previous");
3491
+ }
3492
+
3458
3493
  async function toggleFullscreen() {
3459
3494
  if (document.fullscreenElement) {
3460
3495
  await document.exitFullscreen?.();
@@ -3478,6 +3513,9 @@ function renderPresentationScript(nonce) {
3478
3513
  if (event.key === "p") handleCommand("presenter");
3479
3514
  if (event.key === "o") handleCommand("overview");
3480
3515
  });
3516
+ document.addEventListener("touchstart", handleTouchStart, { passive: true });
3517
+ document.addEventListener("touchend", handleTouchEnd, { passive: true });
3518
+ document.addEventListener("touchcancel", () => { touchStart = null; }, { passive: true });
3481
3519
 
3482
3520
  window.addEventListener("message", (event) => {
3483
3521
  const message = event.data;
@@ -4822,10 +4860,25 @@ async function renderDeckExternalEmbedResponse(input) {
4822
4860
  mountPath,
4823
4861
  title: deck.meta.title ?? slug
4824
4862
  }, input.document, input.options.document);
4863
+ const viewerOptions = typeof input.options.viewer === "function" ? await input.options.viewer(input.context) : input.options.viewer;
4864
+ const controls = viewerOptions?.controls === void 0 ? { items: (_defaults, context) => [{
4865
+ type: "link",
4866
+ key: "open-viewer",
4867
+ href: context.meta.paths.viewer,
4868
+ label: "Open full viewer in new tab",
4869
+ icon: "external-link",
4870
+ attributes: {
4871
+ "aria-label": "Open full viewer in new tab",
4872
+ "data-hono-decks-viewer-link": true,
4873
+ target: "_blank",
4874
+ rel: "noreferrer"
4875
+ }
4876
+ }] } : viewerOptions.controls;
4825
4877
  const viewer = await createDeckViewerEmbed({
4826
4878
  deck,
4827
4879
  mountPath,
4828
- ...typeof input.options.viewer === "function" ? await input.options.viewer(input.context) : input.options.viewer,
4880
+ ...viewerOptions,
4881
+ controls,
4829
4882
  availablePages: input.availablePages,
4830
4883
  nonce: document.nonce
4831
4884
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hono-decks",
3
- "version": "0.4.4",
3
+ "version": "0.5.0",
4
4
  "description": "Slide decks for Hono and Cloudflare Workers",
5
5
  "keywords": [
6
6
  "cloudflare-workers",