@zerohive/hive-viewer 0.2.1 → 0.2.2

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/dist/index.mjs CHANGED
@@ -3,7 +3,7 @@ import { useEffect as useEffect6, useMemo as useMemo6, useRef as useRef3, useSta
3
3
 
4
4
  // src/utils/locale.ts
5
5
  var defaultLocale = {
6
- "loading": "Loading\u2026",
6
+ loading: "Loading\u2026",
7
7
  "error.title": "Error",
8
8
  "toolbar.layout.single": "Single page",
9
9
  "toolbar.layout.two": "Side-by-side",
@@ -26,7 +26,17 @@ var defaultLocale = {
26
26
  function guessFileType(name, explicit) {
27
27
  if (explicit) return explicit;
28
28
  const ext = (name?.split(".").pop() || "").toLowerCase();
29
- const allowed = ["pdf", "md", "docx", "xlsx", "pptx", "txt", "png", "jpg", "svg"];
29
+ const allowed = [
30
+ "pdf",
31
+ "md",
32
+ "docx",
33
+ "xlsx",
34
+ "pptx",
35
+ "txt",
36
+ "png",
37
+ "jpg",
38
+ "svg"
39
+ ];
30
40
  return allowed.includes(ext) ? ext : "txt";
31
41
  }
32
42
  function arrayBufferToBase64(buf) {
@@ -57,7 +67,8 @@ async function resolveSource(args) {
57
67
  const ab = await base64ToArrayBuffer(args.base64);
58
68
  return { fileType, fileName, arrayBuffer: ab };
59
69
  }
60
- if (!args.fileUrl) throw new Error("No file source provided. Use fileUrl, blob, or base64.");
70
+ if (!args.fileUrl)
71
+ throw new Error("No file source provided. Use fileUrl, blob, or base64.");
61
72
  const res = await fetch(args.fileUrl);
62
73
  if (!res.ok) throw new Error(`Failed to fetch file (${res.status})`);
63
74
  const total = Number(res.headers.get("content-length") || "") || void 0;
@@ -173,44 +184,69 @@ function SignaturePanel(props) {
173
184
 
174
185
  // src/renderers/PdfRenderer.tsx
175
186
  import { useEffect, useMemo, useRef, useState } from "react";
176
- import { GlobalWorkerOptions, getDocument } from "pdfjs-dist";
187
+ import {
188
+ GlobalWorkerOptions,
189
+ getDocument
190
+ } from "pdfjs-dist";
177
191
  import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
178
192
  function PdfRenderer(props) {
179
193
  const { url, arrayBuffer } = props;
180
194
  const [doc, setDoc] = useState(null);
181
195
  const [pageCount, setPageCount] = useState(0);
182
- const [rendered, setRendered] = useState(/* @__PURE__ */ new Map());
196
+ const [rendered, setRendered] = useState(
197
+ /* @__PURE__ */ new Map()
198
+ );
183
199
  const [thumbs, setThumbs] = useState([]);
184
200
  const [size, setSize] = useState({ w: 840, h: 1188 });
201
+ const [error, setError] = useState(null);
202
+ const [loading, setLoading] = useState(false);
185
203
  const containerRef = useRef(null);
186
204
  useEffect(() => {
187
205
  try {
188
- GlobalWorkerOptions.workerSrc = new URL("pdfjs-dist/build/pdf.worker.min.mjs", import.meta.url).toString();
206
+ GlobalWorkerOptions.workerSrc = new URL(
207
+ "pdfjs-dist/build/pdf.worker.min.mjs",
208
+ import.meta.url
209
+ ).toString();
189
210
  } catch {
190
211
  }
191
212
  }, []);
192
213
  useEffect(() => {
193
214
  let cancel = false;
215
+ setError(null);
216
+ setLoading(true);
194
217
  (async () => {
195
218
  setDoc(null);
196
219
  setRendered(/* @__PURE__ */ new Map());
197
220
  setThumbs([]);
198
- if (!url && !arrayBuffer) return;
199
- const task = getDocument(url ? { url, rangeChunkSize: 512 * 1024 } : { data: arrayBuffer });
200
- const pdf = await task.promise;
201
- if (cancel) return;
202
- setDoc(pdf);
203
- setPageCount(pdf.numPages);
204
- props.onPageCount(pdf.numPages);
205
- setThumbs(Array.from({ length: pdf.numPages }));
206
- const p1 = await pdf.getPage(1);
207
- const base = p1.getViewport({ scale: 1 });
208
- const w = Math.min(980, Math.max(640, base.width));
209
- const s = w / base.width;
210
- const vp = p1.getViewport({ scale: s });
211
- setSize({ w: Math.round(vp.width), h: Math.round(vp.height) });
212
- })().catch(() => {
213
- });
221
+ if (!url && !arrayBuffer) {
222
+ setError("No PDF source provided.");
223
+ setLoading(false);
224
+ return;
225
+ }
226
+ try {
227
+ const task = getDocument(
228
+ url ? { url, rangeChunkSize: 512 * 1024 } : { data: arrayBuffer }
229
+ );
230
+ const pdf = await task.promise;
231
+ if (cancel) return;
232
+ setDoc(pdf);
233
+ setPageCount(pdf.numPages);
234
+ props.onPageCount(pdf.numPages);
235
+ setThumbs(Array.from({ length: pdf.numPages }));
236
+ const p1 = await pdf.getPage(1);
237
+ const base = p1.getViewport({ scale: 1 });
238
+ const w = Math.min(980, Math.max(640, base.width));
239
+ const s = w / base.width;
240
+ const vp = p1.getViewport({ scale: s });
241
+ setSize({ w: Math.round(vp.width), h: Math.round(vp.height) });
242
+ } catch (e) {
243
+ setError(
244
+ "Failed to load PDF. " + (e instanceof Error ? e.message : "")
245
+ );
246
+ } finally {
247
+ setLoading(false);
248
+ }
249
+ })();
214
250
  return () => {
215
251
  cancel = true;
216
252
  };
@@ -232,37 +268,43 @@ function PdfRenderer(props) {
232
268
  (async () => {
233
269
  for (const p of pagesToShow) {
234
270
  if (rendered.has(p)) continue;
235
- const page = await doc.getPage(p);
236
- if (cancel) return;
237
- const base = page.getViewport({ scale: 1 });
238
- const vp = page.getViewport({ scale: size.w / base.width });
239
- const canvas = document.createElement("canvas");
240
- canvas.width = Math.round(vp.width);
241
- canvas.height = Math.round(vp.height);
242
- const ctx = canvas.getContext("2d", { alpha: false });
243
- if (!ctx) continue;
244
- await page.render({ canvasContext: ctx, viewport: vp }).promise;
245
- if (cancel) return;
246
- setRendered((prev) => {
247
- const next = new Map(prev);
248
- next.set(p, canvas);
249
- return next;
250
- });
251
271
  try {
252
- const tw = 140;
253
- const th = Math.round(tw * (canvas.height / canvas.width));
254
- const t = document.createElement("canvas");
255
- t.width = tw;
256
- t.height = th;
257
- const tctx = t.getContext("2d");
258
- if (tctx) {
259
- tctx.drawImage(canvas, 0, 0, tw, th);
260
- const url2 = t.toDataURL("image/jpeg", 0.75);
261
- setThumbs((prev) => {
262
- const next = prev.slice();
263
- next[p - 1] = url2;
264
- return next;
265
- });
272
+ const page = await doc.getPage(p);
273
+ if (cancel) return;
274
+ const base = page.getViewport({ scale: 1 });
275
+ const vp = page.getViewport({ scale: size.w / base.width });
276
+ const canvas = document.createElement("canvas");
277
+ canvas.width = Math.round(vp.width);
278
+ canvas.height = Math.round(vp.height);
279
+ const ctx = canvas.getContext("2d", { alpha: false });
280
+ if (!ctx) continue;
281
+ await page.render({ canvasContext: ctx, viewport: vp }).promise;
282
+ if (cancel) return;
283
+ setRendered((prev) => {
284
+ const next = new Map(prev);
285
+ next.set(p, canvas);
286
+ return next;
287
+ });
288
+ if (!thumbs[p - 1]) {
289
+ const thumbCanvas = document.createElement("canvas");
290
+ const thumbScale = 120 / vp.width;
291
+ thumbCanvas.width = Math.round(vp.width * thumbScale);
292
+ thumbCanvas.height = Math.round(vp.height * thumbScale);
293
+ const thumbCtx = thumbCanvas.getContext("2d", { alpha: false });
294
+ if (thumbCtx) {
295
+ thumbCtx.drawImage(
296
+ canvas,
297
+ 0,
298
+ 0,
299
+ thumbCanvas.width,
300
+ thumbCanvas.height
301
+ );
302
+ setThumbs((prev) => {
303
+ const arr = prev.slice();
304
+ arr[p - 1] = thumbCanvas.toDataURL("image/png");
305
+ return arr;
306
+ });
307
+ }
266
308
  }
267
309
  } catch {
268
310
  }
@@ -271,13 +313,16 @@ function PdfRenderer(props) {
271
313
  return () => {
272
314
  cancel = true;
273
315
  };
274
- }, [doc, pagesToShow, size.w, rendered]);
316
+ }, [doc, pagesToShow, size.w, rendered, thumbs]);
275
317
  function onWheel(e) {
276
318
  if (!pageCount) return;
277
319
  if (Math.abs(e.deltaY) < 10) return;
278
320
  const dir = e.deltaY > 0 ? 1 : -1;
279
321
  const step = props.layout === "side-by-side" ? 2 : 1;
280
- const next = Math.max(1, Math.min(pageCount, props.currentPage + dir * step));
322
+ const next = Math.max(
323
+ 1,
324
+ Math.min(pageCount, props.currentPage + dir * step)
325
+ );
281
326
  props.onCurrentPageChange(next);
282
327
  }
283
328
  function clickPlace(e, page) {
@@ -290,22 +335,37 @@ function PdfRenderer(props) {
290
335
  }
291
336
  return /* @__PURE__ */ jsxs4("div", { className: "hv-doc", ref: containerRef, onWheel, children: [
292
337
  !doc ? /* @__PURE__ */ jsx4("div", { className: "hv-loading", children: "Loading PDF\u2026" }) : null,
293
- doc ? /* @__PURE__ */ jsx4("div", { className: props.layout === "side-by-side" ? "hv-pages hv-pages--two" : "hv-pages", children: pagesToShow.map((p) => {
294
- const c = rendered.get(p);
295
- return /* @__PURE__ */ jsx4("div", { className: "hv-page", style: { width: size.w, height: size.h }, onClick: (e) => clickPlace(e, p), children: c ? /* @__PURE__ */ jsx4(
296
- "canvas",
297
- {
298
- className: "hv-canvas",
299
- width: c.width,
300
- height: c.height,
301
- ref: (node) => {
302
- if (!node) return;
303
- const ctx = node.getContext("2d");
304
- if (ctx) ctx.drawImage(c, 0, 0);
305
- }
306
- }
307
- ) : /* @__PURE__ */ jsx4("div", { className: "hv-loading", children: "Rendering\u2026" }) }, p);
308
- }) }) : null
338
+ doc ? /* @__PURE__ */ jsx4(
339
+ "div",
340
+ {
341
+ className: props.layout === "side-by-side" ? "hv-pages hv-pages--two" : "hv-pages",
342
+ children: pagesToShow.map((p) => {
343
+ const c = rendered.get(p);
344
+ return /* @__PURE__ */ jsx4(
345
+ "div",
346
+ {
347
+ className: "hv-page",
348
+ style: { width: size.w, height: size.h },
349
+ onClick: (e) => clickPlace(e, p),
350
+ children: c ? /* @__PURE__ */ jsx4(
351
+ "canvas",
352
+ {
353
+ className: "hv-canvas",
354
+ width: c.width,
355
+ height: c.height,
356
+ ref: (node) => {
357
+ if (!node) return;
358
+ const ctx = node.getContext("2d");
359
+ if (ctx) ctx.drawImage(c, 0, 0);
360
+ }
361
+ }
362
+ ) : /* @__PURE__ */ jsx4("div", { className: "hv-loading", children: "Rendering\u2026" })
363
+ },
364
+ p
365
+ );
366
+ })
367
+ }
368
+ ) : null
309
369
  ] });
310
370
  }
311
371
 
@@ -613,7 +673,11 @@ function ensureExt(name, ext) {
613
673
  // src/renderers/ImageRenderer.tsx
614
674
  import { useEffect as useEffect4, useMemo as useMemo4, useState as useState4 } from "react";
615
675
  import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
616
- function ImageRenderer({ arrayBuffer, fileType, fileName }) {
676
+ function ImageRenderer({
677
+ arrayBuffer,
678
+ fileType,
679
+ fileName
680
+ }) {
617
681
  const [zoom, setZoom] = useState4(1);
618
682
  const url = useMemo4(() => {
619
683
  if (!arrayBuffer) return void 0;
@@ -629,14 +693,42 @@ function ImageRenderer({ arrayBuffer, fileType, fileName }) {
629
693
  /* @__PURE__ */ jsxs7("div", { className: "hv-mini-toolbar", children: [
630
694
  /* @__PURE__ */ jsx7("div", { className: "hv-title", children: fileName }),
631
695
  /* @__PURE__ */ jsx7("div", { className: "hv-spacer" }),
632
- /* @__PURE__ */ jsx7("button", { type: "button", className: "hv-btn", onClick: () => setZoom((z) => Math.max(0.25, z - 0.25)), children: "-" }),
696
+ /* @__PURE__ */ jsx7(
697
+ "button",
698
+ {
699
+ type: "button",
700
+ className: "hv-btn",
701
+ onClick: () => setZoom((z) => Math.max(0.25, z - 0.25)),
702
+ children: "-"
703
+ }
704
+ ),
633
705
  /* @__PURE__ */ jsxs7("div", { className: "hv-zoom", children: [
634
706
  Math.round(zoom * 100),
635
707
  "%"
636
708
  ] }),
637
- /* @__PURE__ */ jsx7("button", { type: "button", className: "hv-btn", onClick: () => setZoom((z) => Math.min(4, z + 0.25)), children: "+" })
709
+ /* @__PURE__ */ jsx7(
710
+ "button",
711
+ {
712
+ type: "button",
713
+ className: "hv-btn",
714
+ onClick: () => setZoom((z) => Math.min(4, z + 0.25)),
715
+ children: "+"
716
+ }
717
+ )
638
718
  ] }),
639
- /* @__PURE__ */ jsx7("div", { className: "hv-center", children: url ? /* @__PURE__ */ jsx7("img", { src: url, alt: fileName, style: { transform: `scale(${zoom})` }, className: "hv-image" }) : /* @__PURE__ */ jsx7("div", { className: "hv-loading", children: "Loading\u2026" }) })
719
+ /* @__PURE__ */ jsxs7("div", { className: "hv-center", children: [
720
+ !arrayBuffer && /* @__PURE__ */ jsx7("div", { className: "hv-error", children: "No image data provided." }),
721
+ arrayBuffer && !url && /* @__PURE__ */ jsx7("div", { className: "hv-error", children: "Failed to load image." }),
722
+ url && /* @__PURE__ */ jsx7(
723
+ "img",
724
+ {
725
+ src: url,
726
+ alt: fileName,
727
+ style: { transform: `scale(${zoom})` },
728
+ className: "hv-image"
729
+ }
730
+ )
731
+ ] })
640
732
  ] });
641
733
  }
642
734
 
@@ -653,37 +745,57 @@ function extractText(xml) {
653
745
  function PptxRenderer(props) {
654
746
  const [slides, setSlides] = useState5([]);
655
747
  const [thumbs, setThumbs] = useState5([]);
748
+ const [error, setError] = useState5(null);
749
+ const [loading, setLoading] = useState5(false);
656
750
  useEffect5(() => {
657
751
  let cancelled = false;
752
+ setError(null);
753
+ setLoading(true);
658
754
  (async () => {
659
755
  setSlides([]);
660
756
  setThumbs([]);
661
757
  if (!props.arrayBuffer) {
662
758
  props.onSlideCount(1);
663
759
  setSlides([{ index: 1, text: "No content" }]);
760
+ setError("No PPTX data provided.");
761
+ setLoading(false);
664
762
  return;
665
763
  }
666
- const zip = await JSZip.loadAsync(props.arrayBuffer);
667
- const files = Object.keys(zip.files).filter((p) => /^ppt\/slides\/slide\d+\.xml$/.test(p)).sort((a, b) => {
668
- const na = Number(a.match(/slide(\d+)\.xml/)?.[1] || 0);
669
- const nb = Number(b.match(/slide(\d+)\.xml/)?.[1] || 0);
670
- return na - nb;
671
- });
672
- const out = [];
673
- for (let i = 0; i < files.length; i++) {
674
- const xml = await zip.file(files[i]).async("string");
675
- out.push({ index: i + 1, text: extractText(xml) });
764
+ try {
765
+ const zip = await JSZip.loadAsync(props.arrayBuffer);
766
+ const files = Object.keys(zip.files).filter((p) => /^ppt\/slides\/slide\d+\.xml$/.test(p)).sort((a, b) => {
767
+ const na = Number(a.match(/slide(\d+)\.xml/)?.[1] || 0);
768
+ const nb = Number(b.match(/slide(\d+)\.xml/)?.[1] || 0);
769
+ return na - nb;
770
+ });
771
+ const out = [];
772
+ for (let i = 0; i < files.length; i++) {
773
+ const xml = await zip.file(files[i]).async("string");
774
+ out.push({ index: i + 1, text: extractText(xml) });
775
+ }
776
+ if (cancelled) return;
777
+ const count = Math.max(1, out.length);
778
+ props.onSlideCount(count);
779
+ setSlides(out.length ? out : [{ index: 1, text: "(empty)" }]);
780
+ setThumbs(
781
+ Array.from(
782
+ { length: count },
783
+ (_, i) => `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgThumb(i + 1))}`
784
+ )
785
+ );
786
+ } catch (e) {
787
+ props.onSlideCount(1);
788
+ setSlides([
789
+ { index: 1, text: "Unable to render this .pptx in-browser." }
790
+ ]);
791
+ setThumbs([void 0]);
792
+ setError(
793
+ "Failed to load PPTX. " + (e instanceof Error ? e.message : "")
794
+ );
795
+ } finally {
796
+ setLoading(false);
676
797
  }
677
- if (cancelled) return;
678
- const count = Math.max(1, out.length);
679
- props.onSlideCount(count);
680
- setSlides(out.length ? out : [{ index: 1, text: "(empty)" }]);
681
- setThumbs(Array.from({ length: count }, (_, i) => `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgThumb(i + 1))}`));
682
- })().catch(() => {
683
- props.onSlideCount(1);
684
- setSlides([{ index: 1, text: "Unable to render this .pptx in-browser." }]);
685
- setThumbs([void 0]);
686
- });
798
+ })();
687
799
  return () => {
688
800
  cancelled = true;
689
801
  };
@@ -692,19 +804,43 @@ function PptxRenderer(props) {
692
804
  props.onThumbs(thumbs);
693
805
  }, [thumbs]);
694
806
  const pagesToShow = useMemo5(() => {
695
- if (props.layout === "side-by-side") return [props.currentPage, Math.min(slides.length || props.currentPage + 1, props.currentPage + 1)];
807
+ if (props.layout === "side-by-side")
808
+ return [
809
+ props.currentPage,
810
+ Math.min(slides.length || props.currentPage + 1, props.currentPage + 1)
811
+ ];
696
812
  return [props.currentPage];
697
813
  }, [props.currentPage, props.layout, slides.length]);
698
- return /* @__PURE__ */ jsx8("div", { className: "hv-doc", children: /* @__PURE__ */ jsx8("div", { className: props.layout === "side-by-side" ? "hv-pages hv-pages--two" : "hv-pages", children: pagesToShow.map((p) => {
699
- const s = slides[p - 1];
700
- return /* @__PURE__ */ jsxs8("div", { className: "hv-slide", tabIndex: 0, onFocus: () => props.onCurrentPageChange(p), children: [
701
- /* @__PURE__ */ jsxs8("div", { className: "hv-slide-title", children: [
702
- "Slide ",
703
- p
704
- ] }),
705
- /* @__PURE__ */ jsx8("div", { className: "hv-slide-text", children: s?.text || "" })
706
- ] }, p);
707
- }) }) });
814
+ return /* @__PURE__ */ jsxs8("div", { className: "hv-doc", children: [
815
+ loading && /* @__PURE__ */ jsx8("div", { className: "hv-loading", children: "Loading PPTX\u2026" }),
816
+ error && /* @__PURE__ */ jsx8("div", { className: "hv-error", children: error }),
817
+ !loading && !error && (!slides || slides.length === 0) && /* @__PURE__ */ jsx8("div", { className: "hv-error", children: "No slides to display." }),
818
+ !error && slides && slides.length > 0 && /* @__PURE__ */ jsx8(
819
+ "div",
820
+ {
821
+ className: props.layout === "side-by-side" ? "hv-pages hv-pages--two" : "hv-pages",
822
+ children: pagesToShow.map((p) => {
823
+ const s = slides[p - 1];
824
+ return /* @__PURE__ */ jsxs8(
825
+ "div",
826
+ {
827
+ className: "hv-slide",
828
+ tabIndex: 0,
829
+ onFocus: () => props.onCurrentPageChange(p),
830
+ children: [
831
+ /* @__PURE__ */ jsxs8("div", { className: "hv-slide-title", children: [
832
+ "Slide ",
833
+ p
834
+ ] }),
835
+ /* @__PURE__ */ jsx8("div", { className: "hv-slide-text", children: s?.text || "" })
836
+ ]
837
+ },
838
+ p
839
+ );
840
+ })
841
+ }
842
+ )
843
+ ] });
708
844
  }
709
845
  function svgThumb(n) {
710
846
  return `<svg xmlns="http://www.w3.org/2000/svg" width="180" height="100"><rect width="100%" height="100%" rx="12" fill="#111827"/><text x="50%" y="54%" font-size="18" fill="#e5e7eb" text-anchor="middle">${n}</text></svg>`;
package/dist/styles.css CHANGED
@@ -1,3 +1,78 @@
1
+ /* Focus ring for accessibility */
2
+ .hv-btn:focus, .hv-thumb:focus, .hv-modal-close:focus {
3
+ outline: 2px solid #6366f1;
4
+ outline-offset: 2px;
5
+ z-index: 2;
6
+ }
7
+
8
+ /* Modal overlay and modal polish */
9
+ .hv-modal-overlay {
10
+ position: fixed;
11
+ inset: 0;
12
+ background: rgba(0,0,0,0.45);
13
+ z-index: 1000;
14
+ display: flex;
15
+ align-items: center;
16
+ justify-content: center;
17
+ padding: 16px;
18
+ }
19
+ .hv-modal {
20
+ background: #fff;
21
+ border-radius: 16px;
22
+ max-width: 95vw;
23
+ max-height: 95vh;
24
+ overflow: auto;
25
+ position: relative;
26
+ padding: 0;
27
+ box-shadow: 0 8px 32px rgba(0,0,0,0.25);
28
+ display: flex;
29
+ flex-direction: column;
30
+ }
31
+ .hv-modal-close {
32
+ position: absolute;
33
+ top: 12px;
34
+ right: 16px;
35
+ background: none;
36
+ border: none;
37
+ font-size: 2rem;
38
+ color: #888;
39
+ cursor: pointer;
40
+ z-index: 1;
41
+ }
42
+
43
+ /* Error and loading states */
44
+ .hv-error {
45
+ padding: 18px 24px;
46
+ color: #b91c1c;
47
+ background: #fef2f2;
48
+ border-radius: 10px;
49
+ margin: 16px auto;
50
+ max-width: 480px;
51
+ font-size: 1rem;
52
+ text-align: center;
53
+ }
54
+ .hv-loading {
55
+ padding: 18px 24px;
56
+ color: #64748b;
57
+ background: #f1f5f9;
58
+ border-radius: 10px;
59
+ margin: 16px auto;
60
+ max-width: 480px;
61
+ font-size: 1rem;
62
+ text-align: center;
63
+ }
64
+
65
+ /* Touch-friendly spacing */
66
+ @media (pointer: coarse) {
67
+ .hv-btn, .hv-thumb, .hv-modal-close {
68
+ min-height: 44px;
69
+ min-width: 44px;
70
+ font-size: 1.1rem;
71
+ }
72
+ .hv-toolbar, .hv-ribbon {
73
+ padding: 16px 8px;
74
+ }
75
+ }
1
76
  .hv-root{--hv-bg:#0b1020;--hv-fg:#e5e7eb;--hv-muted:#9ca3af;--hv-panel:#0f172a;--hv-border:rgba(255,255,255,.10);--hv-btn:rgba(255,255,255,.06);--hv-btnh:rgba(255,255,255,.10);background:var(--hv-bg);color:var(--hv-fg);border:1px solid var(--hv-border);border-radius:16px;overflow:hidden}
2
77
  .hv-root[data-hv-theme="light"]{--hv-bg:#f8fafc;--hv-fg:#0f172a;--hv-muted:#64748b;--hv-panel:#ffffff;--hv-border:rgba(15,23,42,.12);--hv-btn:rgba(15,23,42,.06);--hv-btnh:rgba(15,23,42,.10)}
3
78
  .hv-toolbar{display:flex;gap:10px;align-items:center;justify-content:space-between;padding:10px 12px;background:var(--hv-panel);border-bottom:1px solid var(--hv-border)}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zerohive/hive-viewer",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -43,6 +43,8 @@
43
43
  "xlsx": "^0.18.5"
44
44
  },
45
45
  "devDependencies": {
46
+ "@types/markdown-it": "^14.1.2",
47
+ "@types/node": "^25.0.9",
46
48
  "@types/react": "^18.3.11",
47
49
  "@types/react-dom": "^18.3.1",
48
50
  "react": "^18.3.1",