hyperbook 0.71.5 → 0.72.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.
@@ -179,6 +179,68 @@ var hyperbook = (function () {
179
179
  qrCodeDialog.close();
180
180
  }
181
181
 
182
+ /**
183
+ * Open the share dialog.
184
+ */
185
+ function shareOpen() {
186
+ const shareDialog = document.getElementById("share-dialog");
187
+ shareUpdatePreview();
188
+ shareDialog.showModal();
189
+ }
190
+
191
+ /**
192
+ * Close the share dialog.
193
+ */
194
+ function shareClose() {
195
+ const shareDialog = document.getElementById("share-dialog");
196
+ shareDialog.close();
197
+ }
198
+
199
+ /**
200
+ * Update the URL preview in the share dialog.
201
+ */
202
+ function shareUpdatePreview() {
203
+ const standaloneCheckbox = document.getElementById("share-standalone-checkbox");
204
+ const sectionCheckboxes = document.querySelectorAll("#share-dialog input[data-anchor]");
205
+ const previewEl = document.getElementById("share-url-preview");
206
+
207
+ const baseUrl = window.location.origin + window.location.pathname;
208
+ const params = new URLSearchParams();
209
+
210
+ if (standaloneCheckbox && standaloneCheckbox.checked) {
211
+ params.append("standalone", "true");
212
+ }
213
+
214
+ const selectedSections = Array.from(sectionCheckboxes)
215
+ .filter(cb => cb.checked)
216
+ .map(cb => cb.getAttribute("data-anchor"));
217
+
218
+ if (selectedSections.length > 0) {
219
+ params.append("sections", selectedSections.join(","));
220
+ }
221
+
222
+ const finalUrl = params.toString() ? `${baseUrl}?${params.toString()}` : baseUrl;
223
+ previewEl.textContent = finalUrl;
224
+ }
225
+
226
+ /**
227
+ * Copy the shareable URL to clipboard.
228
+ */
229
+ function shareCopyUrl() {
230
+ const previewEl = document.getElementById("share-url-preview");
231
+ const url = previewEl.textContent;
232
+
233
+ navigator.clipboard.writeText(url).then(() => {
234
+ const button = document.querySelector("#share-dialog .copy-button");
235
+ const originalText = button.textContent;
236
+ button.textContent = window.i18n.get("share-dialog-url-copied");
237
+
238
+ setTimeout(() => {
239
+ button.textContent = originalText;
240
+ }, 2000);
241
+ });
242
+ }
243
+
182
244
  /**
183
245
  * Toggle the navigation drawer.
184
246
  */
@@ -274,6 +336,103 @@ var hyperbook = (function () {
274
336
  initBookmarks(root);
275
337
  }
276
338
 
339
+ /**
340
+ * Hide TOC toggle button when sections are filtered
341
+ */
342
+ function hideTocWhenFiltered() {
343
+ const tocToggle = document.getElementById('toc-toggle');
344
+
345
+ if (tocToggle) {
346
+ tocToggle.style.display = 'none';
347
+ }
348
+ }
349
+
350
+ // Filter sections based on query parameter
351
+ function filterSections() {
352
+ const urlParams = new URLSearchParams(window.location.search);
353
+ const sectionsParam = urlParams.get('sections');
354
+
355
+ if (!sectionsParam) {
356
+ return; // No filtering needed
357
+ }
358
+
359
+ // Parse sections parameter (comma-separated list of IDs)
360
+ const sectionIds = sectionsParam.split(',').map(id => id.trim()).filter(id => id);
361
+
362
+ if (sectionIds.length === 0) {
363
+ return; // No valid IDs provided
364
+ }
365
+
366
+ // Get all headings in the document
367
+ const allHeadings = document.querySelectorAll('h1, h2, h3, h4, h5, h6');
368
+ const headingsArray = Array.from(allHeadings);
369
+
370
+ // Build a map of which elements should be visible
371
+ const visibleElements = new Set();
372
+
373
+ sectionIds.forEach(sectionId => {
374
+ // Find the heading with this ID
375
+ const heading = document.getElementById(sectionId);
376
+ if (!heading) {
377
+ return; // ID not found
378
+ }
379
+
380
+ // Get the heading level (h1 = 1, h2 = 2, etc.)
381
+ const headingLevel = parseInt(heading.tagName.substring(1));
382
+
383
+ // Mark this heading as visible
384
+ visibleElements.add(heading);
385
+
386
+ // Find the index of this heading
387
+ const headingIndex = headingsArray.indexOf(heading);
388
+ if (headingIndex === -1) {
389
+ return;
390
+ }
391
+
392
+ // Collect all elements until the next heading of the same or higher level
393
+ let currentElement = heading.nextElementSibling;
394
+
395
+ while (currentElement) {
396
+ // Check if this is a heading
397
+ const isHeading = /^H[1-6]$/.test(currentElement.tagName);
398
+
399
+ if (isHeading) {
400
+ const currentLevel = parseInt(currentElement.tagName.substring(1));
401
+
402
+ // Stop if we hit a heading of the same or higher level (lower number)
403
+ if (currentLevel <= headingLevel) {
404
+ break;
405
+ }
406
+
407
+ // Include lower-level headings (subsections)
408
+ visibleElements.add(currentElement);
409
+ } else {
410
+ // Include non-heading elements
411
+ visibleElements.add(currentElement);
412
+ }
413
+
414
+ currentElement = currentElement.nextElementSibling;
415
+ }
416
+ });
417
+
418
+ // Hide all elements that are not in visibleElements
419
+ const markdownDiv = document.querySelector('main article .hyperbook-markdown');
420
+ if (markdownDiv) {
421
+ Array.from(markdownDiv.children).forEach(element => {
422
+ // Don't hide UI elements (floating buttons container, side drawers)
423
+ const isUIElement = element.id === 'floating-buttons-container' ||
424
+ element.tagName === 'SIDE-DRAWER';
425
+
426
+ if (!visibleElements.has(element) && !isUIElement) {
427
+ element.style.display = 'none';
428
+ }
429
+ });
430
+
431
+ // Hide TOC toggle when sections are filtered
432
+ hideTocWhenFiltered();
433
+ }
434
+ }
435
+
277
436
  // Check for standalone layout URL parameter or iframe context
278
437
  function checkStandaloneMode() {
279
438
  // Check if explicitly requested via URL parameter
@@ -307,6 +466,7 @@ var hyperbook = (function () {
307
466
  document.addEventListener("DOMContentLoaded", () => {
308
467
  init(document);
309
468
  checkStandaloneMode();
469
+ filterSections();
310
470
  });
311
471
 
312
472
  // Observe for new elements added to the DOM
@@ -332,6 +492,10 @@ var hyperbook = (function () {
332
492
  search,
333
493
  qrcodeOpen,
334
494
  qrcodeClose,
495
+ shareOpen,
496
+ shareClose,
497
+ shareUpdatePreview,
498
+ shareCopyUrl,
335
499
  init,
336
500
  };
337
501
  })();
@@ -469,13 +469,17 @@ figure {
469
469
  text-decoration: underline;
470
470
  }
471
471
 
472
- .hyperbook-markdown #qrcode-dialog {
472
+ #qrcode-dialog {
473
473
  background: var(--color-background);
474
474
  border: 1px solid var(--color-brand);
475
475
  width: 100%;
476
476
  height: 100%;
477
477
  }
478
478
 
479
+ #qrcode-dialog::backdrop {
480
+ background: rgba(0, 0, 0, 0.5);
481
+ }
482
+
479
483
  #qrcode-dialog .container {
480
484
  position: absolute;
481
485
  top: 0;
@@ -524,13 +528,13 @@ figure {
524
528
  background-color: var(--color-brand);
525
529
  }
526
530
 
527
- .hyperbook-markdown #qrcode-dialog .make-qrcode {
531
+ #qrcode-dialog .make-qrcode {
528
532
  width: 100%;
529
533
  max-width: 512px;
530
534
  margin: 0 auto;
531
535
  }
532
536
 
533
- .hyperbook-markdown #qrcode-dialog svg {
537
+ #qrcode-dialog svg {
534
538
  width: 100%;
535
539
  }
