@twick/studio 0.15.24 → 0.15.25

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.js CHANGED
@@ -4671,6 +4671,16 @@ function EffectStylePanelContainer({
4671
4671
  }
4672
4672
  );
4673
4673
  }
4674
+ const formatTime = (seconds) => {
4675
+ if (!Number.isFinite(seconds) || seconds < 0) return "0:00.00";
4676
+ const totalMs = Math.round(seconds * 1e3);
4677
+ const totalSeconds = Math.floor(totalMs / 1e3);
4678
+ const minutes = Math.floor(totalSeconds / 60);
4679
+ const secs = totalSeconds % 60;
4680
+ const ms = Math.floor(totalMs % 1e3 / 10);
4681
+ const pad = (n, l = 2) => String(n).padStart(l, "0");
4682
+ return `${minutes}:${pad(secs)}.${pad(ms)}`;
4683
+ };
4674
4684
  function CaptionsPanel({
4675
4685
  captions,
4676
4686
  addCaption,
@@ -4678,54 +4688,102 @@ function CaptionsPanel({
4678
4688
  deleteCaption,
4679
4689
  updateCaption
4680
4690
  }) {
4681
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "panel-container", children: [
4682
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "panel-title", children: "Captions" }),
4683
- captions.map((caption, i) => /* @__PURE__ */ jsxRuntime.jsxs(
4684
- "div",
4685
- {
4686
- className: "panel-section gap-2",
4687
- children: [
4688
- /* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsx(
4689
- "input",
4690
- {
4691
- type: "text",
4692
- placeholder: "Enter caption text",
4693
- value: caption.t,
4694
- onChange: (e) => updateCaption(i, { ...caption, t: e.target.value }),
4695
- className: "input-dark"
4696
- }
4697
- ) }),
4698
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-container justify-between", children: [
4699
- /* @__PURE__ */ jsxRuntime.jsx(
4700
- "button",
4701
- {
4702
- onClick: () => splitCaption(i),
4703
- className: "btn-ghost",
4704
- title: "Split caption",
4705
- children: /* @__PURE__ */ jsxRuntime.jsx(Scissors, { className: "icon-sm" })
4706
- }
4707
- ),
4708
- /* @__PURE__ */ jsxRuntime.jsx(
4709
- "button",
4710
- {
4711
- onClick: () => deleteCaption(i),
4712
- className: "btn-ghost",
4713
- title: "Delete caption",
4714
- children: /* @__PURE__ */ jsxRuntime.jsx(Trash2, { className: "icon-sm", color: "var(--color-red-500)" })
4715
- }
4716
- )
4717
- ] })
4718
- ]
4719
- },
4720
- i
4721
- )),
4722
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "panel-section", children: /* @__PURE__ */ jsxRuntime.jsx("button", { onClick: addCaption, className: "btn-primary w-full", title: "Add caption", children: "Add" }) })
4691
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "panel-container captions-panel", children: [
4692
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "captions-panel-header", children: [
4693
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "panel-title", children: "Captions" }),
4694
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "captions-panel-header-meta", children: [
4695
+ captions.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "captions-panel-count", children: "No captions yet" }) : null,
4696
+ /* @__PURE__ */ jsxRuntime.jsx(
4697
+ "button",
4698
+ {
4699
+ onClick: addCaption,
4700
+ className: "btn-primary captions-panel-add-button",
4701
+ title: "Add caption",
4702
+ children: "Add caption"
4703
+ }
4704
+ )
4705
+ ] })
4706
+ ] }),
4707
+ captions.length === 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "panel-section captions-panel-empty", children: [
4708
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "captions-panel-empty-title", children: "Start your first caption" }),
4709
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "captions-panel-empty-subtitle", children: "Use the button above to add the first caption block for the active track." }),
4710
+ /* @__PURE__ */ jsxRuntime.jsx(
4711
+ "button",
4712
+ {
4713
+ onClick: addCaption,
4714
+ className: "btn-primary captions-panel-empty-button",
4715
+ title: "Add first caption",
4716
+ children: "Add caption"
4717
+ }
4718
+ )
4719
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "panel-section captions-panel-list", children: captions.map((caption, i) => {
4720
+ return /* @__PURE__ */ jsxRuntime.jsxs(
4721
+ "div",
4722
+ {
4723
+ className: "captions-panel-item",
4724
+ children: [
4725
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "captions-panel-item-header", children: [
4726
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "captions-panel-time captions-panel-time-start", children: formatTime(caption.s) }),
4727
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "captions-panel-time captions-panel-time-end", children: formatTime(caption.e) })
4728
+ ] }),
4729
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "captions-panel-item-body", children: [
4730
+ /* @__PURE__ */ jsxRuntime.jsx(
4731
+ "textarea",
4732
+ {
4733
+ placeholder: "Enter caption text",
4734
+ value: caption.t,
4735
+ onChange: (e) => updateCaption(i, { ...caption, t: e.target.value }),
4736
+ className: "input-dark captions-panel-textarea"
4737
+ }
4738
+ ),
4739
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "captions-panel-actions", children: [
4740
+ /* @__PURE__ */ jsxRuntime.jsx(
4741
+ "button",
4742
+ {
4743
+ onClick: () => splitCaption(i),
4744
+ className: "btn-ghost captions-panel-action-button",
4745
+ title: "Split caption at midpoint",
4746
+ children: /* @__PURE__ */ jsxRuntime.jsx(Scissors, { className: "icon-sm" })
4747
+ }
4748
+ ),
4749
+ /* @__PURE__ */ jsxRuntime.jsx(
4750
+ "button",
4751
+ {
4752
+ onClick: () => deleteCaption(i),
4753
+ className: "btn-ghost captions-panel-action-button",
4754
+ title: "Delete caption",
4755
+ children: /* @__PURE__ */ jsxRuntime.jsx(
4756
+ Trash2,
4757
+ {
4758
+ className: "icon-sm",
4759
+ color: "var(--color-red-500)"
4760
+ }
4761
+ )
4762
+ }
4763
+ )
4764
+ ] })
4765
+ ] })
4766
+ ]
4767
+ },
4768
+ i
4769
+ );
4770
+ }) })
4723
4771
  ] });
