@braincrew-lab/langchain-canvas 0.2.0 → 0.3.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.
@@ -1,15 +1,25 @@
1
1
  import { resolveElements } from './chunk-6L3AL6W4.js';
2
- import { useArtifactPatch } from './chunk-UL5F66PN.js';
3
- import './chunk-S54GJDSJ.js';
2
+ import { useArtifactPatch } from './chunk-FSFOURG5.js';
3
+ import './chunk-ZLXAWRUP.js';
4
+ import { useT } from './chunk-QMOJEGRH.js';
4
5
  import { useState, useRef, useEffect } from 'react';
5
6
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
6
7
 
7
8
  function shapeStyle(el) {
8
- const fill = el.fill ?? "currentColor";
9
- if (el.shape === "ellipse") return { width: "100%", height: "100%", background: fill, borderRadius: "50%" };
10
- if (el.shape === "line") return { width: "100%", height: "100%", background: fill, borderRadius: 2 };
11
- return { width: "100%", height: "100%", background: fill, borderRadius: 8 };
9
+ if (el.shape === "line") return { width: "100%", height: "100%", background: el.fill ?? "currentColor", borderRadius: 2 };
10
+ const borderRadius = el.shape === "ellipse" ? "50%" : el.radius ?? 8;
11
+ if (!el.fill) {
12
+ return { width: "100%", height: "100%", background: "transparent", border: "1.5px solid currentColor", borderRadius, boxSizing: "border-box" };
13
+ }
14
+ return { width: "100%", height: "100%", background: el.fill, borderRadius };
15
+ }
16
+ function rotateStyle(el) {
17
+ return el.rotate ? { transform: `rotate(${el.rotate}deg)` } : {};
12
18
  }
19
+ var normalizeDeg = (deg) => {
20
+ const d = (deg % 360 + 360) % 360;
21
+ return d === 0 ? void 0 : d;
22
+ };
13
23
  var clamp = (v, min, max) => Math.max(min, Math.min(max, v));
14
24
  var dupSeq = 0;
15
25
  var dupId = (base) => `${base}_c${Date.now().toString(36)}${dupSeq++}`;
@@ -40,6 +50,7 @@ function boundsOf(els) {
40
50
  return { x1, y1, x2, y2 };
41
51
  }