536
540
 
@@ -548,23 +552,25 @@ figure {
548
552
  mask-image: url('data:image/svg+xml,<svg width="32px" height="32px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M3 9h6V3H3zm1-5h4v4H4zm1 1h2v2H5zm10 4h6V3h-6zm1-5h4v4h-4zm1 1h2v2h-2zM3 21h6v-6H3zm1-5h4v4H4zm1 1h2v2H5zm15 2h1v2h-2v-3h1zm0-3h1v1h-1zm0-1v1h-1v-1zm-10 2h1v4h-1v-4zm-4-7v2H4v-1H3v-1h3zm4-3h1v1h-1zm3-3v2h-1V3h2v1zm-3 0h1v1h-1zm10 8h1v2h-2v-1h1zm-1-2v1h-2v2h-2v-1h1v-2h3zm-7 4h-1v-1h-1v-1h2v2zm6 2h1v1h-1zm2-5v1h-1v-1zm-9 3v1h-1v-1zm6 5h1v2h-2v-2zm-3 0h1v1h-1v1h-2v-1h1v-1zm0-1v-1h2v1zm0-5h1v3h-1v1h-1v1h-1v-2h-1v-1h3v-1h-1v-1zm-9 0v1H4v-1zm12 4h-1v-1h1zm1-2h-2v-1h2zM8 10h1v1H8v1h1v2H8v-1H7v1H6v-2h1v-2zm3 0V8h3v3h-2v-1h1V9h-1v1zm0-4h1v1h-1zm-1 4h1v1h-1zm3-3V6h1v1z"/><path fill="none" d="M0 0h24v24H0z"/></svg>');
549
553
  }