4724
4772
  }
4773
+ const HIGHLIGHT_BG_FONT_SIZE = 46;
4774
+ const WORD_BY_WORD_FONT_SIZE = 46;
4775
+ const WORD_BY_WORD_WITH_BG_FONT_SIZE = 46;
4776
+ const OUTLINE_ONLY_FONT_SIZE = 42;
4777
+ const SOFT_BOX_FONT_SIZE = 40;
4778
+ const HIGHLIGHT_BG_GEOMETRY = timeline.computeCaptionGeometry(HIGHLIGHT_BG_FONT_SIZE, timeline.CAPTION_STYLE.WORD_BG_HIGHLIGHT);
4779
+ const WORD_BY_WORD_GEOMETRY = timeline.computeCaptionGeometry(WORD_BY_WORD_FONT_SIZE, timeline.CAPTION_STYLE.WORD_BY_WORD);
4780
+ const WORD_BY_WORD_WITH_BG_GEOMETRY = timeline.computeCaptionGeometry(WORD_BY_WORD_WITH_BG_FONT_SIZE, timeline.CAPTION_STYLE.WORD_BY_WORD_WITH_BG);
4781
+ const OUTLINE_ONLY_GEOMETRY = timeline.computeCaptionGeometry(OUTLINE_ONLY_FONT_SIZE, timeline.CAPTION_STYLE.OUTLINE_ONLY);
4782
+ const SOFT_BOX_GEOMETRY = timeline.computeCaptionGeometry(SOFT_BOX_FONT_SIZE, timeline.CAPTION_STYLE.SOFT_BOX);
4725
4783
  const CAPTION_PROPS = {
4726
4784
  [timeline.CAPTION_STYLE.WORD_BG_HIGHLIGHT]: {
4727
4785
  font: {
4728
- size: 46,
4786
+ size: HIGHLIGHT_BG_FONT_SIZE,
4729
4787
  weight: 700,
4730
4788
  family: "Bangers"
4731
4789
  },
@@ -4734,15 +4792,16 @@ const CAPTION_PROPS = {
4734
4792
  highlight: "#ff4081",
4735
4793
  bgColor: "#444444"
4736
4794
  },
4737
- lineWidth: 0.35,
4795
+ lineWidth: HIGHLIGHT_BG_GEOMETRY.lineWidth,
4796
+ rectProps: HIGHLIGHT_BG_GEOMETRY.rectProps,
4738
4797
  stroke: "#000000",
4739
4798
  fontWeight: 700,
4740
- shadowOffset: [-3, 3],
4799
+ shadowOffset: [-1, 1],
4741
4800
  shadowColor: "#000000"
4742
4801
  },
4743
4802
  [timeline.CAPTION_STYLE.WORD_BY_WORD]: {
4744
4803
  font: {
4745
- size: 46,
4804
+ size: WORD_BY_WORD_FONT_SIZE,
4746
4805
  weight: 700,
4747
4806
  family: "Bangers"
4748
4807
  },
@@ -4751,15 +4810,16 @@ const CAPTION_PROPS = {
4751
4810
  highlight: "#ff4081",
4752
4811
  bgColor: "#444444"
4753
4812
  },
4754
- lineWidth: 0.35,
4813
+ lineWidth: WORD_BY_WORD_GEOMETRY.lineWidth,
4814
+ rectProps: WORD_BY_WORD_GEOMETRY.rectProps,
4755
4815
  stroke: "#000000",
4756
- shadowOffset: [-2, 2],
4816
+ shadowOffset: [-1, 1],
4757
4817
  shadowColor: "#000000",
4758
4818
  shadowBlur: 5
4759
4819
  },
4760
4820
  [timeline.CAPTION_STYLE.WORD_BY_WORD_WITH_BG]: {
4761
4821
  font: {
4762
- size: 46,
4822
+ size: WORD_BY_WORD_WITH_BG_FONT_SIZE,
4763
4823
  weight: 700,
4764
4824
  family: "Bangers"
4765
4825
  },
@@ -4768,14 +4828,15 @@ const CAPTION_PROPS = {
4768
4828
  highlight: "#ff4081",
4769
4829
  bgColor: "#444444"
4770
4830
  },
4771
- lineWidth: 0.35,
4772
- shadowOffset: [-2, 2],
4831
+ lineWidth: WORD_BY_WORD_WITH_BG_GEOMETRY.lineWidth,
4832
+ rectProps: WORD_BY_WORD_WITH_BG_GEOMETRY.rectProps,
4833
+ shadowOffset: [-1, 1],
4773
4834
  shadowColor: "#000000",
4774
4835
  shadowBlur: 5
4775
4836
  },
4776
4837
  [timeline.CAPTION_STYLE.OUTLINE_ONLY]: {
4777
4838
  font: {
4778
- size: 42,
4839
+ size: OUTLINE_ONLY_FONT_SIZE,
4779
4840
  weight: 600,
4780
4841
  family: "Arial"
4781
4842
  },
@@ -4784,7 +4845,8 @@ const CAPTION_PROPS = {
4784
4845
  highlight: "#ff4081",
4785
4846
  bgColor: "#000000"
4786
4847
  },
4787
- lineWidth: 0.5,
4848
+ lineWidth: OUTLINE_ONLY_GEOMETRY.lineWidth,
4849
+ rectProps: OUTLINE_ONLY_GEOMETRY.rectProps,
4788
4850
  stroke: "#000000",
4789
4851
  fontWeight: 600,
4790
4852
  shadowOffset: [0, 0],
@@ -4793,7 +4855,7 @@ const CAPTION_PROPS = {
4793
4855
  },
4794
4856
  [timeline.CAPTION_STYLE.SOFT_BOX]: {
4795
4857
  font: {
4796
- size: 40,
4858
+ size: SOFT_BOX_FONT_SIZE,
4797
4859
  weight: 600,
4798
4860
  family: "Montserrat"
4799
4861
  },
@@ -4802,7 +4864,8 @@ const CAPTION_PROPS = {
4802
4864
  highlight: "#ff4081",
4803
4865
  bgColor: "#333333"
4804
4866
  },
4805
- lineWidth: 0.2,
4867
+ lineWidth: SOFT_BOX_GEOMETRY.lineWidth,
4868
+ rectProps: SOFT_BOX_GEOMETRY.rectProps,
4806
4869
  stroke: "#000000",
4807
4870
  fontWeight: 600,
4808
4871
  shadowOffset: [-1, 1],
@@ -6321,6 +6384,116 @@ const CAPTION_COLOR = {
6321
6384
  bgColor: "#8C52FF",
6322
6385
  outlineColor: "#000000"
6323
6386
  };
6387
+ const CAPTION_STYLE_COLOR_META = {
6388
+ // Word background highlight - white text on colored pill
6389
+ highlight_bg: {
6390
+ // Text color, and background pill color used in animation.
6391
+ usedColors: ["text", "bgColor"],
6392
+ labels: {
6393
+ text: "Text Color",
6394
+ bgColor: "Highlight Background"
6395
+ }
6396
+ },
6397
+ // Simple word-by-word – text only
6398
+ word_by_word: {
6399
+ // Visualizer uses text as fill + outlineColor for stroke, and highlight for active word.
6400
+ usedColors: ["text", "highlight", "outlineColor"],
6401
+ labels: {
6402
+ text: "Text Color",
6403
+ highlight: "Highlight Color",
6404
+ outlineColor: "Outline Color"
6405
+ }
6406
+ },
6407
+ // Word-by-word with a phrase bar background
6408
+ word_by_word_with_bg: {
6409
+ // Text color (fill), highlight for active word, outlineColor (stroke), bgColor used by phrase rect.
6410
+ usedColors: ["text", "highlight", "bgColor", "outlineColor"],
6411
+ labels: {
6412
+ text: "Text Color",
6413
+ bgColor: "Bar Background",
6414
+ highlight: "Highlight Color",
6415
+ outlineColor: "Outline Color"
6416
+ }
6417
+ },
6418
+ // Classic outlined text
6419
+ outline_only: {
6420
+ // Outline-only style: fill + outline color; highlight not used in animation.
6421
+ usedColors: ["text", "outlineColor"],
6422
+ labels: {
6423
+ text: "Fill Color",
6424
+ outlineColor: "Outline Color"
6425
+ }
6426
+ },
6427
+ // Soft rounded box behind text
6428
+ soft_box: {
6429
+ usedColors: ["text", "bgColor", "highlight", "outlineColor"],
6430
+ labels: {
6431
+ text: "Text Color",
6432
+ highlight: "Highlight Color",
6433
+ bgColor: "Box Background",
6434
+ outlineColor: "Outline Color"
6435
+ }
6436
+ },
6437
+ // Broadcast style lower-third bar
6438
+ lower_third: {
6439
+ // Title text, bar background, highlight color and outline color.
6440
+ usedColors: ["text", "bgColor", "outlineColor"],
6441
+ labels: {
6442
+ text: "Title Text Color",
6443
+ bgColor: "Bar Background",
6444
+ highlight: "Highlight Color",
6445
+ outlineColor: "Outline Color"
6446
+ }
6447
+ },
6448
+ // Typewriter – text only
6449
+ typewriter: {
6450
+ // Text color and outline color (stroke) used by visualizer; highlight not animated.
6451
+ usedColors: ["text", "outlineColor"],
6452
+ labels: {
6453
+ text: "Text Color",
6454
+ outlineColor: "Outline Color"
6455
+ }
6456
+ },
6457
+ // Karaoke – base text plus active word highlight
6458
+ karaoke: {
6459
+ // Base text color, active word highlight color, outline color.
6460
+ usedColors: ["text", "highlight", "outlineColor"],
6461
+ labels: {
6462
+ text: "Text Color",
6463
+ highlight: "Highlight Color",
6464
+ outlineColor: "Outline Color"
6465
+ }
6466
+ },
6467
+ // Karaoke-word – single active word, previous words dimmed
6468
+ "karaoke-word": {
6469
+ // Same color needs as karaoke.
6470
+ usedColors: ["text", "highlight", "outlineColor"],
6471
+ labels: {
6472
+ text: "Text Color",
6473
+ highlight: "Highlight Color",
6474
+ outlineColor: "Outline Color"
6475
+ }
6476
+ },
6477
+ // Pop / scale – text only
6478
+ pop_scale: {
6479
+ // Text color, highlight color for active word, and outline color; no background.
6480
+ usedColors: ["text", "highlight", "outlineColor"],
6481
+ labels: {
6482
+ text: "Text Color",
6483
+ highlight: "Highlight Color",
6484
+ outlineColor: "Outline Color"
6485
+ }
6486
+ }
6487
+ };
6488
+ const DEFAULT_COLOR_META = {
6489
+ usedColors: ["text", "bgColor", "outlineColor"],
6490
+ labels: {
6491
+ text: "Text Color",
6492
+ bgColor: "Background Color",
6493
+ outlineColor: "Outline Color"
6494
+ }
6495
+ };
6496
+ const CAPTION_FONTS = Object.values(VideoEditor.AVAILABLE_TEXT_FONTS);
6324
6497
  function CaptionPropPanel({
6325
6498
  selectedElement,
6326
6499
  updateElement
@@ -6338,24 +6511,42 @@ function CaptionPropPanel({
6338
6511
  bgColor: CAPTION_COLOR.bgColor,
6339
6512
  outlineColor: CAPTION_COLOR.outlineColor
6340
6513
  });
6514
+ const [useHighlight, setUseHighlight] = react.useState(true);
6515
+ const [useOutline, setUseOutline] = react.useState(true);
6341
6516
  const track = selectedElement instanceof timeline.CaptionElement ? editor.getTrackById(selectedElement.getTrackId()) : null;
6342
6517
  const trackProps = (track == null ? void 0 : track.getProps()) ?? {};
6343
6518
  const applyToAll = (trackProps == null ? void 0 : trackProps.applyToAll) ?? false;
6344
6519
  const handleUpdateCaption = (updates) => {
6345
6520
  const captionElement = selectedElement;
6346
6521
  if (!captionElement) return;
6522
+ const nextFontSize = updates.fontSize ?? fontSize;
6523
+ const geometry = timeline.computeCaptionGeometry(nextFontSize, updates.style ?? (capStyle == null ? void 0 : capStyle.value) ?? "");
6524
+ const highlightEnabled = updates.useHighlightOverride ?? useHighlight;
6525
+ const outlineEnabled = updates.useOutlineOverride ?? useOutline;
6526
+ const rawNextColors = updates.colors ?? colors;
6527
+ let effectiveColors = { ...rawNextColors };
6528
+ if (!highlightEnabled) {
6529
+ const { highlight, ...rest } = effectiveColors;
6530
+ effectiveColors = rest;
6531
+ }
6532
+ if (!outlineEnabled) {
6533
+ const { outlineColor, ...rest } = effectiveColors;
6534
+ effectiveColors = rest;
6535
+ }
6347
6536
  if (applyToAll && track) {
6348
6537
  const nextFont = {
6349
- size: updates.fontSize ?? fontSize,
6538
+ size: nextFontSize,
6350
6539
  family: updates.fontFamily ?? fontFamily
6351
6540
  };
6352
- const nextColors = updates.colors ?? colors;
6541
+ const nextColors = effectiveColors;
6353
6542
  const nextCapStyle = updates.style ?? (capStyle == null ? void 0 : capStyle.value);
6354
6543
  track.setProps({
6355
6544
  ...trackProps,
6356
6545
  capStyle: nextCapStyle,
6357
6546
  font: { ...(trackProps == null ? void 0 : trackProps.font) ?? {}, ...nextFont },
6358
- colors: nextColors
6547
+ colors: nextColors,
6548
+ lineWidth: geometry.lineWidth,
6549
+ rectProps: geometry.rectProps
6359
6550
  });
6360
6551
  editor.refresh();
6361
6552
  } else {
@@ -6364,10 +6555,11 @@ function CaptionPropPanel({
6364
6555
  ...elementProps,
6365
6556
  capStyle: updates.style ?? (capStyle == null ? void 0 : capStyle.value),
6366
6557
  font: {
6367
- size: updates.fontSize ?? fontSize,
6558
+ size: nextFontSize,
6368
6559
  family: updates.fontFamily ?? fontFamily
6369
6560
  },
6370
- colors: updates.colors ?? colors
6561
+ colors: effectiveColors,
6562
+ lineWidth: geometry.lineWidth
6371
6563
  });
6372
6564
  updateElement == null ? void 0 : updateElement(captionElement);
6373
6565
  }
@@ -6393,11 +6585,62 @@ function CaptionPropPanel({
6393
6585
  bgColor: (c == null ? void 0 : c.bgColor) ?? CAPTION_COLOR.bgColor,
6394
6586
  outlineColor: (c == null ? void 0 : c.outlineColor) ?? CAPTION_COLOR.outlineColor
6395
6587
  });
6588
+ setUseHighlight((c == null ? void 0 : c.highlight) != null);
6589
+ setUseOutline((c == null ? void 0 : c.outlineColor) != null);
6396
6590
  }
6397
6591
  }, [selectedElement, applyToAll, changeLog]);
6398
6592
  if (!(selectedElement instanceof timeline.CaptionElement)) {
6399
6593
  return null;
6400
6594
  }
6595
+ const currentStyleKey = capStyle == null ? void 0 : capStyle.value;
6596
+ const currentColorMeta = currentStyleKey && CAPTION_STYLE_COLOR_META[currentStyleKey] || DEFAULT_COLOR_META;
6597
+ const defaultColorLabels = {
6598
+ text: "Text Color",
6599
+ bgColor: "Background Color",
6600
+ highlight: "Highlight Color",
6601
+ outlineColor: "Outline Color"
6602
+ };
6603
+ const renderColorControl = (key) => {
6604
+ if (key === "highlight" && !useHighlight) {
6605
+ return null;
6606
+ }
6607
+ if (key === "outlineColor" && !useOutline) {
6608
+ return null;
6609
+ }
6610
+ const label = currentColorMeta.labels[key] ?? defaultColorLabels[key];
6611
+ const value = colors[key];
6612
+ const handleChange = (next) => {
6613
+ const nextColors = { ...colors, [key]: next };
6614
+ setColors(nextColors);
6615
+ handleUpdateCaption({ colors: nextColors });
6616
+ };
6617
+ if (value == null) {
6618
+ return null;
6619
+ }
6620
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "color-control", children: [
6621
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "label-small", children: label }),
6622
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "color-inputs", children: [
6623
+ /* @__PURE__ */ jsxRuntime.jsx(
6624
+ "input",
6625
+ {
6626
+ type: "color",
6627
+ value,
6628
+ onChange: (e) => handleChange(e.target.value),
6629
+ className: "color-picker"
6630
+ }
6631
+ ),
6632
+ /* @__PURE__ */ jsxRuntime.jsx(
6633
+ "input",
6634
+ {
6635
+ type: "text",
6636
+ value,
6637
+ onChange: (e) => handleChange(e.target.value),
6638
+ className: "color-text"
6639
+ }
6640
+ )
6641
+ ] })
6642
+ ] }, key);
6643
+ };
6401
6644
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "panel-container", children: [
6402
6645
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "panel-section", children: [
6403
6646
  /* @__PURE__ */ jsxRuntime.jsx("label", { className: "label-dark", children: "Caption Style" }),
@@ -6444,7 +6687,7 @@ function CaptionPropPanel({
6444
6687
  ] }),
6445
6688
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "panel-section", children: [
6446
6689
  /* @__PURE__ */ jsxRuntime.jsx("label", { className: "label-dark", children: "Font" }),
6447
- /* @__PURE__ */ jsxRuntime.jsxs(
6690
+ /* @__PURE__ */ jsxRuntime.jsx(
6448
6691
  "select",
6449
6692
  {
6450
6693
  value: fontFamily,
@@ -6454,111 +6697,59 @@ function CaptionPropPanel({
6454
6697
  handleUpdateCaption({ fontFamily: value });
6455
6698
  },
6456
6699
  className: "select-dark w-full",
6457
- children: [
6458
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "Bangers", children: "Bangers" }),
6459
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "Arial", children: "Arial" }),
6460
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "Helvetica", children: "Helvetica" }),
6461
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "Times New Roman", children: "Times New Roman" })
6462
- ]
6700
+ children: CAPTION_FONTS.map((font) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: font, children: font }, font))
6463
6701
  }