42
52
  function FreeSlide({ elements, onChange, padding }) {
53
+ const t = useT();
43
54
  const slideRef = useRef(null);
44
55
  const [els, setEls] = useState(elements);
45
56
  const [selected, setSelected] = useState([]);
@@ -297,8 +308,8 @@ function FreeSlide({ elements, onChange, padding }) {
297
308
  };
298
309
  const onKeyDown = (e) => {
299
310
  if (editingId) return;
300
- const t = e.target;
301
- if (t.tagName === "INPUT" || t.tagName === "TEXTAREA" || t.isContentEditable) return;
311
+ const t2 = e.target;
312
+ if (t2.tagName === "INPUT" || t2.tagName === "TEXTAREA" || t2.isContentEditable) return;
302
313
  const meta = e.metaKey || e.ctrlKey;
303
314
  if (e.key === "Escape") {
304
315
  setSelected([]);
@@ -380,7 +391,7 @@ function FreeSlide({ elements, onChange, padding }) {
380
391
  "div",
381
392
  {
382
393
  className: `cv-free__el ${selectedSet.has(el.id) ? "is-selected cv-free__el--selected" : ""}`,
383
- style: { left: `${el.x}%`, top: `${el.y}%`, width: `${el.w}%`, height: `${el.h}%` },
394
+ style: { left: `${el.x}%`, top: `${el.y}%`, width: `${el.w}%`, height: `${el.h}%`, ...rotateStyle(el) },
384
395
  onPointerDown: (e) => onDown(e, el, "move"),
385
396
  onDoubleClick: (e) => {
386
397
  if (el.type === "text") {
@@ -397,7 +408,9 @@ function FreeSlide({ elements, onChange, padding }) {
397
408
  contentEditable: editingId === el.id,
398
409
  suppressContentEditableWarning: true,
399
410
  style: {
400
- fontSize: el.fontSize ?? 24,
411
+ // px at the 1280-wide design → cqw, so type scales with the slide
412
+ // (12.8px of design width per 1cqw).
413
+ fontSize: `${((el.fontSize ?? 24) / 12.8).toFixed(3)}cqw`,
401
414
  fontWeight: el.bold ? 700 : 400,
402
415
  color: el.color,
403
416
  textAlign: el.align ?? "left"
@@ -408,9 +421,18 @@ function FreeSlide({ elements, onChange, padding }) {
408
421
  },
409
422
  children: el.text
410
423
  }
411
- ) : el.type === "shape" ? /* @__PURE__ */ jsx("div", { style: shapeStyle(el) }) : /* @__PURE__ */ jsx("img", { className: "cv-free__img", src: el.src, alt: "", draggable: false }),
424
+ ) : el.type === "shape" ? /* @__PURE__ */ jsx("div", { style: shapeStyle(el) }) : /* @__PURE__ */ jsx(
425
+ "img",
426
+ {
427
+ className: "cv-free__img",
428
+ src: el.src,
429
+ alt: "",
430
+ draggable: false,
431
+ style: { objectFit: el.fit ?? "contain", ...el.radius ? { borderRadius: el.radius } : {} }
432
+ }
433
+ ),
412
434
  soloId === el.id && el.type === "text" && /* @__PURE__ */ jsxs("div", { className: `cv-free__fmt ${el.y < 16 ? "cv-free__fmt--below" : ""}`, onPointerDown: (e) => e.stopPropagation(), onClick: (e) => e.stopPropagation(), children: [
413
- /* @__PURE__ */ jsx("button", { className: el.bold ? "is-on" : "", onClick: () => updateEl(el.id, { bold: !el.bold }), title: "Bold", children: /* @__PURE__ */ jsx("b", { children: "B" }) }),
435
+ /* @__PURE__ */ jsx("button", { className: el.bold ? "is-on" : "", onClick: () => updateEl(el.id, { bold: !el.bold }), title: t("bold"), children: /* @__PURE__ */ jsx("b", { children: "B" }) }),
414
436
  /* @__PURE__ */ jsx(
415
437
  "input",
416
438
  {
@@ -419,42 +441,94 @@ function FreeSlide({ elements, onChange, padding }) {
419
441
  max: 120,
420
442
  value: el.fontSize ?? 24,
421
443
  onChange: (e) => updateEl(el.id, { fontSize: Number(e.target.value) }),
422
- title: "Font size"
444
+ title: t("fontSize")
423
445
  }
424
446
  ),
425
- /* @__PURE__ */ jsx("input", { type: "color", value: el.color ?? "#1f2328", onChange: (e) => updateEl(el.id, { color: e.target.value }), title: "Text color" }),
426
- /* @__PURE__ */ jsx("button", { onClick: () => updateEl(el.id, { align: "left" }), title: "Align left", children: "\u27F8" }),
427
- /* @__PURE__ */ jsx("button", { onClick: () => updateEl(el.id, { align: "center" }), title: "Align center", children: "\u2261" }),
428
- /* @__PURE__ */ jsx("button", { onClick: () => updateEl(el.id, { align: "right" }), title: "Align right", children: "\u27F9" })
447
+ /* @__PURE__ */ jsx("input", { type: "color", value: el.color ?? "#1f2328", onChange: (e) => updateEl(el.id, { color: e.target.value }), title: t("textColor") }),
448
+ /* @__PURE__ */ jsx("button", { onClick: () => updateEl(el.id, { align: "left" }), title: t("alignLeft"), children: "\u27F8" }),
449
+ /* @__PURE__ */ jsx("button", { onClick: () => updateEl(el.id, { align: "center" }), title: t("alignCenter"), children: "\u2261" }),
450
+ /* @__PURE__ */ jsx("button", { onClick: () => updateEl(el.id, { align: "right" }), title: t("alignRight"), children: "\u27F9" })
429
451
  ] }),
430
452
  soloId === el.id && /* @__PURE__ */ jsxs(Fragment, { children: [
431
- /* @__PURE__ */ jsx("span", { className: "cv-free__resize", title: "Resize (hold Shift to lock aspect)", onPointerDown: (e) => onDown(e, el, "resize") }),
453
+ /* @__PURE__ */ jsx("span", { className: "cv-free__resize", title: t("resizeTip"), onPointerDown: (e) => onDown(e, el, "resize") }),
432
454
  /* @__PURE__ */ jsxs("div", { className: `cv-free__ctl ${el.y < 16 ? "cv-free__ctl--below" : ""}`, onPointerDown: (e) => e.stopPropagation(), children: [
433
- el.type === "shape" && /* @__PURE__ */ jsx("input", { className: "cv-free__ctl-fill", type: "color", value: el.fill ?? "#5b5bd6", onChange: (e) => updateEl(el.id, { fill: e.target.value }), onClick: (e) => e.stopPropagation(), title: "Fill color" }),
455
+ el.type === "shape" && /* @__PURE__ */ jsx("input", { className: "cv-free__ctl-fill", type: "color", value: el.fill ?? "#5b5bd6", onChange: (e) => updateEl(el.id, { fill: e.target.value }), onClick: (e) => e.stopPropagation(), title: t("fillColor") }),
456
+ el.type === "image" && /* @__PURE__ */ jsx(
457
+ "button",
458
+ {
459
+ className: el.fit === "cover" ? "is-on" : "",
460
+ onClick: (e) => {
461
+ e.stopPropagation();
462
+ updateEl(el.id, { fit: el.fit === "cover" ? void 0 : "cover" });
463
+ },
464
+ title: t("fitToggle"),
465
+ children: "\u26F6"
466
+ }
467
+ ),
468
+ (el.type === "image" || el.shape === "rect") && /* @__PURE__ */ jsx(
469
+ "input",
470
+ {
471
+ className: "cv-free__ctl-radius",
472
+ type: "number",
473
+ min: 0,
474
+ max: 32,
475
+ value: el.radius ?? (el.type === "shape" ? 8 : 0),
476
+ onChange: (e) => updateEl(el.id, { radius: clamp(Number(e.target.value) || 0, 0, 32) }),
477
+ onClick: (e) => e.stopPropagation(),
478
+ title: t("cornerRadius")
479
+ }
480
+ ),
481
+ /* @__PURE__ */ jsx(
482
+ "button",
483
+ {
484
+ onClick: (e) => {
485
+ e.stopPropagation();
486
+ updateEl(el.id, { rotate: normalizeDeg((el.rotate ?? 0) + (e.shiftKey ? -15 : 15)) });
487
+ },
488
+ title: t("rotateStep"),
489
+ children: "\u27F3"
490
+ }
491
+ ),
492
+ /* @__PURE__ */ jsx(
493
+ "input",
494
+ {
495
+ className: "cv-free__ctl-rot",
496
+ type: "number",
497
+ min: -360,
498
+ max: 360,
499
+ value: Math.round(el.rotate ?? 0),
500
+ onChange: (e) => {
501
+ const deg = Number(e.target.value);
502
+ updateEl(el.id, { rotate: Number.isFinite(deg) && deg !== 0 ? clamp(deg, -360, 360) : void 0 });
503
+ },
504
+ onClick: (e) => e.stopPropagation(),
505
+ title: t("rotationDeg")
506
+ }
507
+ ),
434
508
  /* @__PURE__ */ jsx("button", { onClick: (e) => {
435
509
  e.stopPropagation();
436
510
  duplicateSelection();
437
- }, title: "Duplicate (\u2318D)", children: "\u29C9" }),
511
+ }, title: t("duplicateShort"), children: "\u29C9" }),
438
512
  /* @__PURE__ */ jsx("button", { onClick: (e) => {
439
513
  e.stopPropagation();
440
514
  reorderSelection(1, true);
441
- }, title: "Bring to front", children: "\u2912" }),
515
+ }, title: t("bringToFront"), children: "\u2912" }),
442
516
  /* @__PURE__ */ jsx("button", { onClick: (e) => {
443
517
  e.stopPropagation();
444
518
  reorderSelection(1);
445
- }, title: "Bring forward (\u2318])", children: "\u2191" }),
519
+ }, title: t("bringForward"), children: "\u2191" }),
446
520
  /* @__PURE__ */ jsx("button", { onClick: (e) => {
447
521
  e.stopPropagation();
448
522
  reorderSelection(-1);
449
- }, title: "Send backward (\u2318[)", children: "\u2193" }),
523
+ }, title: t("sendBackward"), children: "\u2193" }),
450
524
  /* @__PURE__ */ jsx("button", { onClick: (e) => {
451
525
  e.stopPropagation();
452
526
  reorderSelection(-1, true);
453
- }, title: "Send to back", children: "\u2913" }),
527
+ }, title: t("sendToBack"), children: "\u2913" }),
454
528
  /* @__PURE__ */ jsx("button", { className: "cv-free__ctl-del", onClick: (e) => {
455
529
  e.stopPropagation();
456
530
  deleteSelection();
457
- }, title: "Delete", children: "\xD7" })
531
+ }, title: t("delete"), children: "\xD7" })
458
532
  ] })