550
554
 
551
- .hyperbook-markdown #toc-toggle,
552
- .hyperbook-markdown #qrcode-open {
555
+ .floating-buttons-container {
553
556
  position: fixed;
554
- padding: 4px;
555
557
  top: 90px;
556
558
  right: 20px;
559
+ display: flex;
560
+ flex-direction: column;
561
+ gap: 10px;
562
+ z-index: 1000;
563
+ }
564
+
565
+ #toc-toggle,
566
+ #qrcode-open {
567
+ padding: 4px;
557
568
  border-radius: 50%;
558
569
  height: 40px;
559
570
  width: 40px;
560
571
  border-style: solid;
561
572
  border-width: 1px;
562
573
  opacity: 0.7;
563
- z-index: 1000;
564
- }
565
-
566
- .hyperbook-markdown #qrcode-open {
567
- top: 140px;
568
574
  }
569
575
 
570
576
  .hyperbook-markdown #toc-toggle:hover,
@@ -587,3 +593,189 @@ figure {
587
593
  margin: 2px 3px;
588
594
  transition: 0.4s;
589
595
  }
596
+
597
+ /* Share button and dialog styles */
598
+ #share-button {
599
+ background: none;
600
+ border: none;
601
+ cursor: pointer;
602
+ margin-right: 16px;
603
+ padding: 8px;
604
+ line-height: 1;
605
+ display: flex;
606
+ align-items: center;
607
+ justify-content: center;
608
+ }
609
+
610
+ #share-button:hover {
611
+ opacity: 0.8;
612
+ }
613
+
614
+ .share-icon {
615
+ background-color: var(--color-brand-text);
616
+ width: 24px;
617
+ height: 24px;
618
+ mask-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/><line x1="8.59" y1="13.51" x2="15.42" y2="17.49"/><line x1="15.41" y1="6.51" x2="8.59" y2="10.49"/></svg>');
619
+ mask-size: contain;
620
+ mask-repeat: no-repeat;
621
+ mask-position: center;
622
+ }
623
+
624
+ header.inverted .share-icon {
625
+ background-color: var(--color-brand);
626
+ }
627
+
628
+ #share-dialog {
629
+ background: var(--color-background);
630
+ border: 1px solid var(--color-brand);
631
+ max-width: 600px;
632
+ max-height: 80vh;
633
+ padding: 0;
634
+ color: var(--color-text);
635
+ }
636
+
637
+ #share-dialog::backdrop {
638
+ background: rgba(0, 0, 0, 0.5);
639
+ }
640
+
641
+ #share-dialog .container {
642
+ padding: 24px;
643
+ display: flex;
644
+ flex-direction: column;
645
+ gap: 16px;
646
+ max-height: calc(80vh - 48px);
647
+ overflow-y: auto;
648
+ }
649
+
650
+ #share-dialog .title {
651
+ color: var(--color-text);
652
+ font-size: 1.5rem;
653
+ margin: 0;
654
+ }
655
+
656
+ #share-dialog .standalone-option {
657
+ display: flex;
658
+ align-items: center;
659
+ gap: 8px;
660
+ color: var(--color-text);
661
+ cursor: pointer;
662
+ font-size: 1rem;
663
+ padding: 8px;
664
+ background: var(--color-nav);
665
+ border-radius: 4px;
666
+ }
667
+
668
+ #share-dialog .sections-title {
669
+ color: var(--color-text);
670
+ font-size: 1.1rem;
671
+ margin: 8px 0 4px 0;
672
+ }
673
+
674
+ #share-dialog .sections-list {
675
+ display: flex;
676
+ flex-direction: column;
677
+ gap: 4px;
678
+ max-height: 300px;
679
+ overflow-y: auto;
680
+ border: 1px solid var(--color-spacer);
681
+ border-radius: 4px;
682
+ padding: 8px;
683
+ }
684
+
685
+ #share-dialog .heading-item {
686
+ display: flex;
687
+ align-items: center;
688
+ gap: 8px;
689
+ color: var(--color-text);
690
+ cursor: pointer;
691
+ padding: 4px 8px;
692
+ border-radius: 4px;
693
+ }
694
+
695
+ #share-dialog .heading-item:hover {
696
+ background: var(--color-nav);
697
+ }
698
+
699
+ #share-dialog .heading-item.level-1 {
700
+ font-weight: bold;
701
+ padding-left: 8px;
702
+ }
703
+
704
+ #share-dialog .heading-item.level-2 {
705
+ padding-left: 24px;
706
+ }
707
+
708
+ #share-dialog .heading-item.level-3 {
709
+ padding-left: 40px;
710
+ font-size: 0.95rem;
711
+ }
712
+
713
+ #share-dialog .heading-item.level-4 {
714
+ padding-left: 56px;
715
+ font-size: 0.9rem;
716
+ }
717
+
718
+ #share-dialog .heading-item.level-5 {
719
+ padding-left: 72px;
720
+ font-size: 0.85rem;
721
+ }
722
+
723
+ #share-dialog .heading-item.level-6 {
724
+ padding-left: 88px;
725
+ font-size: 0.8rem;
726
+ }
727
+
728
+ #share-dialog .url-preview {
729
+ background: var(--color-nav);
730
+ color: var(--color-text);
731
+ padding: 12px;
732
+ border-radius: 4px;
733
+ font-family: monospace;
734
+ font-size: 0.9rem;
735
+ word-break: break-all;
736
+ min-height: 40px;
737
+ }
738
+
739
+ #share-dialog .copy-button {
740
+ background: var(--color-brand);
741
+ color: var(--color-brand-text);
742
+ border: none;
743
+ padding: 12px 24px;
744
+ border-radius: 4px;
745
+ cursor: pointer;
746
+ font-size: 1rem;
747
+ font-weight: bold;
748
+ transition: opacity 0.2s;
749
+ }
750
+
751
+ #share-dialog .copy-button:hover {
752
+ opacity: 0.9;
753
+ }
754
+
755
+ #share-dialog .close {
756
+ background: none;
757
+ border: none;
758
+ font-size: 2rem;
759
+ color: var(--color-text);
760
+ cursor: pointer;
761
+ position: absolute;
762
+ top: 0px;
763
+ right: 0px;
764
+ width: 48px;
765
+ height: 48px;
766
+ display: flex;
767
+ justify-content: center;
768
+ align-items: center;
769
+ border-radius: 8px;
770
+ }
771
+
772
+ #share-dialog .close:hover .close-icon {
773
+ background-color: var(--color-brand);
774
+ }
775
+
776
+ #share-dialog .close .close-icon {
777
+ background-color: var(--color-text);
778
+ width: 32px;
779
+ height: 32px;
780
+ mask-image: url('data:image/svg+xml,<svg width="32px" height="32px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M5.29289 5.29289C5.68342 4.90237 6.31658 4.90237 6.70711 5.29289L12 10.5858L17.2929 5.29289C17.6834 4.90237 18.3166 4.90237 18.7071 5.29289C19.0976 5.68342 19.0976 6.31658 18.7071 6.70711L13.4142 12L18.7071 17.2929C19.0976 17.6834 19.0976 18.3166 18.7071 18.7071C18.3166 19.0976 17.6834 19.0976 17.2929 18.7071L12 13.4142L6.70711 18.7071C6.31658 19.0976 5.68342 19.0976 5.29289 18.7071C4.90237 18.3166 4.90237 17.6834 5.29289 17.2929L10.5858 12L5.29289 6.70711C4.90237 6.31658 4.90237 5.68342 5.29289 5.29289Z" fill="%230F1729"/></svg>');
781
+ }