6464
6702
  )
6465
6703
  ] }),
6466
6704
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "panel-section", children: [
6467
6705
  /* @__PURE__ */ jsxRuntime.jsx("label", { className: "label-dark", children: "Colors" }),
6468
6706
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "color-section", children: [
6469
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "color-control", children: [
6470
- /* @__PURE__ */ jsxRuntime.jsx("label", { className: "label-small", children: "Text Color" }),
6471
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "color-inputs", children: [
6472
- /* @__PURE__ */ jsxRuntime.jsx(
6473
- "input",
6474
- {
6475
- type: "color",
6476
- value: colors.text,
6477
- onChange: (e) => {
6478
- const newColors = { ...colors, text: e.target.value };
6479
- setColors(newColors);
6480
- handleUpdateCaption({ colors: newColors });
6481
- },
6482
- className: "color-picker"
6483
- }
6484
- ),
6485
- /* @__PURE__ */ jsxRuntime.jsx(
6486
- "input",
6487
- {
6488
- type: "text",
6489
- value: colors.text,
6490
- onChange: (e) => {
6491
- const newColors = { ...colors, text: e.target.value };
6492
- setColors(newColors);
6493
- handleUpdateCaption({ colors: newColors });
6494
- },
6495
- className: "color-text"
6496
- }
6497
- )
6498
- ] })
6499
- ] }),
6500
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "color-control", children: [
6501
- /* @__PURE__ */ jsxRuntime.jsx("label", { className: "label-small", children: "Background Color" }),
6502
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "color-inputs", children: [
6503
- /* @__PURE__ */ jsxRuntime.jsx(
6504
- "input",
6505
- {
6506
- type: "color",
6507
- value: colors.bgColor,
6508
- onChange: (e) => {
6509
- const newColors = { ...colors, bgColor: e.target.value };
6510
- setColors(newColors);
6511
- handleUpdateCaption({ colors: newColors });
6512
- },
6513
- className: "color-picker"
6514
- }
6515
- ),
6516
- /* @__PURE__ */ jsxRuntime.jsx(
6517
- "input",
6518
- {
6519
- type: "text",
6520
- value: colors.bgColor,
6521
- onChange: (e) => {
6522
- const newColors = { ...colors, bgColor: e.target.value };
6523
- setColors(newColors);
6524
- handleUpdateCaption({ colors: newColors });
6525
- },
6526
- className: "color-text"
6527
- }
6528
- )
6529
- ] })
6530
- ] }),
6531
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "color-control", children: [
6532
- /* @__PURE__ */ jsxRuntime.jsx("label", { className: "label-small", children: "Outline Color" }),
6533
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "color-inputs", children: [
6534
- /* @__PURE__ */ jsxRuntime.jsx(
6535
- "input",
6536
- {
6537
- type: "color",
6538
- value: colors.outlineColor,
6539
- onChange: (e) => {
6540
- const newColors = { ...colors, outlineColor: e.target.value };
6541
- setColors(newColors);
6542
- handleUpdateCaption({ colors: newColors });
6543
- },
6544
- className: "color-picker"
6545
- }
6546
- ),
6547
- /* @__PURE__ */ jsxRuntime.jsx(
6548
- "input",
6549
- {
6550
- type: "text",
6551
- value: colors.outlineColor,
6552
- onChange: (e) => {
6553
- const newColors = { ...colors, outlineColor: e.target.value };
6554
- setColors(newColors);
6555
- handleUpdateCaption({ colors: newColors });
6556
- },
6557
- className: "color-text"
6558
- }
6559
- )
6560
- ] })
6561
- ] })
6707
+ currentColorMeta.usedColors.includes("highlight") && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "checkbox-control", children: /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "checkbox-label", children: [
6708
+ /* @__PURE__ */ jsxRuntime.jsx(
6709
+ "input",
6710
+ {
6711
+ type: "checkbox",
6712
+ checked: useHighlight,
6713
+ onChange: (e) => {
6714
+ const enabled = e.target.checked;
6715
+ setUseHighlight(enabled);
6716
+ const nextColors = enabled ? { ...colors, highlight: colors.highlight || CAPTION_COLOR.highlight } : { ...colors };
6717
+ setColors(nextColors);
6718
+ handleUpdateCaption({
6719
+ colors: nextColors,
6720
+ useHighlightOverride: enabled
6721
+ });
6722
+ },
6723
+ className: "checkbox-purple"
6724
+ }
6725
+ ),
6726
+ "Use Highlight Color"
6727
+ ] }) }),
6728
+ currentColorMeta.usedColors.includes("outlineColor") && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "checkbox-control", children: /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "checkbox-label", children: [
6729
+ /* @__PURE__ */ jsxRuntime.jsx(
6730
+ "input",
6731
+ {
6732
+ type: "checkbox",
6733
+ checked: useOutline,
6734
+ onChange: (e) => {
6735
+ const enabled = e.target.checked;
6736
+ setUseOutline(enabled);
6737
+ const nextColors = enabled ? {
6738
+ ...colors,
6739
+ outlineColor: colors.outlineColor || CAPTION_COLOR.outlineColor
6740
+ } : { ...colors };
6741
+ setColors(nextColors);
6742
+ handleUpdateCaption({
6743
+ colors: nextColors,
6744
+ useOutlineOverride: enabled
6745
+ });
6746
+ },
6747
+ className: "checkbox-purple"
6748
+ }
6749
+ ),
6750
+ "Use Outline Color"
6751
+ ] }) }),
6752
+ currentColorMeta.usedColors.map((key) => renderColorControl(key))
6562
6753
  ] })
6563
6754
  ] })
6564
6755
  ] });