459
533
  ] })
460
534
  ]
@@ -472,16 +546,16 @@ function FreeSlide({ elements, onChange, padding }) {
472
546
  onPointerDown: (e) => e.stopPropagation(),
473
547
  onClick: (e) => e.stopPropagation(),
474
548
  children: [
475
- /* @__PURE__ */ jsx("button", { onClick: groupSelection, title: "Group (\u2318G)", children: "\u229E" }),
476
- /* @__PURE__ */ jsx("button", { onClick: ungroupSelection, disabled: !selEls.some((el) => el.group), title: "Ungroup (\u2318\u21E7G)", children: "\u229F" }),
477
- /* @__PURE__ */ jsx("button", { onClick: () => alignSelection("left"), title: "Align left edges", children: "\u21E4" }),
478
- /* @__PURE__ */ jsx("button", { onClick: () => alignSelection("center"), title: "Align horizontal centers", children: "\u2194" }),
479
- /* @__PURE__ */ jsx("button", { onClick: () => alignSelection("right"), title: "Align right edges", children: "\u21E5" }),
480
- /* @__PURE__ */ jsx("button", { onClick: () => alignSelection("top"), title: "Align top edges", children: "\u2912" }),
481
- /* @__PURE__ */ jsx("button", { onClick: () => alignSelection("middle"), title: "Align vertical centers", children: "\u2195" }),
482
- /* @__PURE__ */ jsx("button", { onClick: () => alignSelection("bottom"), title: "Align bottom edges", children: "\u2913" }),
483
- /* @__PURE__ */ jsx("button", { onClick: duplicateSelection, title: "Duplicate (\u2318D)", children: "\u29C9" }),
484
- /* @__PURE__ */ jsx("button", { className: "cv-free__multibar-del", onClick: deleteSelection, title: "Delete selection", children: "\xD7" })
549
+ /* @__PURE__ */ jsx("button", { onClick: groupSelection, title: t("groupShort"), children: "\u229E" }),
550
+ /* @__PURE__ */ jsx("button", { onClick: ungroupSelection, disabled: !selEls.some((el) => el.group), title: t("ungroupShort"), children: "\u229F" }),
551
+ /* @__PURE__ */ jsx("button", { onClick: () => alignSelection("left"), title: t("alignLeftEdges"), children: "\u21E4" }),
552
+ /* @__PURE__ */ jsx("button", { onClick: () => alignSelection("center"), title: t("alignHCenters"), children: "\u2194" }),
553
+ /* @__PURE__ */ jsx("button", { onClick: () => alignSelection("right"), title: t("alignRightEdges"), children: "\u21E5" }),
554
+ /* @__PURE__ */ jsx("button", { onClick: () => alignSelection("top"), title: t("alignTopEdges"), children: "\u2912" }),
555
+ /* @__PURE__ */ jsx("button", { onClick: () => alignSelection("middle"), title: t("alignVCenters"), children: "\u2195" }),
556
+ /* @__PURE__ */ jsx("button", { onClick: () => alignSelection("bottom"), title: t("alignBottomEdges"), children: "\u2913" }),
557
+ /* @__PURE__ */ jsx("button", { onClick: duplicateSelection, title: t("duplicateShort"), children: "\u29C9" }),
558
+ /* @__PURE__ */ jsx("button", { className: "cv-free__multibar-del", onClick: deleteSelection, title: t("deleteSelection"), children: "\xD7" })
485
559
  ]
486
560
  }
487
561
  )
@@ -567,10 +641,12 @@ var LAYOUTS = {
567
641
  var elementSeq = 0;
568
642
  var newElementId = () => `el_${Date.now().toString(36)}_${elementSeq++}`;
569
643
  function SlidesRenderer({ artifact }) {
644
+ const t = useT();
570
645
  const slides = artifact.data.slides ?? [];
571
646
  const patch = useArtifactPatch(artifact.id);
572
647
  const [index, setIndex] = useState(0);
573
648
  const [dragIndex, setDragIndex] = useState(null);
649
+ const [dropIndex, setDropIndex] = useState(null);
574
650
  const [presenting, setPresenting] = useState(false);
575
651
  const imgRef = useRef(null);
576
652
  const bgRef = useRef(null);
@@ -702,15 +778,23 @@ function SlidesRenderer({ artifact }) {
702
778
  slides.map((s, i) => /* @__PURE__ */ jsx(
703
779
  "div",
704
780
  {
705
- className: `cv-deck__thumb-wrap ${i === at ? "is-active" : ""} ${dragIndex === i ? "is-dragging" : ""}`,
781
+ className: `cv-deck__thumb-wrap ${i === at ? "is-active" : ""} ${dragIndex === i ? "is-dragging" : ""} ${dragIndex !== null && dropIndex === i && i !== dragIndex ? i < dragIndex ? "cv-deck__thumb-wrap--drop-before" : "cv-deck__thumb-wrap--drop-after" : ""}`,
706
782
  draggable: true,
707
783
  onDragStart: () => setDragIndex(i),
708
- onDragOver: (e) => e.preventDefault(),
784
+ onDragOver: (e) => {
785
+ e.preventDefault();
786
+ if (dragIndex !== null && i !== dragIndex) setDropIndex(i);
787
+ },
788
+ onDragLeave: () => setDropIndex((cur) => cur === i ? null : cur),
709
789
  onDrop: () => {
710
790
  if (dragIndex !== null) reorder(dragIndex, i);
711
791
  setDragIndex(null);
792
+ setDropIndex(null);
793
+ },
794
+ onDragEnd: () => {
795
+ setDragIndex(null);
796
+ setDropIndex(null);
712
797
  },
713
- onDragEnd: () => setDragIndex(null),
714
798
  children: /* @__PURE__ */ jsxs("button", { className: "cv-deck__thumb", onClick: () => setIndex(i), children: [
715
799
  /* @__PURE__ */ jsx("span", { className: "cv-deck__thumb-n", children: i + 1 }),
716
800
  /* @__PURE__ */ jsx(
@@ -725,11 +809,11 @@ function SlidesRenderer({ artifact }) {
725
809
  (el) => el.type === "text" ? /* @__PURE__ */ jsx(
726
810
  "span",
727
811
  {
728
- style: { position: "absolute", left: `${el.x}%`, top: `${el.y}%`, width: `${el.w}%`, fontSize: (el.fontSize ?? 24) * 0.12, fontWeight: el.bold ? 700 : 400, color: el.color ?? s.textColor, overflow: "hidden", whiteSpace: "pre-wrap" },
812
+ style: { position: "absolute", left: `${el.x}%`, top: `${el.y}%`, width: `${el.w}%`, fontSize: `${((el.fontSize ?? 24) / 12.8).toFixed(3)}cqw`, fontWeight: el.bold ? 700 : 400, color: el.color ?? s.textColor, overflow: "visible", whiteSpace: "pre-wrap", ...rotateStyle(el) },
729
813
  children: el.text
730
814
  },
731
815
  el.id
732
- ) : el.type === "shape" ? /* @__PURE__ */ jsx("div", { style: { position: "absolute", left: `${el.x}%`, top: `${el.y}%`, width: `${el.w}%`, height: `${el.h}%`, color: s.textColor, ...shapeStyle(el) } }, el.id) : /* @__PURE__ */ jsx("img", { src: el.src, alt: "", style: { position: "absolute", left: `${el.x}%`, top: `${el.y}%`, width: `${el.w}%`, height: `${el.h}%`, objectFit: "contain" } }, el.id)
816
+ ) : el.type === "shape" ? /* @__PURE__ */ jsx("div", { style: { position: "absolute", left: `${el.x}%`, top: `${el.y}%`, width: `${el.w}%`, height: `${el.h}%`, color: s.textColor, ...rotateStyle(el), ...shapeStyle(el) } }, el.id) : /* @__PURE__ */ jsx("img", { src: el.src, alt: "", style: { position: "absolute", left: `${el.x}%`, top: `${el.y}%`, width: `${el.w}%`, height: `${el.h}%`, objectFit: el.fit ?? "contain", ...el.radius ? { borderRadius: el.radius } : {}, ...rotateStyle(el) } }, el.id)
733
817
  ) })
734
818
  }
735
819
  )
@@ -741,8 +825,8 @@ function SlidesRenderer({ artifact }) {
741
825
  ] }),
742
826
  /* @__PURE__ */ jsxs("div", { className: "cv-deck__main", children: [
743
827
  /* @__PURE__ */ jsxs("div", { className: "cv-deck__toolbar cv-chrome", children: [
744
- /* @__PURE__ */ jsx("button", { onClick: addTextEl, title: "Add text box", children: "+ Text" }),
745
- /* @__PURE__ */ jsx("button", { onClick: () => imgRef.current?.click(), title: "Add image", children: "+ Image" }),
828
+ /* @__PURE__ */ jsx("button", { onClick: addTextEl, title: t("addTextBox"), children: t("addText") }),
829
+ /* @__PURE__ */ jsx("button", { onClick: () => imgRef.current?.click(), title: t("addImageTip"), children: t("addImage") }),
746
830
  /* @__PURE__ */ jsx("input", { ref: imgRef, type: "file", accept: "image/*", hidden: true, onChange: (e) => addImageEl(e.target.files?.[0]) }),
747
831
  /* @__PURE__ */ jsx("input", { ref: bgRef, type: "file", accept: "image/*", hidden: true, onChange: (e) => setSlideBgImage(e.target.files?.[0]) }),
748
832
  /* @__PURE__ */ jsxs(
@@ -750,13 +834,13 @@ function SlidesRenderer({ artifact }) {
750
834
  {
751
835
  className: "cv-deck__theme",
752
836
  value: "",
753
- title: "Add a shape",
837
+ title: t("addShapeTip"),
754
838
  onChange: (e) => {
755
839
  if (e.target.value) addShapeEl(e.target.value);
756
840
  e.currentTarget.value = "";
757
841
  },
758
842
  children: [
759
- /* @__PURE__ */ jsx("option", { value: "", children: "+ Shape" }),
843
+ /* @__PURE__ */ jsx("option", { value: "", children: t("addShape") }),
760
844
  SHAPES.map((s) => /* @__PURE__ */ jsx("option", { value: s.id, children: s.label }, s.id))
761
845
  ]
762
846
  }
@@ -766,13 +850,13 @@ function SlidesRenderer({ artifact }) {
766
850
  {
767
851
  className: "cv-deck__theme",
768
852
  value: "",
769
- title: "Apply a layout",
853
+ title: t("applyLayout"),
770
854
  onChange: (e) => {
771
855
  if (e.target.value) applyLayout(e.target.value);
772
856
  e.currentTarget.value = "";
773
857
  },
774
858
  children: [
775
- /* @__PURE__ */ jsx("option", { value: "", children: "Layout\u2026" }),
859
+ /* @__PURE__ */ jsx("option", { value: "", children: t("layoutMenu") }),
776
860
  Object.entries(LAYOUTS).map(([k, v]) => /* @__PURE__ */ jsx("option", { value: k, children: v.label }, k))
777
861
  ]
778
862
  }
@@ -782,13 +866,13 @@ function SlidesRenderer({ artifact }) {
782
866
  {
783
867
  className: "cv-deck__theme",
784
868
  value: "",
785
- title: "Auto layout \u2014 align & distribute elements",
869
+ title: t("autoLayoutTip"),
786
870
  onChange: (e) => {
787
871
  if (e.target.value) arrange(e.target.value);
788
872
  e.currentTarget.value = "";
789
873
  },
790
874
  children: [
791
- /* @__PURE__ */ jsx("option", { value: "", children: "\u2922 Arrange\u2026" }),
875
+ /* @__PURE__ */ jsx("option", { value: "", children: t("arrangeMenu") }),
792
876
  /* @__PURE__ */ jsxs("optgroup", { label: "Auto layout", children: [
793
877
  /* @__PURE__ */ jsx("option", { value: "stack-v", children: "Stack vertical" }),
794
878
  /* @__PURE__ */ jsx("option", { value: "stack-h", children: "Stack horizontal" })
@@ -813,30 +897,33 @@ function SlidesRenderer({ artifact }) {
813
897
  {
814
898
  className: "cv-deck__theme",
815
899
  value: "",
816
- title: "Theme",
900
+ title: t("theme"),
817
901
  onChange: (e) => {
818
- const t = THEMES.find((x) => x.id === e.target.value);
819
- if (t) update({ background: t.bg, textColor: t.text, accent: t.accent, fontFamily: t.font });
902
+ const t2 = THEMES.find((x) => x.id === e.target.value);
903
+ if (t2) update({ background: t2.bg, textColor: t2.text, accent: t2.accent, fontFamily: t2.font });
820
904
  e.currentTarget.value = "";
821
905
  },
822
906
  children: [
823
- /* @__PURE__ */ jsx("option", { value: "", children: "Theme\u2026" }),
824
- THEMES.map((t) => /* @__PURE__ */ jsx("option", { value: t.id, children: t.label }, t.id))
907
+ /* @__PURE__ */ jsx("option", { value: "", children: t("themeMenu") }),
908
+ THEMES.map((t2) => /* @__PURE__ */ jsx("option", { value: t2.id, children: t2.label }, t2.id))
825
909
  ]
826
910
  }
827
911
  ),
828
- /* @__PURE__ */ jsx("label", { className: "cv-deck__bg", title: "Background color", children: /* @__PURE__ */ jsx("input", { type: "color", value: /^#/.test(slide.background ?? "") ? slide.background : "#ffffff", onChange: (e) => update({ background: e.target.value }) }) }),
829
- /* @__PURE__ */ jsx("button", { onClick: () => bgRef.current?.click(), title: "Background image", children: "\u{1F5BC} BG" }),
830
- /* @__PURE__ */ jsxs("label", { className: "cv-deck__pad", title: "Content padding (% of slide)", children: [
831
- "Pad",
912
+ /* @__PURE__ */ jsx("label", { className: "cv-deck__bg", title: t("bgColor"), children: /* @__PURE__ */ jsx("input", { type: "color", value: /^#/.test(slide.background ?? "") ? slide.background : "#ffffff", onChange: (e) => update({ background: e.target.value }) }) }),
913
+ /* @__PURE__ */ jsx("button", { onClick: () => bgRef.current?.click(), title: t("bgImageTip"), children: t("bgImage") }),
914
+ /* @__PURE__ */ jsxs("label", { className: "cv-deck__pad", title: t("padTip"), children: [
915
+ t("padding"),
832
916
  /* @__PURE__ */ jsx("input", { type: "number", min: 0, max: 20, value: slide.padding ?? 0, onChange: (e) => update({ padding: Number(e.target.value) || void 0 }) })
833
917
  ] }),
834
918
  /* @__PURE__ */ jsx("span", { className: "cv-deck__spacer" }),
835
- /* @__PURE__ */ jsx("button", { className: "cv-deck__present", onClick: () => setPresenting(true), title: "Present (full screen)", children: "\u25B6 Present" }),
836
- /* @__PURE__ */ jsx("button", { onClick: () => moveSlide(-1), title: "Move up", disabled: at === 0, children: "\u25B2" }),
837
- /* @__PURE__ */ jsx("button", { onClick: () => moveSlide(1), title: "Move down", disabled: at === slides.length - 1, children: "\u25BC" }),
838
- /* @__PURE__ */ jsx("button", { onClick: duplicateSlide, title: "Duplicate slide", children: "\u29C9" }),
839
- /* @__PURE__ */ jsx("button", { onClick: deleteSlide, title: "Delete slide", disabled: slides.length === 1, children: "\u{1F5D1}" })
919
+ /* @__PURE__ */ jsxs("button", { className: "cv-deck__present", onClick: () => setPresenting(true), title: t("presentTip"), children: [
920
+ "\u25B6 ",
921
+ t("present")
922
+ ] }),
923
+ /* @__PURE__ */ jsx("button", { onClick: () => moveSlide(-1), title: t("moveUp"), disabled: at === 0, children: "\u25B2" }),
924
+ /* @__PURE__ */ jsx("button", { onClick: () => moveSlide(1), title: t("moveDown"), disabled: at === slides.length - 1, children: "\u25BC" }),
925
+ /* @__PURE__ */ jsx("button", { onClick: duplicateSlide, title: t("duplicateSlide"), children: "\u29C9" }),
926
+ /* @__PURE__ */ jsx("button", { onClick: deleteSlide, title: t("deleteSlide"), disabled: slides.length === 1, children: "\u{1F5D1}" })
840
927
  ] }),
841
928
  /* @__PURE__ */ jsx("div", { className: "cv-slide cv-slide--blank", style: slideStyle, children: /* @__PURE__ */ jsx(FreeSlide, { elements: resolveElements(slide), onChange: (elements) => update({ elements }), padding: slide.padding }) }),
842
929
  /* @__PURE__ */ jsxs("div", { className: "cv-deck__nav cv-chrome", children: [
@@ -853,14 +940,14 @@ function SlidesRenderer({ artifact }) {
853
940
  {
854
941
  className: "cv-deck__notes cv-chrome",
855
942
  value: slide.notes ?? "",
856
- placeholder: "Speaker notes\u2026",
943
+ placeholder: t("speakerNotes"),
857
944
  onChange: (e) => update({ notes: e.target.value })
858
945
  }
859
946
  )
860
947
  ] }),
861
948
  presenting && /* @__PURE__ */ jsxs("div", { className: "cv-present", onClick: () => setIndex(Math.min(at + 1, slides.length - 1)), children: [
862
949
  /* @__PURE__ */ jsx("div", { className: "cv-present__slide cv-present__fade cv-slide cv-slide--blank", style: slideStyle, children: /* @__PURE__ */ jsx("div", { className: "cv-free", style: slide.padding ? { inset: `${slide.padding}%` } : void 0, children: resolveElements(slide).map(
863
- (el) => el.type === "text" ? /* @__PURE__ */ jsx("div", { className: "cv-free__el", style: { left: `${el.x}%`, top: `${el.y}%`, width: `${el.w}%`, height: `${el.h}%` }, children: /* @__PURE__ */ jsx("div", { className: "cv-free__text", style: { fontSize: el.fontSize ?? 24, fontWeight: el.bold ? 700 : 400, color: el.color, textAlign: el.align ?? "left", whiteSpace: "pre-wrap" }, children: el.text }) }, el.id) : el.type === "shape" ? /* @__PURE__ */ jsx("div", { className: "cv-free__el", style: { left: `${el.x}%`, top: `${el.y}%`, width: `${el.w}%`, height: `${el.h}%`, color: slide.textColor }, children: /* @__PURE__ */ jsx("div", { style: shapeStyle(el) }) }, el.id) : /* @__PURE__ */ jsx("div", { className: "cv-free__el", style: { left: `${el.x}%`, top: `${el.y}%`, width: `${el.w}%`, height: `${el.h}%` }, children: /* @__PURE__ */ jsx("img", { className: "cv-free__img", src: el.src, alt: "" }) }, el.id)
950
+ (el) => el.type === "text" ? /* @__PURE__ */ jsx("div", { className: "cv-free__el", style: { left: `${el.x}%`, top: `${el.y}%`, width: `${el.w}%`, height: `${el.h}%`, ...rotateStyle(el) }, children: /* @__PURE__ */ jsx("div", { className: "cv-free__text", style: { fontSize: el.fontSize ?? 24, fontWeight: el.bold ? 700 : 400, color: el.color, textAlign: el.align ?? "left", whiteSpace: "pre-wrap" }, children: el.text }) }, el.id) : el.type === "shape" ? /* @__PURE__ */ jsx("div", { className: "cv-free__el", style: { left: `${el.x}%`, top: `${el.y}%`, width: `${el.w}%`, height: `${el.h}%`, color: slide.textColor, ...rotateStyle(el) }, children: /* @__PURE__ */ jsx("div", { style: shapeStyle(el) }) }, el.id) : /* @__PURE__ */ jsx("div", { className: "cv-free__el", style: { left: `${el.x}%`, top: `${el.y}%`, width: `${el.w}%`, height: `${el.h}%`, ...rotateStyle(el) }, children: /* @__PURE__ */ jsx("img", { className: "cv-free__img", src: el.src, alt: "", style: { objectFit: el.fit ?? "contain", ...el.radius ? { borderRadius: el.radius } : {} } }) }, el.id)
864
951
  ) }) }, at),
865
952
  slide.notes ? /* @__PURE__ */ jsx("div", { className: "cv-present__notes", onClick: (e) => e.stopPropagation(), children: slide.notes }) : null,
866
953
  /* @__PURE__ */ jsx("div", { className: "cv-present__progress", children: /* @__PURE__ */ jsx("span", { className: "cv-present__progress-fill", style: { width: `${(at + 1) / slides.length * 100}%` } }) }),
@@ -869,7 +956,7 @@ function SlidesRenderer({ artifact }) {
869
956
  " / ",
870
957
  slides.length
871
958
  ] }),
872
- /* @__PURE__ */ jsx("div", { className: "cv-present__hint", children: "\u2190 \u2192 to navigate \xB7 Esc to exit" })
959
+ /* @__PURE__ */ jsx("div", { className: "cv-present__hint", children: t("presentHint") })
873
960
  ] })
874
961
  ] });
875
962
  }