@bendyline/squisq-editor-react 2.4.7 → 2.6.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.
@@ -15,7 +15,7 @@ import {
15
15
  import {
16
16
  RecorderPanel,
17
17
  useModalDialog
18
- } from "./chunk-ZSQN6IO7.js";
18
+ } from "./chunk-JUAQQTJE.js";
19
19
  import {
20
20
  Icon
21
21
  } from "./chunk-GS7QWYFT.js";
@@ -647,6 +647,8 @@ function EditorProvider({
647
647
  useEffect(() => sceneTextChannel.subscribe(setActiveSceneText), [sceneTextChannel]);
648
648
  const articleIdRef = useRef(articleId);
649
649
  articleIdRef.current = articleId;
650
+ const fileNameRef = useRef(fileName);
651
+ fileNameRef.current = fileName;
650
652
  useEffect(() => {
651
653
  setColorScheme(initialColorScheme);
652
654
  }, [initialColorScheme]);
@@ -660,7 +662,8 @@ function EditorProvider({
660
662
  setParseError(null);
661
663
  try {
662
664
  const generatedDoc = markdownToDoc(parsed, {
663
- articleId: articleIdRef.current
665
+ articleId: articleIdRef.current,
666
+ fileName: fileNameRef.current
664
667
  });
665
668
  setDoc(generatedDoc);
666
669
  } catch (docErr) {
@@ -811,7 +814,8 @@ function EditorProvider({
811
814
  setParseError(null);
812
815
  try {
813
816
  const generatedDoc = markdownToDoc(newDoc, {
814
- articleId: articleIdRef.current
817
+ articleId: articleIdRef.current,
818
+ fileName: fileNameRef.current
815
819
  });
816
820
  setDoc(generatedDoc);
817
821
  } catch (docErr) {
@@ -3259,6 +3263,7 @@ import {
3259
3263
  useRef as useRef7
3260
3264
  } from "react";
3261
3265
  import { createPortal as createPortal2 } from "react-dom";
3266
+ import { BlockRenderer, MediaContext } from "@bendyline/squisq-react";
3262
3267
  import {
3263
3268
  VIEWPORT_PRESETS,
3264
3269
  getThemeSummaries as getThemeSummaries2,
@@ -3277,8 +3282,13 @@ import {
3277
3282
  writeCustomThemesToFrontmatter as writeCustomThemesToFrontmatter2,
3278
3283
  writeCustomTemplatesToFrontmatter,
3279
3284
  FRONTMATTER_CUSTOM_THEMES_KEY as FRONTMATTER_CUSTOM_THEMES_KEY2,
3280
- FRONTMATTER_CUSTOM_TEMPLATES_KEY
3285
+ FRONTMATTER_CUSTOM_TEMPLATES_KEY,
3286
+ COVER_SLIDE_TEMPLATE_OPTIONS,
3287
+ createTemplateContext,
3288
+ expandCoverBlock,
3289
+ resolveCoverSlideSettings
3281
3290
  } from "@bendyline/squisq/doc";
3291
+ import { CoverImageExportModal } from "@bendyline/squisq-video-react/cover-image";
3282
3292
 
3283
3293
  // src/transformStyleId.ts
3284
3294
  import { getTransformStyleSummaries } from "@bendyline/squisq/transform";
@@ -3291,11 +3301,15 @@ function resolvePersistedTransformStyleId(value) {
3291
3301
  }
3292
3302
 
3293
3303
  // src/frontmatterSettings.ts
3304
+ import { COVER_SLIDE_FRONTMATTER_KEYS, DEFAULT_COVER_SLIDE_SETTINGS } from "@bendyline/squisq/doc";
3294
3305
  var FRONTMATTER_SETTING_KEYS = {
3295
3306
  theme: { canonical: "squisq-theme", legacy: ["themeId", "theme"] },
3296
3307
  transform: { canonical: "squisq-transform", legacy: "transform-style" },
3297
3308
  captions: { canonical: "squisq-captions", legacy: "caption-style" },
3298
- coverSlide: { canonical: "squisq-cover-slide", legacy: "cover-slide" },
3309
+ coverSlide: COVER_SLIDE_FRONTMATTER_KEYS.enabled,
3310
+ coverTemplate: COVER_SLIDE_FRONTMATTER_KEYS.template,
3311
+ coverDuration: COVER_SLIDE_FRONTMATTER_KEYS.duration,
3312
+ coverPlayback: COVER_SLIDE_FRONTMATTER_KEYS.playback,
3299
3313
  videoLoop: { canonical: "squisq-video-loop", legacy: "video-loop" },
3300
3314
  videoPresentation: {
3301
3315
  canonical: "squisq-video-presentation",
@@ -3309,7 +3323,10 @@ var FRONTMATTER_SETTING_DEFAULTS = {
3309
3323
  theme: "standard",
3310
3324
  transform: "",
3311
3325
  captions: "standard",
3312
- coverSlide: true,
3326
+ coverSlide: DEFAULT_COVER_SLIDE_SETTINGS.enabled,
3327
+ coverTemplate: DEFAULT_COVER_SLIDE_SETTINGS.template,
3328
+ coverDuration: DEFAULT_COVER_SLIDE_SETTINGS.duration,
3329
+ coverPlayback: DEFAULT_COVER_SLIDE_SETTINGS.playback,
3313
3330
  videoLoop: false,
3314
3331
  videoPresentation: "background",
3315
3332
  pipSize: "small",
@@ -3777,19 +3794,14 @@ function PreviewSettingsProvider({
3777
3794
  },
3778
3795
  [persistFrontmatter]
3779
3796
  );
3780
- const fmCoverSlide = useMemo8(
3781
- () => resolveFrontmatterBoolean(
3782
- readFrontmatterKey(
3783
- frontmatter,
3784
- FRONTMATTER_SETTING_KEYS.coverSlide.canonical,
3785
- FRONTMATTER_SETTING_KEYS.coverSlide.legacy
3786
- )
3787
- ),
3797
+ const resolvedCoverSettings = useMemo8(
3798
+ () => resolveCoverSlideSettings(frontmatter),
3788
3799
  [frontmatter]
3789
3800
  );
3801
+ const fmCoverSlide = resolvedCoverSettings.enabled;
3790
3802
  const [selectedCoverSlide, setSelectedCoverSlide] = useState9(null);
3791
3803
  useEffect8(() => setSelectedCoverSlide(null), [fmCoverSlide]);
3792
- const activeCoverSlide = selectedCoverSlide ?? fmCoverSlide ?? FRONTMATTER_SETTING_DEFAULTS.coverSlide;
3804
+ const activeCoverSlide = selectedCoverSlide ?? fmCoverSlide;
3793
3805
  const handleSetCoverSlideEnabled = useCallback9(
3794
3806
  (enabled) => {
3795
3807
  setSelectedCoverSlide(enabled);
@@ -3806,6 +3818,55 @@ function PreviewSettingsProvider({
3806
3818
  },
3807
3819
  [persistFrontmatter]
3808
3820
  );
3821
+ const [selectedCoverSlideTemplate, setSelectedCoverSlideTemplate] = useState9(null);
3822
+ useEffect8(() => setSelectedCoverSlideTemplate(null), [resolvedCoverSettings.template]);
3823
+ const activeCoverSlideTemplate = selectedCoverSlideTemplate ?? resolvedCoverSettings.template;
3824
+ const handleSetCoverSlideTemplate = useCallback9(
3825
+ (template) => {
3826
+ setSelectedCoverSlideTemplate(template);
3827
+ persistFrontmatter({
3828
+ [FRONTMATTER_SETTING_KEYS.coverTemplate.canonical]: omitFrontmatterDefault(
3829
+ template,
3830
+ FRONTMATTER_SETTING_DEFAULTS.coverTemplate
3831
+ ),
3832
+ [FRONTMATTER_SETTING_KEYS.coverTemplate.legacy]: null
3833
+ });
3834
+ },
3835
+ [persistFrontmatter]
3836
+ );
3837
+ const [selectedCoverSlideDuration, setSelectedCoverSlideDuration] = useState9(null);
3838
+ useEffect8(() => setSelectedCoverSlideDuration(null), [resolvedCoverSettings.duration]);
3839
+ const activeCoverSlideDuration = selectedCoverSlideDuration ?? resolvedCoverSettings.duration;
3840
+ const handleSetCoverSlideDuration = useCallback9(
3841
+ (duration) => {
3842
+ if (!Number.isFinite(duration) || duration < 0 || duration > 60) return;
3843
+ setSelectedCoverSlideDuration(duration);
3844
+ persistFrontmatter({
3845
+ [FRONTMATTER_SETTING_KEYS.coverDuration.canonical]: omitFrontmatterDefault(
3846
+ duration,
3847
+ FRONTMATTER_SETTING_DEFAULTS.coverDuration
3848
+ ),
3849
+ [FRONTMATTER_SETTING_KEYS.coverDuration.legacy]: null
3850
+ });
3851
+ },
3852
+ [persistFrontmatter]
3853
+ );
3854
+ const [selectedCoverSlidePlayback, setSelectedCoverSlidePlayback] = useState9(null);
3855
+ useEffect8(() => setSelectedCoverSlidePlayback(null), [resolvedCoverSettings.playback]);
3856
+ const activeCoverSlidePlayback = selectedCoverSlidePlayback ?? resolvedCoverSettings.playback;
3857
+ const handleSetCoverSlidePlayback = useCallback9(
3858
+ (playback) => {
3859
+ setSelectedCoverSlidePlayback(playback);
3860
+ persistFrontmatter({
3861
+ [FRONTMATTER_SETTING_KEYS.coverPlayback.canonical]: omitFrontmatterDefault(
3862
+ playback,
3863
+ FRONTMATTER_SETTING_DEFAULTS.coverPlayback
3864
+ ),
3865
+ [FRONTMATTER_SETTING_KEYS.coverPlayback.legacy]: null
3866
+ });
3867
+ },
3868
+ [persistFrontmatter]
3869
+ );
3809
3870
  const themeDesigner = useMemo8(
3810
3871
  () => designer.open ? {
3811
3872
  value: designer.editing,
@@ -3843,6 +3904,12 @@ function PreviewSettingsProvider({
3843
3904
  setVideoLoopEnabled: handleSetVideoLoopEnabled,
3844
3905
  activeCoverSlide,
3845
3906
  setCoverSlideEnabled: handleSetCoverSlideEnabled,
3907
+ activeCoverSlideTemplate,
3908
+ setCoverSlideTemplate: handleSetCoverSlideTemplate,
3909
+ activeCoverSlideDuration,
3910
+ setCoverSlideDuration: handleSetCoverSlideDuration,
3911
+ activeCoverSlidePlayback,
3912
+ setCoverSlidePlayback: handleSetCoverSlidePlayback,
3846
3913
  customThemes,
3847
3914
  openThemeDesigner,
3848
3915
  deleteCustomTheme,
@@ -3865,6 +3932,9 @@ function PreviewSettingsProvider({
3865
3932
  activePipPosition,
3866
3933
  activeVideoLoop,
3867
3934
  activeCoverSlide,
3935
+ activeCoverSlideTemplate,
3936
+ activeCoverSlideDuration,
3937
+ activeCoverSlidePlayback,
3868
3938
  handleSetThemeId,
3869
3939
  handleSetTransformStyle,
3870
3940
  handleSetCaptionMode,
@@ -3874,6 +3944,9 @@ function PreviewSettingsProvider({
3874
3944
  handlePipPosition,
3875
3945
  handleSetVideoLoopEnabled,
3876
3946
  handleSetCoverSlideEnabled,
3947
+ handleSetCoverSlideTemplate,
3948
+ handleSetCoverSlideDuration,
3949
+ handleSetCoverSlidePlayback,
3877
3950
  customThemes,
3878
3951
  openThemeDesigner,
3879
3952
  deleteCustomTheme,
@@ -4018,6 +4091,282 @@ var selectStyle = {
4018
4091
  fontSize: "12px",
4019
4092
  cursor: "pointer"
4020
4093
  };
4094
+ var COVER_MENU_WIDTH = 336;
4095
+ var COVER_MENU_GAP = 4;
4096
+ var COVER_MENU_MARGIN = 8;
4097
+ function CoverSlideMenuControl({ compact }) {
4098
+ const settings = usePreviewSettings();
4099
+ const { colorScheme, doc, fileName, mediaProvider } = useEditorContext();
4100
+ const triggerRef = useRef7(null);
4101
+ const menuRef = useRef7(null);
4102
+ const [open, setOpen] = useState9(false);
4103
+ const [anchor, setAnchor] = useState9(null);
4104
+ const [exportOpen, setExportOpen] = useState9(false);
4105
+ const updatePosition = useCallback9(() => {
4106
+ const trigger = triggerRef.current;
4107
+ if (!trigger) return;
4108
+ const rect = trigger.getBoundingClientRect();
4109
+ const width = Math.min(COVER_MENU_WIDTH, window.innerWidth - COVER_MENU_MARGIN * 2);
4110
+ setAnchor({
4111
+ top: rect.bottom + COVER_MENU_GAP,
4112
+ left: clampPreviewPopoverLeft(rect, width, window.innerWidth)
4113
+ });
4114
+ }, []);
4115
+ const closeMenu = useCallback9(() => {
4116
+ setOpen(false);
4117
+ setAnchor(null);
4118
+ }, []);
4119
+ useEffect8(() => {
4120
+ if (!open) return;
4121
+ const handlePointerDown = (event) => {
4122
+ const target = event.target;
4123
+ if (triggerRef.current?.contains(target) || menuRef.current?.contains(target)) return;
4124
+ closeMenu();
4125
+ };
4126
+ const handleKeyDown = (event) => {
4127
+ if (event.key !== "Escape") return;
4128
+ event.preventDefault();
4129
+ closeMenu();
4130
+ triggerRef.current?.focus();
4131
+ };
4132
+ document.addEventListener("mousedown", handlePointerDown);
4133
+ document.addEventListener("keydown", handleKeyDown);
4134
+ window.addEventListener("resize", updatePosition);
4135
+ window.addEventListener("scroll", updatePosition, true);
4136
+ return () => {
4137
+ document.removeEventListener("mousedown", handlePointerDown);
4138
+ document.removeEventListener("keydown", handleKeyDown);
4139
+ window.removeEventListener("resize", updatePosition);
4140
+ window.removeEventListener("scroll", updatePosition, true);
4141
+ };
4142
+ }, [closeMenu, open, updatePosition]);
4143
+ const coverExists = !!doc?.startBlock;
4144
+ const coverPreviewBlock = useMemo8(() => {
4145
+ if (!open || !doc?.startBlock) return null;
4146
+ const context = createTemplateContext(settings.activeTheme, 0, 1, settings.activeViewport);
4147
+ return {
4148
+ id: "cover-slide-menu-preview",
4149
+ startTime: -1,
4150
+ duration: 0,
4151
+ audioSegment: -1,
4152
+ layers: expandCoverBlock(doc.startBlock, context, settings.activeCoverSlideTemplate)
4153
+ };
4154
+ }, [
4155
+ open,
4156
+ doc?.startBlock,
4157
+ settings.activeTheme,
4158
+ settings.activeViewport,
4159
+ settings.activeCoverSlideTemplate
4160
+ ]);
4161
+ return /* @__PURE__ */ jsxs6(
4162
+ "div",
4163
+ {
4164
+ className: `squisq-preview-control squisq-cover-slide-control${compact ? " squisq-preview-control--compact" : ""}`,
4165
+ children: [
4166
+ /* @__PURE__ */ jsxs6(
4167
+ "button",
4168
+ {
4169
+ ref: triggerRef,
4170
+ type: "button",
4171
+ className: `squisq-cover-slide-trigger${open ? " squisq-cover-slide-trigger--open" : ""}`,
4172
+ "aria-label": "Cover slide settings",
4173
+ "aria-haspopup": "dialog",
4174
+ "aria-expanded": open,
4175
+ onClick: () => {
4176
+ if (open) {
4177
+ closeMenu();
4178
+ } else {
4179
+ updatePosition();
4180
+ setOpen(true);
4181
+ }
4182
+ },
4183
+ children: [
4184
+ /* @__PURE__ */ jsx8(
4185
+ "span",
4186
+ {
4187
+ className: `squisq-cover-slide-status${settings.activeCoverSlide ? " squisq-cover-slide-status--enabled" : ""}`,
4188
+ "aria-hidden": "true",
4189
+ children: /* @__PURE__ */ jsx8(Icon, { icon: settings.activeCoverSlide ? "fa-solid fa-check" : "fa-solid fa-minus" })
4190
+ }
4191
+ ),
4192
+ /* @__PURE__ */ jsx8("span", { children: "Cover slide" }),
4193
+ /* @__PURE__ */ jsx8("svg", { width: "10", height: "10", viewBox: "0 0 10 10", "aria-hidden": "true", children: /* @__PURE__ */ jsx8("path", { d: "M2 3.5 5 6.5 8 3.5", fill: "none", stroke: "currentColor", strokeWidth: "1.4" }) })
4194
+ ]
4195
+ }
4196
+ ),
4197
+ open && anchor && createPortal2(
4198
+ /* @__PURE__ */ jsxs6(
4199
+ "div",
4200
+ {
4201
+ ref: menuRef,
4202
+ className: "squisq-cover-slide-menu",
4203
+ "data-theme": colorScheme,
4204
+ role: "dialog",
4205
+ "aria-label": "Cover slide settings",
4206
+ style: { top: anchor.top, left: anchor.left },
4207
+ children: [
4208
+ /* @__PURE__ */ jsxs6("div", { className: "squisq-cover-slide-menu-heading", children: [
4209
+ /* @__PURE__ */ jsx8("span", { children: "Cover slide" }),
4210
+ /* @__PURE__ */ jsxs6("label", { className: "squisq-cover-slide-switch", children: [
4211
+ /* @__PURE__ */ jsx8(
4212
+ "input",
4213
+ {
4214
+ type: "checkbox",
4215
+ "aria-label": "Cover slide",
4216
+ checked: settings.activeCoverSlide,
4217
+ onChange: (event) => settings.setCoverSlideEnabled(event.target.checked)
4218
+ }
4219
+ ),
4220
+ /* @__PURE__ */ jsx8("span", { children: settings.activeCoverSlide ? "Shown" : "Hidden" })
4221
+ ] })
4222
+ ] }),
4223
+ /* @__PURE__ */ jsx8(
4224
+ "div",
4225
+ {
4226
+ className: `squisq-cover-slide-preview${settings.activeCoverSlide ? "" : " squisq-cover-slide-preview--disabled"}`,
4227
+ style: {
4228
+ aspectRatio: `${settings.activeViewport.width} / ${settings.activeViewport.height}`
4229
+ },
4230
+ children: coverPreviewBlock ? /* @__PURE__ */ jsx8(MediaContext.Provider, { value: mediaProvider ?? null, children: /* @__PURE__ */ jsx8("div", { className: "squisq-cover-slide-preview-frame", "aria-hidden": "true", children: /* @__PURE__ */ jsx8(
4231
+ BlockRenderer,
4232
+ {
4233
+ block: coverPreviewBlock,
4234
+ blockTime: 0,
4235
+ basePath: ".",
4236
+ viewport: settings.activeViewport,
4237
+ animationsEnabled: false,
4238
+ muted: true,
4239
+ theme: settings.activeTheme
4240
+ }
4241
+ ) }) }) : /* @__PURE__ */ jsxs6("p", { className: "squisq-cover-slide-preview-empty", children: [
4242
+ "No cover to preview yet \u2014 start the document with a level-1 heading (",
4243
+ /* @__PURE__ */ jsx8("code", { children: "#\xA0Title" }),
4244
+ ") to generate one."
4245
+ ] })
4246
+ }
4247
+ ),
4248
+ /* @__PURE__ */ jsxs6("label", { className: "squisq-cover-slide-field", children: [
4249
+ /* @__PURE__ */ jsx8("span", { children: "Appearance" }),
4250
+ /* @__PURE__ */ jsx8(
4251
+ "select",
4252
+ {
4253
+ "aria-label": "Cover slide appearance",
4254
+ value: settings.activeCoverSlideTemplate,
4255
+ onChange: (event) => settings.setCoverSlideTemplate(event.target.value),
4256
+ children: COVER_SLIDE_TEMPLATE_OPTIONS.map((option) => /* @__PURE__ */ jsx8(
4257
+ "option",
4258
+ {
4259
+ value: option.id,
4260
+ disabled: option.requiresHeroImage && !doc?.startBlock?.heroSrc,
4261
+ children: option.label
4262
+ },
4263
+ option.id
4264
+ ))
4265
+ }
4266
+ )
4267
+ ] }),
4268
+ /* @__PURE__ */ jsx8("p", { className: "squisq-cover-slide-help", children: COVER_SLIDE_TEMPLATE_OPTIONS.find(
4269
+ (option) => option.id === settings.activeCoverSlideTemplate
4270
+ )?.description }),
4271
+ /* @__PURE__ */ jsxs6("label", { className: "squisq-cover-slide-field", children: [
4272
+ /* @__PURE__ */ jsxs6("span", { children: [
4273
+ "Duration",
4274
+ /* @__PURE__ */ jsxs6("output", { children: [
4275
+ settings.activeCoverSlideDuration.toFixed(1),
4276
+ "s"
4277
+ ] })
4278
+ ] }),
4279
+ /* @__PURE__ */ jsx8(
4280
+ "input",
4281
+ {
4282
+ "aria-label": "Cover slide duration",
4283
+ type: "range",
4284
+ min: 0,
4285
+ max: 10,
4286
+ step: 0.5,
4287
+ value: settings.activeCoverSlideDuration,
4288
+ onChange: (event) => settings.setCoverSlideDuration(Number(event.target.value))
4289
+ }
4290
+ )
4291
+ ] }),
4292
+ /* @__PURE__ */ jsxs6("fieldset", { className: "squisq-cover-slide-timing", children: [
4293
+ /* @__PURE__ */ jsx8("legend", { children: "Exported video timing" }),
4294
+ /* @__PURE__ */ jsxs6("label", { children: [
4295
+ /* @__PURE__ */ jsx8(
4296
+ "input",
4297
+ {
4298
+ type: "radio",
4299
+ name: "squisq-cover-playback",
4300
+ value: "preroll",
4301
+ checked: settings.activeCoverSlidePlayback === "preroll",
4302
+ onChange: () => settings.setCoverSlidePlayback("preroll")
4303
+ }
4304
+ ),
4305
+ /* @__PURE__ */ jsxs6("span", { children: [
4306
+ "Delay video",
4307
+ /* @__PURE__ */ jsx8("small", { children: "Start the story after the cover." })
4308
+ ] })
4309
+ ] }),
4310
+ /* @__PURE__ */ jsxs6("label", { children: [
4311
+ /* @__PURE__ */ jsx8(
4312
+ "input",
4313
+ {
4314
+ type: "radio",
4315
+ name: "squisq-cover-playback",
4316
+ value: "overlay",
4317
+ checked: settings.activeCoverSlidePlayback === "overlay",
4318
+ onChange: () => settings.setCoverSlidePlayback("overlay")
4319
+ }
4320
+ ),
4321
+ /* @__PURE__ */ jsxs6("span", { children: [
4322
+ "Play underneath",
4323
+ /* @__PURE__ */ jsx8("small", { children: "Let the story advance behind the cover." })
4324
+ ] })
4325
+ ] })
4326
+ ] }),
4327
+ /* @__PURE__ */ jsxs6(
4328
+ "button",
4329
+ {
4330
+ type: "button",
4331
+ className: "squisq-cover-slide-export",
4332
+ disabled: !coverExists,
4333
+ title: coverExists ? void 0 : "No cover slide to export \u2014 start the document with a level-1 heading (# Title) to generate one.",
4334
+ onClick: () => {
4335
+ closeMenu();
4336
+ setExportOpen(true);
4337
+ },
4338
+ children: [
4339
+ /* @__PURE__ */ jsx8(Icon, { icon: "fa-solid fa-image" }),
4340
+ "Export cover as image\u2026"
4341
+ ]
4342
+ }
4343
+ )
4344
+ ]
4345
+ }
4346
+ ),
4347
+ document.body
4348
+ ),
4349
+ exportOpen && doc && createPortal2(
4350
+ /* @__PURE__ */ jsx8(
4351
+ CoverImageExportModal,
4352
+ {
4353
+ doc,
4354
+ mediaProvider,
4355
+ theme: settings.activeTheme,
4356
+ coverSlideTemplate: settings.activeCoverSlideTemplate,
4357
+ defaultWidth: settings.activeViewport.width,
4358
+ defaultHeight: settings.activeViewport.height,
4359
+ defaultFileName: fileName,
4360
+ colorScheme,
4361
+ onClose: () => setExportOpen(false)
4362
+ }
4363
+ ),
4364
+ document.body
4365
+ )
4366
+ ]
4367
+ }
4368
+ );
4369
+ }
4021
4370
  function PreviewToolbarControls({ displayMode } = {}) {
4022
4371
  const s = usePreviewSettings();
4023
4372
  const controlKeys = controlKeysForMode(displayMode ?? s.activeDisplayMode, s.hasVideoMedia);
@@ -4310,24 +4659,7 @@ function PreviewToolbarControls({ displayMode } = {}) {
4310
4659
  "loop"
4311
4660
  );
4312
4661
  case "cover":
4313
- return /* @__PURE__ */ jsx8(
4314
- "div",
4315
- {
4316
- className: `squisq-preview-control${compact ? " squisq-preview-control--compact" : ""}`,
4317
- children: /* @__PURE__ */ jsxs6("label", { className: "squisq-preview-checkbox", children: [
4318
- /* @__PURE__ */ jsx8(
4319
- "input",
4320
- {
4321
- type: "checkbox",
4322
- checked: s.activeCoverSlide,
4323
- onChange: (e2) => s.setCoverSlideEnabled(e2.target.checked)
4324
- }
4325
- ),
4326
- /* @__PURE__ */ jsx8("span", { children: "Cover slide" })
4327
- ] })
4328
- },
4329
- "cover"
4330
- );
4662
+ return /* @__PURE__ */ jsx8(CoverSlideMenuControl, { compact }, "cover");
4331
4663
  }
4332
4664
  };
4333
4665
  const hasOverflow = visibleCount < controlKeys.length;
@@ -4440,8 +4772,10 @@ function PreviewModeMenu({ openRequest = 0 }) {
4440
4772
  useEffect8(() => {
4441
4773
  if (!open) return;
4442
4774
  const handlePointerDown = (event) => {
4443
- const target = event.target;
4444
- if (triggerRef.current?.contains(target) || menuRef.current?.contains(target)) return;
4775
+ const eventPath = event.composedPath();
4776
+ if (triggerRef.current && eventPath.includes(triggerRef.current) || menuRef.current && eventPath.includes(menuRef.current)) {
4777
+ return;
4778
+ }
4445
4779
  closeMenu();
4446
4780
  };
4447
4781
  const handleKeyDown = (event) => {
@@ -5198,7 +5532,7 @@ function TemplateThumbnail({ def, width = W2, height = H2 }) {
5198
5532
 
5199
5533
  // src/TemplateContentPreview.tsx
5200
5534
  import { useMemo as useMemo11 } from "react";
5201
- import { BlockRenderer, MediaContext } from "@bendyline/squisq-react";
5535
+ import { BlockRenderer as BlockRenderer2, MediaContext as MediaContext2 } from "@bendyline/squisq-react";
5202
5536
 
5203
5537
  // src/templateContentPreviewResolver.ts
5204
5538
  import {
@@ -5502,8 +5836,8 @@ function TemplateContentPreview({
5502
5836
  className: "squisq-template-gallery-content-preview",
5503
5837
  style: { aspectRatio: `${source.viewport.width} / ${source.viewport.height}` },
5504
5838
  "aria-hidden": "true",
5505
- children: /* @__PURE__ */ jsx14(MediaContext.Provider, { value: source.mediaProvider ?? null, children: /* @__PURE__ */ jsx14(
5506
- BlockRenderer,
5839
+ children: /* @__PURE__ */ jsx14(MediaContext2.Provider, { value: source.mediaProvider ?? null, children: /* @__PURE__ */ jsx14(
5840
+ BlockRenderer2,
5507
5841
  {
5508
5842
  block: preview.visual,
5509
5843
  blockTime: 0,
@@ -5587,6 +5921,17 @@ var TEMPLATE_ENTRIES = [
5587
5921
  /* @__PURE__ */ jsx15("rect", { x: 11, y: 24, width: 20, height: 2.5, rx: 1, fill: F1, opacity: 0.7 })
5588
5922
  ] })
5589
5923
  },
5924
+ {
5925
+ name: "bigText",
5926
+ label: "Big Text",
5927
+ description: "Gigantic uppercase display text \u2014 thumbnail-style \u2014 on a theme surface or over an image with a contrast bloom.",
5928
+ icon: /* @__PURE__ */ jsxs11(TemplateIcon, { children: [
5929
+ /* @__PURE__ */ jsx15("rect", { x: 4, y: 4, width: 48, height: 32, rx: 2, fill: F1, opacity: 0.25 }),
5930
+ /* @__PURE__ */ jsx15("ellipse", { cx: 28, cy: 20, rx: 22, ry: 13, fill: F1, opacity: 0.35 }),
5931
+ /* @__PURE__ */ jsx15("rect", { x: 8, y: 12, width: 40, height: 10, rx: 2, fill: FA }),
5932
+ /* @__PURE__ */ jsx15("rect", { x: 16, y: 26, width: 24, height: 4, rx: 1, fill: F2 })
5933
+ ] })
5934
+ },
5590
5935
  {
5591
5936
  name: "content",
5592
5937
  label: "Content",
@@ -9763,7 +10108,15 @@ function MermaidDiagramTypeThumbnail({ preview }) {
9763
10108
  }
9764
10109
 
9765
10110
  // src/Toolbar.tsx
9766
- import { useCallback as useCallback29, useEffect as useEffect24, useMemo as useMemo24, useReducer, useRef as useRef28, useState as useState31 } from "react";
10111
+ import {
10112
+ useCallback as useCallback29,
10113
+ useEffect as useEffect24,
10114
+ useLayoutEffect as useLayoutEffect4,
10115
+ useMemo as useMemo24,
10116
+ useReducer,
10117
+ useRef as useRef28,
10118
+ useState as useState31
10119
+ } from "react";
9767
10120
  import { VIEWPORT_PRESETS as VIEWPORT_PRESETS2 } from "@bendyline/squisq/schemas";
9768
10121
  import { DEFAULT_THEME, flattenBlocks } from "@bendyline/squisq/doc";
9769
10122
  import {
@@ -9941,8 +10294,8 @@ function RecorderEntry({ open, onOpenChange, showTrigger = true } = {}) {
9941
10294
  const handleSave = useCallback16(
9942
10295
  (result) => {
9943
10296
  bumpMediaRevision();
9944
- if (result.source === "mic") {
9945
- if (activeView === "raw" && monacoEditor) {
10297
+ if (result.mediaKind === "audio") {
10298
+ if (result.source === "mic" && activeView === "raw" && monacoEditor) {
9946
10299
  annotateMonacoHeading(monacoEditor, result.filename);
9947
10300
  }
9948
10301
  const audioTag = `<audio src="${result.relativePath}" controls></audio>`;
@@ -10332,7 +10685,7 @@ import { createPortal as createPortal7 } from "react-dom";
10332
10685
  // src/customTemplates/TemplateDesigner.tsx
10333
10686
  import { useCallback as useCallback26, useId as useId9, useMemo as useMemo23, useRef as useRef25, useState as useState28 } from "react";
10334
10687
  import { createPortal as createPortal6 } from "react-dom";
10335
- import { MediaContext as MediaContext2 } from "@bendyline/squisq-react";
10688
+ import { MediaContext as MediaContext3 } from "@bendyline/squisq-react";
10336
10689
 
10337
10690
  // src/scene/Scene.tsx
10338
10691
  import {
@@ -15126,7 +15479,7 @@ function TemplateDesigner({
15126
15479
  /* @__PURE__ */ jsx36(LayerToolbar, { layer: selectedLayer, onAttr: handleLayerAttr })
15127
15480
  ] })
15128
15481
  ] }),
15129
- /* @__PURE__ */ jsx36("div", { className: "squisq-template-designer-scene", children: /* @__PURE__ */ jsx36(MediaContext2.Provider, { value: mediaProvider, children: /* @__PURE__ */ jsx36(
15482
+ /* @__PURE__ */ jsx36("div", { className: "squisq-template-designer-scene", children: /* @__PURE__ */ jsx36(MediaContext3.Provider, { value: mediaProvider, children: /* @__PURE__ */ jsx36(
15130
15483
  Scene,
15131
15484
  {
15132
15485
  viewport: currentViewport,
@@ -16479,6 +16832,8 @@ function Toolbar({
16479
16832
  slotAfterActions,
16480
16833
  slotRight,
16481
16834
  showPlayTab = true,
16835
+ showFormattingControls = true,
16836
+ showInsertControls = true,
16482
16837
  hostMode = "document"
16483
16838
  }) {
16484
16839
  const {
@@ -16570,9 +16925,8 @@ function Toolbar({
16570
16925
  const codeSnippetMenuRef = useRef28(null);
16571
16926
  const mermaidTypeMenuRef = useRef28(null);
16572
16927
  const chartTypeMenuRef = useRef28(null);
16573
- const [insertMenuAnchor, setInsertMenuAnchor] = useState31(
16574
- null
16575
- );
16928
+ const insertMenuTriggerRectRef = useRef28(null);
16929
+ const [insertMenuAnchor, setInsertMenuAnchor] = useState31(null);
16576
16930
  const [codeSnippetMenuAnchor, setCodeSnippetMenuAnchor] = useState31(null);
16577
16931
  const [mermaidTypeMenuAnchor, setMermaidTypeMenuAnchor] = useState31(null);
16578
16932
  const [chartTypeMenuAnchor, setChartTypeMenuAnchor] = useState31(null);
@@ -16588,7 +16942,8 @@ function Toolbar({
16588
16942
  if (left + INSERT_MENU_WIDTH + margin > vw) {
16589
16943
  left = Math.max(margin, rect.right - INSERT_MENU_WIDTH);
16590
16944
  }
16591
- setInsertMenuAnchor({ top: rect.bottom + gap, left });
16945
+ insertMenuTriggerRectRef.current = rect;
16946
+ setInsertMenuAnchor({ top: rect.bottom + gap, left, placement: "down" });
16592
16947
  setCodeSnippetMenuAnchor(null);
16593
16948
  setMermaidTypeMenuAnchor(null);
16594
16949
  setChartTypeMenuAnchor(null);
@@ -16642,6 +16997,7 @@ function Toolbar({
16642
16997
  setMermaidTypeMenuAnchor(null);
16643
16998
  }, []);
16644
16999
  const closeInsertMenu = useCallback29(() => {
17000
+ insertMenuTriggerRectRef.current = null;
16645
17001
  setInsertMenuAnchor(null);
16646
17002
  setCodeSnippetMenuAnchor(null);
16647
17003
  setMermaidTypeMenuAnchor(null);
@@ -16735,6 +17091,28 @@ function Toolbar({
16735
17091
  document.addEventListener("mousedown", handleClick);
16736
17092
  return () => document.removeEventListener("mousedown", handleClick);
16737
17093
  }, [insertMenuAnchor, closeInsertMenu]);
17094
+ const insertMenuOpen = insertMenuAnchor !== null;
17095
+ useLayoutEffect4(() => {
17096
+ if (!insertMenuOpen) return;
17097
+ const menu = insertMenuRef.current;
17098
+ const triggerRect = insertMenuTriggerRectRef.current;
17099
+ if (!menu || !triggerRect) return;
17100
+ const gap = 4;
17101
+ const margin = 8;
17102
+ const spaceBelow = Math.max(0, window.innerHeight - triggerRect.bottom - gap - margin);
17103
+ const spaceAbove = Math.max(0, triggerRect.top - gap - margin);
17104
+ const naturalHeight = Math.max(menu.scrollHeight, menu.getBoundingClientRect().height);
17105
+ const placement = naturalHeight > spaceBelow && spaceAbove > spaceBelow ? "up" : "down";
17106
+ const maxHeight = placement === "up" ? spaceAbove : spaceBelow;
17107
+ const visibleHeight = Math.min(naturalHeight, maxHeight);
17108
+ const top = placement === "up" ? Math.max(margin, triggerRect.top - gap - visibleHeight) : triggerRect.bottom + gap;
17109
+ setInsertMenuAnchor((current) => {
17110
+ if (!current || current.top === top && current.maxHeight === maxHeight && current.placement === placement) {
17111
+ return current;
17112
+ }
17113
+ return { ...current, top, maxHeight, placement };
17114
+ });
17115
+ }, [insertMenuOpen]);
16738
17116
  const [overflowPlacement, setOverflowPlacement] = useState31("down");
16739
17117
  useEffect24(() => {
16740
17118
  if (!showOverflow || !overflowRef.current) return;
@@ -17461,7 +17839,7 @@ ${TASK_LIST_MARKDOWN}
17461
17839
  return Number(m[1]) <= visibleHeadingMax;
17462
17840
  };
17463
17841
  const hasVisibleMediaButtons = MEDIA_BUTTONS.some((b) => isButtonVisible(b.id));
17464
- const showInsertInOverflow = overflowIndex !== null && overflowIndex <= FIRST_MEDIA_INDEX && hasVisibleMediaButtons;
17842
+ const showInsertInOverflow = showInsertControls && overflowIndex !== null && overflowIndex <= FIRST_MEDIA_INDEX && hasVisibleMediaButtons;
17465
17843
  const isInTable = isWysiwyg ? tiptapEditor.isActive("table") : false;
17466
17844
  const wysiwygTemplate = isWysiwyg ? tiptapEditor.isActive("heading") ? tiptapEditor.getAttributes("heading")?.dataTemplate ?? "" : null : null;
17467
17845
  const isRawView = activeView === "raw";
@@ -17681,17 +18059,17 @@ ${TASK_LIST_MARKDOWN}
17681
18059
  dataTemplateParams: updated.templateParams
17682
18060
  }).run();
17683
18061
  };
17684
- const showTemplateInOverflow = currentTemplate !== null && clippedContextual.has("template");
17685
- const showTransitionInOverflow = currentTransition !== null && clippedContextual.has("transition");
18062
+ const showTemplateInOverflow = showFormattingControls && currentTemplate !== null && clippedContextual.has("template");
18063
+ const showTransitionInOverflow = showFormattingControls && currentTransition !== null && clippedContextual.has("transition");
17686
18064
  const showBlockSectionInOverflow = showTemplateInOverflow || showTransitionInOverflow;
17687
18065
  const overflowBlockLabel = currentTemplate ? templateLabel(currentTemplate) : "Heading";
17688
- const showToolbarOverflow = !findMode && !isPreview && !isCodeMode && (overflowIndex !== null || clippedContextual.size > 0);
18066
+ const showToolbarOverflow = !findMode && !isPreview && !isCodeMode && (showFormattingControls && (overflowIndex !== null || clippedContextual.size > 0) || showInsertInOverflow);
17689
18067
  return /* @__PURE__ */ jsxs31(
17690
18068
  "div",
17691
18069
  {
17692
18070
  className: `squisq-toolbar${findMode ? " squisq-toolbar--find" : ""} ${className || ""}`,
17693
18071
  role: "toolbar",
17694
- "aria-label": findMode ? "Find toolbar" : "Formatting toolbar",
18072
+ "aria-label": findMode ? "Find toolbar" : showFormattingControls ? "Formatting toolbar" : "Editor toolbar",
17695
18073
  children: [
17696
18074
  /* @__PURE__ */ jsx41(
17697
18075
  "input",
@@ -17762,8 +18140,8 @@ ${TASK_LIST_MARKDOWN}
17762
18140
  );
17763
18141
  }) }),
17764
18142
  findMode ? /* @__PURE__ */ jsx41(FindToolbar, { onClose: () => setFindMode(false) }) : slotAfterTabs,
17765
- !findMode && !isPreview && !isCodeMode && /* @__PURE__ */ jsxs31("div", { className: "squisq-toolbar-actions", ref: actionsRef, children: [
17766
- groups.map((group, gi) => /* @__PURE__ */ jsxs31("div", { className: "squisq-toolbar-group", children: [
18143
+ !findMode && !isPreview && !isCodeMode && (showFormattingControls || showInsertControls) && /* @__PURE__ */ jsxs31("div", { className: "squisq-toolbar-actions", ref: actionsRef, children: [
18144
+ showFormattingControls && groups.map((group, gi) => /* @__PURE__ */ jsxs31("div", { className: "squisq-toolbar-group", children: [
17767
18145
  gi > 0 && /* @__PURE__ */ jsx41("div", { className: "squisq-toolbar-separator" }),
17768
18146
  BUTTONS.filter((b) => b.group === group && isButtonVisible(b.id)).map((btn) => {
17769
18147
  const active = btn.id === "emoji" ? emojiPickerAnchor !== null : formatActive && formattingEditor ? isTiptapActive(formattingEditor, btn.id) : false;
@@ -17785,8 +18163,8 @@ ${TASK_LIST_MARKDOWN}
17785
18163
  );
17786
18164
  })
17787
18165
  ] }, group)),
17788
- /* @__PURE__ */ jsxs31("div", { className: "squisq-toolbar-group", children: [
17789
- /* @__PURE__ */ jsx41("div", { className: "squisq-toolbar-separator" }),
18166
+ showInsertControls && /* @__PURE__ */ jsxs31("div", { className: "squisq-toolbar-group", children: [
18167
+ showFormattingControls && /* @__PURE__ */ jsx41("div", { className: "squisq-toolbar-separator" }),
17790
18168
  /* @__PURE__ */ jsx41(
17791
18169
  "button",
17792
18170
  {
@@ -17802,7 +18180,7 @@ ${TASK_LIST_MARKDOWN}
17802
18180
  }
17803
18181
  )
17804
18182
  ] }),
17805
- currentTemplate !== null && /* @__PURE__ */ jsxs31(
18183
+ showFormattingControls && currentTemplate !== null && /* @__PURE__ */ jsxs31(
17806
18184
  "div",
17807
18185
  {
17808
18186
  className: `squisq-toolbar-group squisq-toolbar-contextual squisq-template-picker${clippedContextual.has("template") ? " squisq-toolbar-contextual--clipped" : ""}`,
@@ -17822,7 +18200,7 @@ ${TASK_LIST_MARKDOWN}
17822
18200
  ]
17823
18201
  }
17824
18202
  ),
17825
- currentTransition !== null && /* @__PURE__ */ jsxs31(
18203
+ showFormattingControls && currentTransition !== null && /* @__PURE__ */ jsxs31(
17826
18204
  "div",
17827
18205
  {
17828
18206
  className: `squisq-toolbar-group squisq-toolbar-contextual squisq-transition-picker-group${clippedContextual.has("transition") ? " squisq-toolbar-contextual--clipped" : ""}`,
@@ -17841,7 +18219,7 @@ ${TASK_LIST_MARKDOWN}
17841
18219
  ]
17842
18220
  }
17843
18221
  ),
17844
- isInTable && /* @__PURE__ */ jsxs31(
18222
+ showFormattingControls && isInTable && /* @__PURE__ */ jsxs31(
17845
18223
  "div",
17846
18224
  {
17847
18225
  className: `squisq-toolbar-group squisq-toolbar-contextual squisq-table-controls${clippedContextual.has("table") ? " squisq-toolbar-contextual--clipped" : ""}`,
@@ -18041,6 +18419,7 @@ ${TASK_LIST_MARKDOWN}
18041
18419
  }
18042
18420
  )
18043
18421
  ] }),
18422
+ !findMode && !isPreview && !isCodeMode && !showFormattingControls && !showInsertControls && /* @__PURE__ */ jsx41("div", { className: "squisq-toolbar-actions" }),
18044
18423
  showToolbarOverflow && /* @__PURE__ */ jsxs31("div", { className: "squisq-toolbar-overflow", ref: overflowRef, children: [
18045
18424
  /* @__PURE__ */ jsx41(
18046
18425
  "button",
@@ -18059,7 +18438,7 @@ ${TASK_LIST_MARKDOWN}
18059
18438
  {
18060
18439
  className: `squisq-toolbar-overflow-menu squisq-toolbar-overflow-menu--${overflowPlacement}`,
18061
18440
  children: [
18062
- BUTTONS.slice(overflowIndex ?? BUTTONS.length).filter((b) => isButtonVisible(b.id)).filter((b) => b.group !== "media").map((btn) => {
18441
+ showFormattingControls && BUTTONS.slice(overflowIndex ?? BUTTONS.length).filter((b) => isButtonVisible(b.id)).filter((b) => b.group !== "media").map((btn) => {
18063
18442
  const active = btn.id === "emoji" ? emojiPickerAnchor !== null : formatActive && formattingEditor ? isTiptapActive(formattingEditor, btn.id) : false;
18064
18443
  const disabled = btn.id === "image" && !mediaProvider || !buttonAllowed(btn.id);
18065
18444
  return /* @__PURE__ */ jsxs31(
@@ -18135,7 +18514,7 @@ ${TASK_LIST_MARKDOWN}
18135
18514
  accentColor: previewSettings?.activeTheme.colors.primary
18136
18515
  }
18137
18516
  ) }),
18138
- isInTable && clippedContextual.has("table") && /* @__PURE__ */ jsxs31(Fragment13, { children: [
18517
+ showFormattingControls && isInTable && clippedContextual.has("table") && /* @__PURE__ */ jsxs31(Fragment13, { children: [
18139
18518
  /* @__PURE__ */ jsx41(
18140
18519
  "div",
18141
18520
  {
@@ -18263,7 +18642,13 @@ ${TASK_LIST_MARKDOWN}
18263
18642
  ref: insertMenuRef,
18264
18643
  className: "squisq-insert-menu",
18265
18644
  "data-theme": colorScheme,
18266
- style: { position: "fixed", top: insertMenuAnchor.top, left: insertMenuAnchor.left },
18645
+ "data-placement": insertMenuAnchor.placement,
18646
+ style: {
18647
+ position: "fixed",
18648
+ top: insertMenuAnchor.top,
18649
+ left: insertMenuAnchor.left,
18650
+ maxHeight: insertMenuAnchor.maxHeight
18651
+ },
18267
18652
  role: "menu",
18268
18653
  children: [
18269
18654
  showConvertActions && /* @__PURE__ */ jsxs31(Fragment13, { children: [
@@ -22229,7 +22614,7 @@ function mermaidErrorMessage(error) {
22229
22614
  }
22230
22615
 
22231
22616
  // src/mermaid/MermaidDiagramCanvas.tsx
22232
- import { useCallback as useCallback33, useEffect as useEffect27, useId as useId12, useLayoutEffect as useLayoutEffect4, useRef as useRef32, useState as useState36 } from "react";
22617
+ import { useCallback as useCallback33, useEffect as useEffect27, useId as useId12, useLayoutEffect as useLayoutEffect5, useRef as useRef32, useState as useState36 } from "react";
22233
22618
  import { jsx as jsx46, jsxs as jsxs35 } from "react/jsx-runtime";
22234
22619
  var renderSequence = 0;
22235
22620
  function excludesCanvasPan(target, viewport) {
@@ -22359,7 +22744,7 @@ function MermaidDiagramCanvas({
22359
22744
  current = false;
22360
22745
  };
22361
22746
  }, [source, onModelChange, theme]);
22362
- useLayoutEffect4(() => {
22747
+ useLayoutEffect5(() => {
22363
22748
  const scroll = scrollRef.current;
22364
22749
  if (!scroll) return;
22365
22750
  const measure = () => {
@@ -22380,7 +22765,7 @@ function MermaidDiagramCanvas({
22380
22765
  observer.observe(scroll);
22381
22766
  return () => observer.disconnect();
22382
22767
  }, []);
22383
- useLayoutEffect4(() => {
22768
+ useLayoutEffect5(() => {
22384
22769
  setContentSize(readSvgViewBox(svgRootRef.current));
22385
22770
  }, [svg]);
22386
22771
  const fitZoom = contentSize && canvasSize ? calculateFitScale(contentSize, canvasSize) : 1;
@@ -22389,7 +22774,7 @@ function MermaidDiagramCanvas({
22389
22774
  setZoom(fitZoom);
22390
22775
  setPan({ x: 0, y: 0 });
22391
22776
  }, [fitZoom]);
22392
- useLayoutEffect4(() => {
22777
+ useLayoutEffect5(() => {
22393
22778
  if (viewMode !== "fit") return;
22394
22779
  setZoom(fitZoom);
22395
22780
  setPan({ x: 0, y: 0 });
@@ -22456,7 +22841,7 @@ function MermaidDiagramCanvas({
22456
22841
  selectedNodeId,
22457
22842
  selectedTextId
22458
22843
  ]);
22459
- useLayoutEffect4(() => {
22844
+ useLayoutEffect5(() => {
22460
22845
  const node2 = renamingNodeId ? model2?.nodes.find((candidate) => candidate.id === renamingNodeId) : null;
22461
22846
  const edge2 = renamingEdgeId ? model2?.edges.find((candidate) => candidate.id === renamingEdgeId) : null;
22462
22847
  const text = renamingTextId ? model2 && mermaidEditableTexts(model2).find((candidate) => candidate.id === renamingTextId) : null;
@@ -22469,7 +22854,7 @@ function MermaidDiagramCanvas({
22469
22854
  });
22470
22855
  return () => window.cancelAnimationFrame(frame);
22471
22856
  }, [model2, renamingEdgeId, renamingNodeId, renamingTextId]);
22472
- useLayoutEffect4(() => {
22857
+ useLayoutEffect5(() => {
22473
22858
  const root = svgRootRef.current;
22474
22859
  if (!root || !model2) {
22475
22860
  setSelectionAnchor(null);
@@ -23009,7 +23394,7 @@ function CanvasAction({
23009
23394
  }
23010
23395
 
23011
23396
  // src/mermaid/MermaidShapePalette.tsx
23012
- import { useEffect as useEffect28, useLayoutEffect as useLayoutEffect5, useMemo as useMemo29, useRef as useRef33, useState as useState37 } from "react";
23397
+ import { useEffect as useEffect28, useLayoutEffect as useLayoutEffect6, useMemo as useMemo29, useRef as useRef33, useState as useState37 } from "react";
23013
23398
  import { jsx as jsx47, jsxs as jsxs36 } from "react/jsx-runtime";
23014
23399
  var CATEGORIES = ["Basic", "Process", "Data", "Documents", "Symbols"];
23015
23400
  var PALETTE_WIDTH = 360;
@@ -23045,7 +23430,7 @@ function MermaidShapePalette({ selected, onPick, onClose }) {
23045
23430
  )
23046
23431
  })).filter((section) => section.shapes.length > 0);
23047
23432
  }, [query]);
23048
- useLayoutEffect5(() => {
23433
+ useLayoutEffect6(() => {
23049
23434
  const palette = ref.current;
23050
23435
  const anchor = palette?.parentElement;
23051
23436
  if (!palette || !anchor) return;
@@ -23204,7 +23589,7 @@ import { createRoot as createRoot3 } from "react-dom/client";
23204
23589
  import {
23205
23590
  useCallback as useCallback34,
23206
23591
  useEffect as useEffect29,
23207
- useLayoutEffect as useLayoutEffect6,
23592
+ useLayoutEffect as useLayoutEffect7,
23208
23593
  useRef as useRef34,
23209
23594
  useState as useState38,
23210
23595
  useSyncExternalStore
@@ -24098,7 +24483,7 @@ function useDismissibleMermaidPopover(ref, onClose) {
24098
24483
  }
24099
24484
  function useClampedMermaidPopoverPosition(ref, preferredWidth, preferredMaxHeight) {
24100
24485
  const [position, setPosition] = useState38(null);
24101
- useLayoutEffect6(() => {
24486
+ useLayoutEffect7(() => {
24102
24487
  const picker = ref.current;
24103
24488
  const anchor = picker?.parentElement;
24104
24489
  if (!picker || !anchor) return;
@@ -27090,7 +27475,7 @@ function persistFromWrite(bodyMd, state) {
27090
27475
  }
27091
27476
 
27092
27477
  // src/WysiwygEditor.tsx
27093
- import { useCallback as useCallback38, useEffect as useEffect39, useMemo as useMemo35, useRef as useRef40, useState as useState49 } from "react";
27478
+ import { useCallback as useCallback38, useEffect as useEffect40, useMemo as useMemo35, useRef as useRef40, useState as useState50 } from "react";
27094
27479
  import { useEditor as useEditor2, EditorContent as EditorContent2 } from "@tiptap/react";
27095
27480
  import { Selection } from "@tiptap/pm/state";
27096
27481
  import StarterKit2 from "@tiptap/starter-kit";
@@ -27531,6 +27916,7 @@ var ImageWithMediaProvider = Image.extend({
27531
27916
  // src/tiptap/TiptapVideo.tsx
27532
27917
  import { Node as Node3, mergeAttributes as mergeAttributes2 } from "@tiptap/core";
27533
27918
  import { NodeViewWrapper as NodeViewWrapper2, ReactNodeViewRenderer as ReactNodeViewRenderer2 } from "@tiptap/react";
27919
+ import { useEffect as useEffect39, useState as useState49 } from "react";
27534
27920
 
27535
27921
  // src/tiptap/useResolvedMediaSrc.ts
27536
27922
  import { useEffect as useEffect38, useState as useState48 } from "react";
@@ -27589,15 +27975,23 @@ function VideoNodeView({ node: node2, updateAttributes, selected }) {
27589
27975
  const placement = normalizeVideoPlacement(rawPlacement);
27590
27976
  const lockToBlock = normalizeLockToBlock(rawLockToBlock);
27591
27977
  const resolvedSrc = useResolvedMediaSrc(src ?? "");
27978
+ const [audioOnly, setAudioOnly] = useState49(false);
27592
27979
  const resolvedPoster = useResolvedMediaSrc(poster ?? "");
27980
+ useEffect39(() => {
27981
+ setAudioOnly(false);
27982
+ }, [resolvedSrc]);
27983
+ const handleLoadedMetadata = (event) => {
27984
+ const video = event.currentTarget;
27985
+ setAudioOnly(video.videoWidth <= 0 && video.videoHeight <= 0);
27986
+ };
27593
27987
  return /* @__PURE__ */ jsxs43(
27594
27988
  NodeViewWrapper2,
27595
27989
  {
27596
27990
  as: "div",
27597
- className: `squisq-inline-video-player squisq-video-node${selected ? " squisq-video-node--selected" : ""}`,
27991
+ className: `${audioOnly ? "squisq-inline-audio-player squisq-video-node--audio-only" : "squisq-inline-video-player"} squisq-video-node${selected ? " squisq-video-node--selected" : ""}`,
27598
27992
  "data-video-placement": placement,
27599
27993
  children: [
27600
- /* @__PURE__ */ jsxs43(
27994
+ !audioOnly && /* @__PURE__ */ jsxs43(
27601
27995
  "div",
27602
27996
  {
27603
27997
  className: "squisq-video-placement-toolbar",
@@ -27635,7 +28029,16 @@ function VideoNodeView({ node: node2, updateAttributes, selected }) {
27635
28029
  ]
27636
28030
  }
27637
28031
  ),
27638
- /* @__PURE__ */ jsx54(
28032
+ audioOnly ? /* @__PURE__ */ jsx54(
28033
+ "audio",
28034
+ {
28035
+ "data-drag-handle": true,
28036
+ draggable: true,
28037
+ src: resolvedSrc || void 0,
28038
+ controls,
28039
+ preload: "metadata"
28040
+ }
28041
+ ) : /* @__PURE__ */ jsx54(
27639
28042
  "video",
27640
28043
  {
27641
28044
  "data-drag-handle": true,
@@ -27646,7 +28049,8 @@ function VideoNodeView({ node: node2, updateAttributes, selected }) {
27646
28049
  playsInline: true,
27647
28050
  preload: "metadata",
27648
28051
  width: width ?? void 0,
27649
- height: height ?? void 0
28052
+ height: height ?? void 0,
28053
+ onLoadedMetadata: handleLoadedMetadata
27650
28054
  }
27651
28055
  )
27652
28056
  ]
@@ -28239,11 +28643,11 @@ function WysiwygEditor({
28239
28643
  mermaidThemeStoreRef.current = createMermaidThemeStore(activeTheme ?? DEFAULT_THEME4);
28240
28644
  }
28241
28645
  const mermaidThemeStore = mermaidThemeStoreRef.current;
28242
- useEffect39(() => {
28646
+ useEffect40(() => {
28243
28647
  mermaidThemeStore.setTheme(activeTheme ?? DEFAULT_THEME4);
28244
28648
  }, [activeTheme, mermaidThemeStore]);
28245
28649
  const { docTemplates, onDocTemplatesChange } = useDocCustomTemplates();
28246
- const [designerState, setDesignerState] = useState49(
28650
+ const [designerState, setDesignerState] = useState50(
28247
28651
  null
28248
28652
  );
28249
28653
  const handleDesignerSave = useCallback38(
@@ -28259,7 +28663,7 @@ function WysiwygEditor({
28259
28663
  [docTemplates, onDocTemplatesChange]
28260
28664
  );
28261
28665
  const mentionProviderRef = useRef40(mentionProvider);
28262
- useEffect39(() => {
28666
+ useEffect40(() => {
28263
28667
  mentionProviderRef.current = mentionProvider;
28264
28668
  }, [mentionProvider]);
28265
28669
  const resolvedPlaceholder = useMemo35(() => placeholder ?? pickEmptyPrompt(), [placeholder]);
@@ -28267,7 +28671,7 @@ function WysiwygEditor({
28267
28671
  const lastSourceRef = useRef40(editorSource);
28268
28672
  const pendingLocalSourcesRef = useRef40([]);
28269
28673
  const mediaProviderRef = useRef40(mediaProvider);
28270
- useEffect39(() => {
28674
+ useEffect40(() => {
28271
28675
  mediaProviderRef.current = mediaProvider;
28272
28676
  }, [mediaProvider]);
28273
28677
  const frontmatterRef = useRef40(stripFrontmatter(editorSource).frontmatter);
@@ -28284,7 +28688,7 @@ function WysiwygEditor({
28284
28688
  }
28285
28689
  }
28286
28690
  const submitOnEnterRef = useRef40(submitOnEnter);
28287
- useEffect39(() => {
28691
+ useEffect40(() => {
28288
28692
  submitOnEnterRef.current = submitOnEnter;
28289
28693
  }, [submitOnEnter]);
28290
28694
  const editor = useEditor2({
@@ -28466,25 +28870,25 @@ function WysiwygEditor({
28466
28870
  }
28467
28871
  }
28468
28872
  });
28469
- useEffect39(() => {
28873
+ useEffect40(() => {
28470
28874
  if (editor) {
28471
28875
  setTiptapEditor(editor);
28472
28876
  }
28473
28877
  return () => setTiptapEditor(null);
28474
28878
  }, [editor, setTiptapEditor]);
28475
- useEffect39(() => {
28879
+ useEffect40(() => {
28476
28880
  if (editor) editor.setEditable(!readOnly);
28477
28881
  }, [editor, readOnly]);
28478
28882
  const containerRef = useRef40(null);
28479
- const [badgeMenu, setBadgeMenu] = useState49(null);
28480
- const [propsMenu, setPropsMenu] = useState49(null);
28883
+ const [badgeMenu, setBadgeMenu] = useState50(null);
28884
+ const [propsMenu, setPropsMenu] = useState50(null);
28481
28885
  const closeBadgeMenu = useCallback38(() => {
28482
28886
  setBadgeMenu(null);
28483
28887
  requestAnimationFrame(() => {
28484
28888
  if (editor && !editor.isDestroyed) editor.commands.focus();
28485
28889
  });
28486
28890
  }, [editor]);
28487
- useEffect39(() => {
28891
+ useEffect40(() => {
28488
28892
  if (!editor) return;
28489
28893
  const root = containerRef.current;
28490
28894
  if (!root) return;
@@ -28540,7 +28944,7 @@ function WysiwygEditor({
28540
28944
  root.addEventListener("mousedown", onClick);
28541
28945
  return () => root.removeEventListener("mousedown", onClick);
28542
28946
  }, [editor]);
28543
- useEffect39(() => {
28947
+ useEffect40(() => {
28544
28948
  if (!editor) return;
28545
28949
  const pendingIndex = pendingLocalSourcesRef.current.lastIndexOf(editorSource);
28546
28950
  if (pendingIndex >= 0) {
@@ -28797,7 +29201,7 @@ function moveSelectionToDropPoint(view, event) {
28797
29201
  }
28798
29202
 
28799
29203
  // src/InlinePreviewGutter.tsx
28800
- import { useLayoutEffect as useLayoutEffect7, useMemo as useMemo37, useRef as useRef41, useState as useState51 } from "react";
29204
+ import { useLayoutEffect as useLayoutEffect8, useMemo as useMemo37, useRef as useRef41, useState as useState52 } from "react";
28801
29205
  import { VIEWPORT_PRESETS as VIEWPORT_PRESETS4 } from "@bendyline/squisq/schemas";
28802
29206
  import {
28803
29207
  flattenBlocks as flattenBlocks4,
@@ -28806,17 +29210,17 @@ import {
28806
29210
  deriveTemplateInputs as deriveTemplateInputs2
28807
29211
  } from "@bendyline/squisq/doc";
28808
29212
  import { extractPlainText as extractPlainText3, getChildren } from "@bendyline/squisq/markdown";
28809
- import { BlockRenderer as BlockRenderer2, MediaContext as MediaContext3 } from "@bendyline/squisq-react";
29213
+ import { BlockRenderer as BlockRenderer3, MediaContext as MediaContext4 } from "@bendyline/squisq-react";
28810
29214
 
28811
29215
  // src/useHeadingLayout.ts
28812
- import { useCallback as useCallback39, useEffect as useEffect40, useMemo as useMemo36, useState as useState50 } from "react";
29216
+ import { useCallback as useCallback39, useEffect as useEffect41, useMemo as useMemo36, useState as useState51 } from "react";
28813
29217
  import { flattenBlocks as flattenBlocks3, hasTemplate } from "@bendyline/squisq/doc";
28814
29218
  function useHeadingLayout(refInsideWrapper) {
28815
29219
  const { doc, activeView, monacoEditor, tiptapEditor } = useEditorContext();
28816
29220
  const flatBlocks = useMemo36(() => doc ? flattenBlocks3(doc.blocks) : [], [doc]);
28817
- const [entries, setEntries] = useState50([]);
28818
- const [pageEdges, setPageEdges] = useState50(null);
28819
- useEffect40(() => {
29221
+ const [entries, setEntries] = useState51([]);
29222
+ const [pageEdges, setPageEdges] = useState51(null);
29223
+ useEffect41(() => {
28820
29224
  if (activeView !== "wysiwyg") return;
28821
29225
  const node2 = refInsideWrapper.current;
28822
29226
  if (!node2) return;
@@ -28884,7 +29288,7 @@ function useHeadingLayout(refInsideWrapper) {
28884
29288
  window.removeEventListener("resize", recompute);
28885
29289
  };
28886
29290
  }, [activeView, flatBlocks, refInsideWrapper]);
28887
- useEffect40(() => {
29291
+ useEffect41(() => {
28888
29292
  if (activeView !== "raw") return;
28889
29293
  if (!monacoEditor) return;
28890
29294
  const node2 = refInsideWrapper.current;
@@ -28945,7 +29349,7 @@ function useHeadingLayout(refInsideWrapper) {
28945
29349
  window.removeEventListener("resize", recompute);
28946
29350
  };
28947
29351
  }, [activeView, monacoEditor, flatBlocks, refInsideWrapper]);
28948
- useEffect40(() => {
29352
+ useEffect41(() => {
28949
29353
  setEntries([]);
28950
29354
  setPageEdges(null);
28951
29355
  }, [activeView]);
@@ -29239,8 +29643,8 @@ function InlinePreviewGutter({
29239
29643
  headingEntries.forEach((e2) => m.set(e2.block.id, e2.top));
29240
29644
  return m;
29241
29645
  }, [headingEntries]);
29242
- const [positions, setPositions] = useState51(/* @__PURE__ */ new Map());
29243
- useLayoutEffect7(() => {
29646
+ const [positions, setPositions] = useState52(/* @__PURE__ */ new Map());
29647
+ useLayoutEffect8(() => {
29244
29648
  if (items.length === 0) {
29245
29649
  setPositions((prev) => prev.size === 0 ? prev : /* @__PURE__ */ new Map());
29246
29650
  return;
@@ -29368,7 +29772,7 @@ function InlinePreviewGutter({
29368
29772
  })
29369
29773
  }
29370
29774
  ),
29371
- /* @__PURE__ */ jsx57(MediaContext3.Provider, { value: mediaProvider ?? null, children: items.map((item) => {
29775
+ /* @__PURE__ */ jsx57(MediaContext4.Provider, { value: mediaProvider ?? null, children: items.map((item) => {
29372
29776
  const top = positions.get(item.id);
29373
29777
  const hidden = top == null;
29374
29778
  return /* @__PURE__ */ jsxs45(
@@ -29400,7 +29804,7 @@ function InlinePreviewGutter({
29400
29804
  aspectRatio: `${viewport.width} / ${viewport.height}`
29401
29805
  },
29402
29806
  children: /* @__PURE__ */ jsx57(
29403
- BlockRenderer2,
29807
+ BlockRenderer3,
29404
29808
  {
29405
29809
  block: item.block,
29406
29810
  blockTime: 0,
@@ -29755,10 +30159,10 @@ function buildPreviewDoc(doc, options) {
29755
30159
  // src/OutlinePanel.tsx
29756
30160
  import {
29757
30161
  useCallback as useCallback40,
29758
- useEffect as useEffect41,
30162
+ useEffect as useEffect42,
29759
30163
  useMemo as useMemo38,
29760
30164
  useRef as useRef42,
29761
- useState as useState52
30165
+ useState as useState53
29762
30166
  } from "react";
29763
30167
  import { flattenBlocks as flattenBlocks5, hasTemplate as hasTemplate3 } from "@bendyline/squisq/doc";
29764
30168
  import { extractPlainText as extractPlainText5 } from "@bendyline/squisq/markdown";
@@ -29877,8 +30281,8 @@ function OutlinePanel({ width, className, readOnly = false }) {
29877
30281
  const { scrollToBlock } = useHeadingLayout(paneRef);
29878
30282
  const cursorActiveId = useActiveOutlineBlockId();
29879
30283
  const activeDragRef = useRef42(null);
29880
- const [draggedBlockId, setDraggedBlockId] = useState52(null);
29881
- const [dropTarget, setDropTarget] = useState52(null);
30284
+ const [draggedBlockId, setDraggedBlockId] = useState53(null);
30285
+ const [dropTarget, setDropTarget] = useState53(null);
29882
30286
  const blockModeActiveId = useMemo38(() => {
29883
30287
  if (layoutMode !== "block" || activeBlockStartLine == null || !doc) return null;
29884
30288
  const match = flattenBlocks5(doc.blocks).find(
@@ -30145,11 +30549,11 @@ function OutlineNode({
30145
30549
  function useActiveOutlineBlockId() {
30146
30550
  const { doc, activeView, tiptapEditor, monacoEditor } = useEditorContext();
30147
30551
  const flatBlocks = useMemo38(() => doc ? flattenBlocks5(doc.blocks) : [], [doc]);
30148
- const [activeId, setActiveId] = useState52(null);
30149
- useEffect41(() => {
30552
+ const [activeId, setActiveId] = useState53(null);
30553
+ useEffect42(() => {
30150
30554
  setActiveId(null);
30151
30555
  }, [activeView]);
30152
- useEffect41(() => {
30556
+ useEffect42(() => {
30153
30557
  if (activeView !== "wysiwyg" || !tiptapEditor) return;
30154
30558
  const update = () => {
30155
30559
  const { from } = tiptapEditor.state.selection;
@@ -30171,7 +30575,7 @@ function useActiveOutlineBlockId() {
30171
30575
  tiptapEditor.off("update", update);
30172
30576
  };
30173
30577
  }, [activeView, tiptapEditor, flatBlocks]);
30174
- useEffect41(() => {
30578
+ useEffect42(() => {
30175
30579
  if (activeView !== "raw" || !monacoEditor) return;
30176
30580
  const update = () => {
30177
30581
  const line = monacoEditor.getPosition()?.lineNumber;
@@ -30222,7 +30626,7 @@ function bumpHeadingLevelInSource(source, line, delta) {
30222
30626
  }
30223
30627
 
30224
30628
  // src/codeContext/CodeContextZones.tsx
30225
- import { useCallback as useCallback42, useEffect as useEffect43, useMemo as useMemo40, useRef as useRef44, useState as useState53 } from "react";
30629
+ import { useCallback as useCallback42, useEffect as useEffect44, useMemo as useMemo40, useRef as useRef44, useState as useState54 } from "react";
30226
30630
  import { createPortal as createPortal10 } from "react-dom";
30227
30631
 
30228
30632
  // src/codeContext/diffContextSections.ts
@@ -30346,7 +30750,7 @@ function setOrdinal(zone, ordinal) {
30346
30750
  // src/codeContext/CodeContextSectionView.tsx
30347
30751
  import { parseMarkdown as parseMarkdown8 } from "@bendyline/squisq/markdown";
30348
30752
  import { MarkdownRenderer } from "@bendyline/squisq-react";
30349
- import { useCallback as useCallback41, useEffect as useEffect42, useMemo as useMemo39, useRef as useRef43 } from "react";
30753
+ import { useCallback as useCallback41, useEffect as useEffect43, useMemo as useMemo39, useRef as useRef43 } from "react";
30350
30754
  import { jsx as jsx59, jsxs as jsxs47 } from "react/jsx-runtime";
30351
30755
  function CodeContextSectionView({
30352
30756
  section,
@@ -30366,7 +30770,7 @@ function CodeContextSectionView({
30366
30770
  () => expanded && section.markdown ? parseMarkdown8(section.markdown).children : null,
30367
30771
  [expanded, section.markdown]
30368
30772
  );
30369
- useEffect42(() => {
30773
+ useEffect43(() => {
30370
30774
  const el = rootRef.current;
30371
30775
  if (!el || typeof ResizeObserver === "undefined") return;
30372
30776
  const report = () => {
@@ -30438,11 +30842,11 @@ function CodeContextSectionView({
30438
30842
  import { Fragment as Fragment18, jsx as jsx60 } from "react/jsx-runtime";
30439
30843
  function CodeContextZones({ options }) {
30440
30844
  const { monacoEditor } = useEditorContext();
30441
- const [manager, setManager] = useState53(null);
30442
- const [, setZonesVersion] = useState53(0);
30443
- const [expandedById, setExpandedById] = useState53({});
30845
+ const [manager, setManager] = useState54(null);
30846
+ const [, setZonesVersion] = useState54(0);
30847
+ const [expandedById, setExpandedById] = useState54({});
30444
30848
  const seenIds = useRef44(/* @__PURE__ */ new Set());
30445
- useEffect43(() => {
30849
+ useEffect44(() => {
30446
30850
  if (!monacoEditor) return;
30447
30851
  const mgr = new CodeContextZoneManager(monacoEditor);
30448
30852
  const off = mgr.onDidChangeZones(() => setZonesVersion((v2) => v2 + 1));
@@ -30465,7 +30869,7 @@ function CodeContextZones({ options }) {
30465
30869
  });
30466
30870
  return out;
30467
30871
  }, [fileTop, sections]);
30468
- useEffect43(() => {
30872
+ useEffect44(() => {
30469
30873
  if (!manager) return;
30470
30874
  manager.sync(resolved.map((r) => r.spec));
30471
30875
  setExpandedById((prev) => {
@@ -30901,14 +31305,14 @@ function placeClipInBlock(source, fromLine, targetHeadingLine, spec, startAt) {
30901
31305
  }
30902
31306
 
30903
31307
  // src/TimelineTrack.tsx
30904
- import { useCallback as useCallback44, useEffect as useEffect46, useMemo as useMemo41, useRef as useRef47, useState as useState56 } from "react";
31308
+ import { useCallback as useCallback44, useEffect as useEffect47, useMemo as useMemo41, useRef as useRef47, useState as useState57 } from "react";
30905
31309
  import {
30906
31310
  resolveMediaSchedule as resolveMediaSchedule2,
30907
31311
  getDocPlaybackDuration,
30908
31312
  VIEWPORT_PRESETS as VIEWPORT_PRESETS6
30909
31313
  } from "@bendyline/squisq/schemas";
30910
31314
  import { flattenBlocks as flattenBlocks6, DEFAULT_THEME as DEFAULT_THEME6, getPinnedBlockMeta } from "@bendyline/squisq/doc";
30911
- import { MediaClipLayer, MediaContext as MediaContext5 } from "@bendyline/squisq-react";
31315
+ import { MediaClipLayer, MediaContext as MediaContext6 } from "@bendyline/squisq-react";
30912
31316
 
30913
31317
  // src/embeddedMedia.ts
30914
31318
  import { parseTimeSeconds as parseTimeSeconds2 } from "@bendyline/squisq/markdown";
@@ -31028,7 +31432,7 @@ function collectTimelinePlaybackSchedule(doc, scheduled) {
31028
31432
  // src/TimelineBlockPreview.tsx
31029
31433
  import { memo } from "react";
31030
31434
  import { VIEWPORT_PRESETS as VIEWPORT_PRESETS5 } from "@bendyline/squisq/schemas";
31031
- import { BlockRenderer as BlockRenderer3, MediaContext as MediaContext4 } from "@bendyline/squisq-react";
31435
+ import { BlockRenderer as BlockRenderer4, MediaContext as MediaContext5 } from "@bendyline/squisq-react";
31032
31436
  import { jsx as jsx62 } from "react/jsx-runtime";
31033
31437
  var BlockThumbnail = memo(function BlockThumbnail2({
31034
31438
  visual,
@@ -31036,7 +31440,7 @@ var BlockThumbnail = memo(function BlockThumbnail2({
31036
31440
  basePath = "/",
31037
31441
  mediaProvider = null
31038
31442
  }) {
31039
- return /* @__PURE__ */ jsx62(MediaContext4.Provider, { value: mediaProvider, children: /* @__PURE__ */ jsx62(BlockRenderer3, { block: visual, blockTime: 0, basePath, viewport }) });
31443
+ return /* @__PURE__ */ jsx62(MediaContext5.Provider, { value: mediaProvider, children: /* @__PURE__ */ jsx62(BlockRenderer4, { block: visual, blockTime: 0, basePath, viewport }) });
31040
31444
  });
31041
31445
 
31042
31446
  // src/resolveBlockVisual.ts
@@ -31060,7 +31464,7 @@ function resolveBlockVisual(doc, block, theme, viewport) {
31060
31464
  }
31061
31465
 
31062
31466
  // src/useTimelineClock.ts
31063
- import { useCallback as useCallback43, useEffect as useEffect44, useRef as useRef45, useState as useState54 } from "react";
31467
+ import { useCallback as useCallback43, useEffect as useEffect45, useRef as useRef45, useState as useState55 } from "react";
31064
31468
  function advanceTime(prev, dt, total) {
31065
31469
  if (total <= 0) return 0;
31066
31470
  return Math.min(total, Math.max(0, prev + dt));
@@ -31080,8 +31484,8 @@ function playTimelineMediaAt(root, time) {
31080
31484
  });
31081
31485
  }
31082
31486
  function useTimelineClock(total) {
31083
- const [currentTime, setCurrentTime] = useState54(0);
31084
- const [isPlaying, setIsPlaying] = useState54(false);
31487
+ const [currentTime, setCurrentTime] = useState55(0);
31488
+ const [isPlaying, setIsPlaying] = useState55(false);
31085
31489
  const rafRef = useRef45(null);
31086
31490
  const lastRef = useRef45(0);
31087
31491
  const currentTimeRef = useRef45(0);
@@ -31089,10 +31493,10 @@ function useTimelineClock(total) {
31089
31493
  const mediaHostRef = useRef45(null);
31090
31494
  currentTimeRef.current = currentTime;
31091
31495
  isPlayingRef.current = isPlaying;
31092
- useEffect44(() => {
31496
+ useEffect45(() => {
31093
31497
  setCurrentTime((t) => Math.min(t, Math.max(0, total)));
31094
31498
  }, [total]);
31095
- useEffect44(() => {
31499
+ useEffect45(() => {
31096
31500
  if (!isPlaying) return;
31097
31501
  lastRef.current = performance.now();
31098
31502
  const tick = (now) => {
@@ -31154,7 +31558,7 @@ function timelineMediaLabel(src, kind) {
31154
31558
  }
31155
31559
 
31156
31560
  // src/TimelineItemMenu.tsx
31157
- import { useEffect as useEffect45, useLayoutEffect as useLayoutEffect8, useRef as useRef46, useState as useState55 } from "react";
31561
+ import { useEffect as useEffect46, useLayoutEffect as useLayoutEffect9, useRef as useRef46, useState as useState56 } from "react";
31158
31562
  import { createPortal as createPortal11 } from "react-dom";
31159
31563
  import { Fragment as Fragment19, jsx as jsx63, jsxs as jsxs49 } from "react/jsx-runtime";
31160
31564
  function TimelineItemMenu({
@@ -31169,8 +31573,8 @@ function TimelineItemMenu({
31169
31573
  onClose
31170
31574
  }) {
31171
31575
  const panelRef = useRef46(null);
31172
- const [style, setStyle] = useState55(() => menuStyle(anchor));
31173
- useLayoutEffect8(() => {
31576
+ const [style, setStyle] = useState56(() => menuStyle(anchor));
31577
+ useLayoutEffect9(() => {
31174
31578
  const panel = panelRef.current;
31175
31579
  if (!panel) return;
31176
31580
  const updatePosition = () => setStyle(menuStyle(anchor, panel));
@@ -31183,7 +31587,7 @@ function TimelineItemMenu({
31183
31587
  resizeObserver?.disconnect();
31184
31588
  };
31185
31589
  }, [anchor]);
31186
- useEffect45(() => {
31590
+ useEffect46(() => {
31187
31591
  const onKeyDown = (event) => {
31188
31592
  if (event.key !== "Escape") return;
31189
31593
  event.preventDefault();
@@ -31243,12 +31647,12 @@ function BlockMenu({
31243
31647
  onStartTime,
31244
31648
  onTransition
31245
31649
  }) {
31246
- const [explicitDuration, setExplicitDuration] = useState55(target.explicitDuration);
31247
- const [duration, setDuration] = useState55(formatNumber(target.duration));
31248
- const [startTime, setStartTime] = useState55(
31650
+ const [explicitDuration, setExplicitDuration] = useState56(target.explicitDuration);
31651
+ const [duration, setDuration] = useState56(formatNumber(target.duration));
31652
+ const [startTime, setStartTime] = useState56(
31249
31653
  target.startTime == null ? "" : formatNumber(target.startTime)
31250
31654
  );
31251
- const [transition, setTransition] = useState55(target.transition);
31655
+ const [transition, setTransition] = useState56(target.transition);
31252
31656
  const toggleAutotime = (autotimed) => {
31253
31657
  setExplicitDuration(!autotimed);
31254
31658
  if (autotimed) {
@@ -31320,11 +31724,11 @@ function VideoMenu({
31320
31724
  target,
31321
31725
  onPatch
31322
31726
  }) {
31323
- const [placement, setPlacement] = useState55(target.placement);
31324
- const [locked, setLocked] = useState55(target.lockToBlock);
31325
- const [pipSize, setPipSize] = useState55(target.pipSize ?? "");
31326
- const [pipShape, setPipShape] = useState55(target.pipShape ?? "");
31327
- const [pipPosition, setPipPosition] = useState55(target.pipPosition ?? "");
31727
+ const [placement, setPlacement] = useState56(target.placement);
31728
+ const [locked, setLocked] = useState56(target.lockToBlock);
31729
+ const [pipSize, setPipSize] = useState56(target.pipSize ?? "");
31730
+ const [pipShape, setPipShape] = useState56(target.pipShape ?? "");
31731
+ const [pipPosition, setPipPosition] = useState56(target.pipPosition ?? "");
31328
31732
  const placed = placement === "picture-in-picture" || placement === "overlay";
31329
31733
  const placementOptions = [
31330
31734
  target.canUseContentPlacement ? { value: "content", label: "In layout" } : { value: "default", label: "Default" },
@@ -31594,9 +31998,9 @@ function TimelineTrack({
31594
31998
  colorScheme
31595
31999
  } = useEditorContext();
31596
32000
  const doc = docProp ?? contextDoc;
31597
- const [drag, setDrag] = useState56(null);
31598
- const [itemMenu, setItemMenu] = useState56(null);
31599
- const [pxPerSecond, setPxPerSecond] = useState56(DEFAULT_PX_PER_SECOND);
32001
+ const [drag, setDrag] = useState57(null);
32002
+ const [itemMenu, setItemMenu] = useState57(null);
32003
+ const [pxPerSecond, setPxPerSecond] = useState57(DEFAULT_PX_PER_SECOND);
31600
32004
  const scrollRef = useRef47(null);
31601
32005
  const blocks = useMemo41(() => doc ? flattenBlocks6(doc.blocks) : [], [doc]);
31602
32006
  const previewSettings = usePreviewSettingsOptional();
@@ -31629,8 +32033,8 @@ function TimelineTrack({
31629
32033
  setDrag(null);
31630
32034
  play();
31631
32035
  }, [play]);
31632
- const [scrubbing, setScrubbing] = useState56(false);
31633
- useEffect46(() => {
32036
+ const [scrubbing, setScrubbing] = useState57(false);
32037
+ useEffect47(() => {
31634
32038
  if (!scrubbing) return;
31635
32039
  const onMove = (e2) => {
31636
32040
  const scroll = scrollRef.current;
@@ -31668,7 +32072,7 @@ function TimelineTrack({
31668
32072
  [blocks]
31669
32073
  );
31670
32074
  const followedBlockRef = useRef47(null);
31671
- useEffect46(() => {
32075
+ useEffect47(() => {
31672
32076
  if (!isPlaying) {
31673
32077
  followedBlockRef.current = null;
31674
32078
  return;
@@ -31679,7 +32083,7 @@ function TimelineTrack({
31679
32083
  const line = headingLine(block);
31680
32084
  if (line != null) goToBlockByLine(line);
31681
32085
  }, [isPlaying, currentTime, blockAtTime, goToBlockByLine]);
31682
- useEffect46(() => {
32086
+ useEffect47(() => {
31683
32087
  if (!isPlaying) return;
31684
32088
  const scroll = scrollRef.current;
31685
32089
  if (!scroll) return;
@@ -31727,8 +32131,8 @@ function TimelineTrack({
31727
32131
  );
31728
32132
  const zoomIn = useCallback44(() => setPxPerSecond((s) => Math.min(ZOOM_MAX, s * ZOOM_FACTOR)), []);
31729
32133
  const zoomOut = useCallback44(() => setPxPerSecond((s) => Math.max(ZOOM_MIN, s / ZOOM_FACTOR)), []);
31730
- const [viewportWidth, setViewportWidth] = useState56(0);
31731
- useEffect46(() => {
32134
+ const [viewportWidth, setViewportWidth] = useState57(0);
32135
+ useEffect47(() => {
31732
32136
  const el = scrollRef.current;
31733
32137
  if (!el || typeof ResizeObserver === "undefined") return;
31734
32138
  const update = () => setViewportWidth(el.clientWidth);
@@ -31742,7 +32146,7 @@ function TimelineTrack({
31742
32146
  const dragRef = useRef47(null);
31743
32147
  dragRef.current = drag;
31744
32148
  const isDragging = drag != null && !drag.committed;
31745
- useEffect46(() => {
32149
+ useEffect47(() => {
31746
32150
  if (!isDragging) return;
31747
32151
  const onMove = (e2) => {
31748
32152
  const d = dragRef.current;
@@ -31773,7 +32177,7 @@ function TimelineTrack({
31773
32177
  window.removeEventListener("pointerup", onUp);
31774
32178
  };
31775
32179
  }, [isDragging]);
31776
- useEffect46(() => {
32180
+ useEffect47(() => {
31777
32181
  if (dragRef.current?.committed) setDrag(null);
31778
32182
  }, [doc]);
31779
32183
  const beginDrag = useCallback44(
@@ -32245,7 +32649,7 @@ function TimelineTrack({
32245
32649
  }
32246
32650
  )
32247
32651
  ] }) }),
32248
- /* @__PURE__ */ jsx64("div", { ref: activeClock.registerMediaHost, className: "squisq-timeline-media-host", "aria-hidden": true, children: /* @__PURE__ */ jsx64(MediaContext5.Provider, { value: mediaProvider ?? null, children: /* @__PURE__ */ jsx64(
32652
+ /* @__PURE__ */ jsx64("div", { ref: activeClock.registerMediaHost, className: "squisq-timeline-media-host", "aria-hidden": true, children: /* @__PURE__ */ jsx64(MediaContext6.Provider, { value: mediaProvider ?? null, children: /* @__PURE__ */ jsx64(
32249
32653
  MediaClipLayer,
32250
32654
  {
32251
32655
  schedule: playbackClips,
@@ -32378,7 +32782,7 @@ import { getDocPlaybackDuration as getDocPlaybackDuration2, resolveMediaSchedule
32378
32782
  import { DocPlayer } from "@bendyline/squisq-react";
32379
32783
 
32380
32784
  // src/usePreviewProjection.ts
32381
- import { useEffect as useEffect47, useState as useState57 } from "react";
32785
+ import { useEffect as useEffect48, useState as useState58 } from "react";
32382
32786
  import { resolveAudioMapping } from "@bendyline/squisq/doc";
32383
32787
  import { applyTransform } from "@bendyline/squisq/transform";
32384
32788
  function hasEquivalentAudio(left, right) {
@@ -32398,8 +32802,8 @@ function preserveEquivalentAudio(previous, next) {
32398
32802
  };
32399
32803
  }
32400
32804
  function usePreviewProjection(doc, transformStyle, workspaceContainer, documentTitle2) {
32401
- const [projection, setProjection] = useState57(null);
32402
- useEffect47(() => {
32805
+ const [projection, setProjection] = useState58(null);
32806
+ useEffect48(() => {
32403
32807
  if (!doc || !doc.blocks.length) {
32404
32808
  setProjection(null);
32405
32809
  return;
@@ -32422,13 +32826,7 @@ function usePreviewProjection(doc, transformStyle, workspaceContainer, documentT
32422
32826
  if (!cancelled) commit(immediate);
32423
32827
  }
32424
32828
  );
32425
- setProjection((previous) => {
32426
- if (!previous) return immediate;
32427
- return {
32428
- ...immediate,
32429
- playerDoc: { ...immediate.playerDoc, audio: previous.playerDoc.audio }
32430
- };
32431
- });
32829
+ setProjection((previous) => previous ?? immediate);
32432
32830
  return () => {
32433
32831
  cancelled = true;
32434
32832
  };
@@ -32457,7 +32855,10 @@ function TimelineCompositionPanel({
32457
32855
  activePipSize,
32458
32856
  activePipShape,
32459
32857
  activePipPosition,
32460
- activeCoverSlide
32858
+ activeCoverSlide,
32859
+ activeCoverSlideTemplate,
32860
+ activeCoverSlideDuration,
32861
+ activeCoverSlidePlayback
32461
32862
  } = usePreviewSettings();
32462
32863
  const { fileName } = useEditorContext();
32463
32864
  const projection = usePreviewProjection(
@@ -32543,6 +32944,9 @@ function TimelineCompositionPanel({
32543
32944
  captionStyle: activeCaptionStyle,
32544
32945
  captionsEnabled: activeCaptionsEnabled,
32545
32946
  showCoverSlide: activeCoverSlide,
32947
+ coverSlideTemplate: activeCoverSlideTemplate,
32948
+ coverSlideDuration: activeCoverSlideDuration,
32949
+ coverSlidePlayback: activeCoverSlidePlayback,
32546
32950
  enableSwipe: false,
32547
32951
  globalKeyboardShortcuts: false
32548
32952
  }
@@ -32659,7 +33063,7 @@ function TimelineToolbar({
32659
33063
  }
32660
33064
 
32661
33065
  // src/PlainHtmlPreview.tsx
32662
- import { useCallback as useCallback45, useEffect as useEffect48, useMemo as useMemo44, useRef as useRef48, useState as useState58 } from "react";
33066
+ import { useCallback as useCallback45, useEffect as useEffect49, useMemo as useMemo44, useRef as useRef48, useState as useState59 } from "react";
32663
33067
  import { parseMarkdown as parseMarkdown9 } from "@bendyline/squisq/markdown";
32664
33068
 
32665
33069
  // src/utils/collectInlineFontAwesomeCss.ts
@@ -32725,10 +33129,13 @@ function PlainHtmlPreview({
32725
33129
  style,
32726
33130
  globalKeyboardShortcuts = false,
32727
33131
  onFrameChange,
32728
- onLinkClick
33132
+ onLinkClick,
33133
+ showCodeCopyButton = false,
33134
+ onCopyCode
32729
33135
  }) {
32730
33136
  const iframeRef = useRef48(null);
32731
33137
  const removeFrameLinkHandlerRef = useRef48(() => void 0);
33138
+ const removeFrameCodeCopyControlsRef = useRef48(() => void 0);
32732
33139
  const setIframeRef = useCallback45(
32733
33140
  (frame) => {
32734
33141
  iframeRef.current = frame;
@@ -32737,8 +33144,8 @@ function PlainHtmlPreview({
32737
33144
  [onFrameChange]
32738
33145
  );
32739
33146
  const mdDoc = useMemo44(() => parseMarkdown9(markdown), [markdown]);
32740
- const [resolvedImages, setResolvedImages] = useState58(null);
32741
- useEffect48(() => {
33147
+ const [resolvedImages, setResolvedImages] = useState59(null);
33148
+ useEffect49(() => {
32742
33149
  if (!mediaProvider) {
32743
33150
  setResolvedImages(null);
32744
33151
  return;
@@ -32774,8 +33181,8 @@ function PlainHtmlPreview({
32774
33181
  return merged;
32775
33182
  }, [resolvedImages, images]);
32776
33183
  const iconsCss = useMemo44(() => collectInlineFontAwesomeCss(), []);
32777
- const [renderFn, setRenderFn] = useState58(() => cachedRender);
32778
- useEffect48(() => {
33184
+ const [renderFn, setRenderFn] = useState59(() => cachedRender);
33185
+ useEffect49(() => {
32779
33186
  if (renderFn) return;
32780
33187
  let cancelled = false;
32781
33188
  loadRenderFn().then((fn) => {
@@ -32808,11 +33215,107 @@ function PlainHtmlPreview({
32808
33215
  frameDocument.addEventListener("click", handleClick, true);
32809
33216
  removeFrameLinkHandlerRef.current = () => frameDocument.removeEventListener("click", handleClick, true);
32810
33217
  }, [onLinkClick]);
32811
- useEffect48(() => {
33218
+ useEffect49(() => {
32812
33219
  installFrameLinkHandler();
32813
33220
  return () => removeFrameLinkHandlerRef.current();
32814
33221
  }, [html, installFrameLinkHandler]);
32815
- useEffect48(() => {
33222
+ const installFrameCodeCopyControls = useCallback45(() => {
33223
+ removeFrameCodeCopyControlsRef.current();
33224
+ removeFrameCodeCopyControlsRef.current = () => void 0;
33225
+ const frameDocument = iframeRef.current?.contentDocument;
33226
+ if (!frameDocument || !showCodeCopyButton) return;
33227
+ const style2 = frameDocument.createElement("style");
33228
+ style2.dataset.squisqCodeCopy = "";
33229
+ style2.textContent = `
33230
+ .squisq-preview-code-copy-frame { position: relative; margin: 1em 0; }
33231
+ .squisq-preview-code-copy-frame > pre { margin: 0; padding-right: 5rem; }
33232
+ .squisq-preview-code-copy {
33233
+ position: absolute; z-index: 1; top: .55rem; right: .55rem;
33234
+ min-width: 3.7rem; padding: .28rem .5rem;
33235
+ border: 1px solid color-mix(in srgb, var(--plain-text) 20%, transparent);
33236
+ border-radius: 5px;
33237
+ background: color-mix(in srgb, var(--plain-bg) 90%, transparent);
33238
+ color: inherit; font: 500 .72rem/1.2 system-ui, sans-serif;
33239
+ cursor: pointer; opacity: .58;
33240
+ transition: opacity 120ms ease, background-color 120ms ease;
33241
+ }
33242
+ .squisq-preview-code-copy-frame:hover > .squisq-preview-code-copy,
33243
+ .squisq-preview-code-copy:focus-visible,
33244
+ .squisq-preview-code-copy[data-copy-state="copied"],
33245
+ .squisq-preview-code-copy[data-copy-state="failed"] { opacity: 1; }
33246
+ .squisq-preview-code-copy:hover { background: var(--plain-bg); }
33247
+ .squisq-preview-code-copy:disabled { cursor: wait; }
33248
+ `;
33249
+ frameDocument.head.append(style2);
33250
+ const cleanups = [];
33251
+ const wrappers = [];
33252
+ const timers = /* @__PURE__ */ new Set();
33253
+ for (const code of frameDocument.querySelectorAll(
33254
+ "pre.squisq-code-block > code"
33255
+ )) {
33256
+ const pre = code.parentElement;
33257
+ if (!pre?.parentElement) continue;
33258
+ const wrapper = frameDocument.createElement("div");
33259
+ wrapper.className = "squisq-preview-code-copy-frame";
33260
+ pre.replaceWith(wrapper);
33261
+ wrapper.append(pre);
33262
+ wrappers.push(wrapper);
33263
+ const button = frameDocument.createElement("button");
33264
+ button.type = "button";
33265
+ button.className = "squisq-preview-code-copy";
33266
+ button.dataset.copyState = "idle";
33267
+ button.textContent = "Copy";
33268
+ button.setAttribute("aria-label", "Copy code to clipboard");
33269
+ wrapper.append(button);
33270
+ const languageClass = Array.from(code.classList).find((name) => name.startsWith("language-"));
33271
+ const language = languageClass?.slice("language-".length) || void 0;
33272
+ const handleCopy = async () => {
33273
+ if (button.dataset.copyState === "copying") return;
33274
+ button.dataset.copyState = "copying";
33275
+ button.textContent = "Copying\u2026";
33276
+ button.disabled = true;
33277
+ try {
33278
+ if (onCopyCode) {
33279
+ await onCopyCode(code.textContent ?? "", language === void 0 ? {} : { language });
33280
+ } else {
33281
+ const clipboard = frameDocument.defaultView?.navigator.clipboard ?? (typeof navigator === "undefined" ? void 0 : navigator.clipboard);
33282
+ if (!clipboard?.writeText) throw new Error("Clipboard access is unavailable");
33283
+ await clipboard.writeText(code.textContent ?? "");
33284
+ }
33285
+ button.dataset.copyState = "copied";
33286
+ button.textContent = "Copied";
33287
+ } catch {
33288
+ button.dataset.copyState = "failed";
33289
+ button.textContent = "Copy failed";
33290
+ } finally {
33291
+ button.disabled = false;
33292
+ const timer = setTimeout(() => {
33293
+ timers.delete(timer);
33294
+ button.dataset.copyState = "idle";
33295
+ button.textContent = "Copy";
33296
+ }, 1600);
33297
+ timers.add(timer);
33298
+ }
33299
+ };
33300
+ button.addEventListener("click", handleCopy);
33301
+ cleanups.push(() => button.removeEventListener("click", handleCopy));
33302
+ }
33303
+ removeFrameCodeCopyControlsRef.current = () => {
33304
+ for (const cleanup of cleanups) cleanup();
33305
+ for (const timer of timers) clearTimeout(timer);
33306
+ timers.clear();
33307
+ for (const wrapper of wrappers) {
33308
+ const pre = wrapper.firstElementChild;
33309
+ if (pre && wrapper.parentElement) wrapper.replaceWith(pre);
33310
+ }
33311
+ style2.remove();
33312
+ };
33313
+ }, [onCopyCode, showCodeCopyButton]);
33314
+ useEffect49(() => {
33315
+ installFrameCodeCopyControls();
33316
+ return () => removeFrameCodeCopyControlsRef.current();
33317
+ }, [html, installFrameCodeCopyControls]);
33318
+ useEffect49(() => {
32816
33319
  if (!globalKeyboardShortcuts) return;
32817
33320
  const handleKeyDown = (event) => {
32818
33321
  if (event.defaultPrevented || event.altKey || event.ctrlKey || event.metaKey || event.shiftKey || event.key !== "ArrowDown" && event.key !== "ArrowUp") {
@@ -32844,7 +33347,10 @@ function PlainHtmlPreview({
32844
33347
  "data-testid": "plain-html-preview",
32845
33348
  title: title ?? "HTML preview",
32846
33349
  srcDoc: html,
32847
- onLoad: installFrameLinkHandler,
33350
+ onLoad: () => {
33351
+ installFrameLinkHandler();
33352
+ installFrameCodeCopyControls();
33353
+ },
32848
33354
  sandbox: "allow-same-origin",
32849
33355
  style: { ...IFRAME_STYLE, ...style }
32850
33356
  }
@@ -32888,7 +33394,7 @@ function collectImageRefs(doc) {
32888
33394
  }
32889
33395
 
32890
33396
  // src/PreviewPanel.tsx
32891
- import { useState as useState61, useEffect as useEffect52, useMemo as useMemo48, useCallback as useCallback49, useRef as useRef52 } from "react";
33397
+ import { useState as useState62, useEffect as useEffect53, useMemo as useMemo48, useCallback as useCallback49, useRef as useRef52 } from "react";
32892
33398
  import { createPortal as createPortal13 } from "react-dom";
32893
33399
  import { DocPlayer as DocPlayer2, LinearDocView as LinearDocView2, useMediaProvider as useMediaProvider2 } from "@bendyline/squisq-react";
32894
33400
  import { resolveTransformStyle as resolveTransformStyle2 } from "@bendyline/squisq/transform";
@@ -33027,12 +33533,12 @@ import {
33027
33533
  createContext as createContext5,
33028
33534
  useCallback as useCallback46,
33029
33535
  useContext as useContext5,
33030
- useEffect as useEffect49,
33536
+ useEffect as useEffect50,
33031
33537
  useId as useId14,
33032
- useLayoutEffect as useLayoutEffect9,
33538
+ useLayoutEffect as useLayoutEffect10,
33033
33539
  useMemo as useMemo45,
33034
33540
  useRef as useRef49,
33035
- useState as useState59
33541
+ useState as useState60
33036
33542
  } from "react";
33037
33543
  import { createPortal as createPortal12 } from "react-dom";
33038
33544
  import { jsx as jsx69, jsxs as jsxs54 } from "react/jsx-runtime";
@@ -33115,10 +33621,10 @@ function PresentationModeProvider({
33115
33621
  const { activeView, colorScheme, doc } = useEditorContext();
33116
33622
  const { activeTheme } = usePreviewSettings();
33117
33623
  const popupNameId = useId14().replace(/[^a-zA-Z0-9_-]/g, "");
33118
- const [selectedTarget, setSelectedTarget] = useState59("control");
33119
- const [activeTarget, setActiveTarget] = useState59(null);
33120
- const [popupRoot, setPopupRoot] = useState59(null);
33121
- const [error, setError] = useState59(null);
33624
+ const [selectedTarget, setSelectedTarget] = useState60("control");
33625
+ const [activeTarget, setActiveTarget] = useState60(null);
33626
+ const [popupRoot, setPopupRoot] = useState60(null);
33627
+ const [error, setError] = useState60(null);
33122
33628
  const activeTargetRef = useRef49(activeTarget);
33123
33629
  activeTargetRef.current = activeTarget;
33124
33630
  const previousActiveTargetRef = useRef49(null);
@@ -33261,12 +33767,12 @@ function PresentationModeProvider({
33261
33767
  },
33262
33768
  [availableTargets, selectedTarget, stop]
33263
33769
  );
33264
- useEffect49(() => {
33770
+ useEffect50(() => {
33265
33771
  if (availableTargets.includes(selectedTarget)) return;
33266
33772
  setSelectedTarget("control");
33267
33773
  if (activeTargetRef.current !== null) void stop();
33268
33774
  }, [availableTargets, selectedTarget, stop]);
33269
- useEffect49(() => {
33775
+ useEffect50(() => {
33270
33776
  const root = rootRef.current;
33271
33777
  const ownerDocument = root?.ownerDocument;
33272
33778
  if (!root || !ownerDocument) return;
@@ -33278,7 +33784,7 @@ function PresentationModeProvider({
33278
33784
  ownerDocument.addEventListener("fullscreenchange", handleFullscreenChange);
33279
33785
  return () => ownerDocument.removeEventListener("fullscreenchange", handleFullscreenChange);
33280
33786
  }, [rootRef]);
33281
- useEffect49(() => {
33787
+ useEffect50(() => {
33282
33788
  const root = rootRef.current;
33283
33789
  if (!root) return;
33284
33790
  if (activeTarget) root.dataset.presentationMode = activeTarget;
@@ -33287,10 +33793,10 @@ function PresentationModeProvider({
33287
33793
  delete root.dataset.presentationMode;
33288
33794
  };
33289
33795
  }, [activeTarget, rootRef]);
33290
- useEffect49(() => {
33796
+ useEffect50(() => {
33291
33797
  if (activeView !== "preview" && activeTargetRef.current !== null) void stop();
33292
33798
  }, [activeView, stop]);
33293
- useEffect49(() => {
33799
+ useEffect50(() => {
33294
33800
  const previous = previousActiveTargetRef.current;
33295
33801
  previousActiveTargetRef.current = activeTarget;
33296
33802
  if (previous === null || activeTarget !== null) return;
@@ -33298,7 +33804,7 @@ function PresentationModeProvider({
33298
33804
  returnFocusRef.current = null;
33299
33805
  if (target?.isConnected) target.focus();
33300
33806
  }, [activeTarget]);
33301
- useEffect49(() => {
33807
+ useEffect50(() => {
33302
33808
  if (activeTarget !== "control") return;
33303
33809
  const ownerDocument = rootRef.current?.ownerDocument;
33304
33810
  if (!ownerDocument) return;
@@ -33310,12 +33816,12 @@ function PresentationModeProvider({
33310
33816
  ownerDocument.addEventListener("keydown", handleKeyDown);
33311
33817
  return () => ownerDocument.removeEventListener("keydown", handleKeyDown);
33312
33818
  }, [activeTarget, rootRef, stop]);
33313
- useEffect49(() => {
33819
+ useEffect50(() => {
33314
33820
  if (!error) return;
33315
33821
  const timer = window.setTimeout(() => setError(null), 6e3);
33316
33822
  return () => window.clearTimeout(timer);
33317
33823
  }, [error]);
33318
- useEffect49(
33824
+ useEffect50(
33319
33825
  () => () => {
33320
33826
  popupCleanupRef.current?.();
33321
33827
  const popup = popupRef.current;
@@ -33418,8 +33924,8 @@ function PresentationModeControl() {
33418
33924
  const { colorScheme } = useEditorContext();
33419
33925
  const triggerRef = useRef49(null);
33420
33926
  const menuRef = useRef49(null);
33421
- const [open, setOpen] = useState59(false);
33422
- const [anchor, setAnchor] = useState59(null);
33927
+ const [open, setOpen] = useState60(false);
33928
+ const [anchor, setAnchor] = useState60(null);
33423
33929
  const options = PRESENTATION_OPTIONS.filter((option) => availableTargets.includes(option.target));
33424
33930
  const selected = options.find((option) => option.target === selectedTarget) ?? PRESENTATION_OPTIONS[0];
33425
33931
  const updatePosition = useCallback46(() => {
@@ -33444,7 +33950,7 @@ function PresentationModeControl() {
33444
33950
  updatePosition();
33445
33951
  setOpen(true);
33446
33952
  }, [updatePosition]);
33447
- useEffect49(() => {
33953
+ useEffect50(() => {
33448
33954
  if (!open) return;
33449
33955
  const handlePointerDown = (event) => {
33450
33956
  const target = event.target;
@@ -33467,7 +33973,7 @@ function PresentationModeControl() {
33467
33973
  window.removeEventListener("scroll", updatePosition, true);
33468
33974
  };
33469
33975
  }, [closeMenu, open, updatePosition]);
33470
- useLayoutEffect9(() => {
33976
+ useLayoutEffect10(() => {
33471
33977
  if (!open || !anchor) return;
33472
33978
  const checked = menuRef.current?.querySelector('[aria-checked="true"]');
33473
33979
  const first = menuRef.current?.querySelector('[role="menuitemradio"]');
@@ -33587,10 +34093,10 @@ import {
33587
34093
  createContext as createContext6,
33588
34094
  useCallback as useCallback47,
33589
34095
  useContext as useContext6,
33590
- useEffect as useEffect50,
34096
+ useEffect as useEffect51,
33591
34097
  useMemo as useMemo46,
33592
34098
  useRef as useRef50,
33593
- useState as useState60
34099
+ useState as useState61
33594
34100
  } from "react";
33595
34101
  import { jsx as jsx70, jsxs as jsxs55 } from "react/jsx-runtime";
33596
34102
  var PrintModeContext = createContext6(null);
@@ -33605,8 +34111,8 @@ function usePrintModeOptional() {
33605
34111
  function PrintModeProvider({ rootRef, children }) {
33606
34112
  const { activeView } = useEditorContext();
33607
34113
  const { activeTarget: presentationTarget, stop: stopPresentation } = usePresentationMode();
33608
- const [active, setActive] = useState60(false);
33609
- const [slidesPerPage, setSlidesPerPage] = useState60(1);
34114
+ const [active, setActive] = useState61(false);
34115
+ const [slidesPerPage, setSlidesPerPage] = useState61(1);
33610
34116
  const customPrintHandlerRef = useRef50(null);
33611
34117
  const printAncestorsRef = useRef50([]);
33612
34118
  const unmarkPrinting = useCallback47(() => {
@@ -33657,7 +34163,7 @@ function PrintModeProvider({ rootRef, children }) {
33657
34163
  markPrinting();
33658
34164
  ownerWindow.print();
33659
34165
  }, [markPrinting, rootRef]);
33660
- useEffect50(() => {
34166
+ useEffect51(() => {
33661
34167
  const root = rootRef.current;
33662
34168
  if (!root) return;
33663
34169
  if (active) root.dataset.printPreview = "true";
@@ -33666,10 +34172,10 @@ function PrintModeProvider({ rootRef, children }) {
33666
34172
  delete root.dataset.printPreview;
33667
34173
  };
33668
34174
  }, [active, rootRef]);
33669
- useEffect50(() => {
34175
+ useEffect51(() => {
33670
34176
  if (activeView !== "preview" && active) close();
33671
34177
  }, [active, activeView, close]);
33672
- useEffect50(() => {
34178
+ useEffect51(() => {
33673
34179
  if (!active) return;
33674
34180
  const ownerWindow = rootRef.current?.ownerDocument.defaultView;
33675
34181
  const ownerDocument = rootRef.current?.ownerDocument;
@@ -33760,17 +34266,17 @@ function PrintPreviewToolbar() {
33760
34266
  }
33761
34267
 
33762
34268
  // src/print/PrintPreview.tsx
33763
- import { useCallback as useCallback48, useEffect as useEffect51, useMemo as useMemo47, useRef as useRef51 } from "react";
34269
+ import { useCallback as useCallback48, useEffect as useEffect52, useMemo as useMemo47, useRef as useRef51 } from "react";
33764
34270
  import {
33765
- BlockRenderer as BlockRenderer4,
34271
+ BlockRenderer as BlockRenderer5,
33766
34272
  LinearDocView,
33767
34273
  useDocPlayback,
33768
34274
  useMediaProvider
33769
34275
  } from "@bendyline/squisq-react";
33770
34276
  import {
33771
- createTemplateContext
34277
+ createTemplateContext as createTemplateContext2
33772
34278
  } from "@bendyline/squisq/schemas";
33773
- import { expandCoverBlock } from "@bendyline/squisq/doc";
34279
+ import { expandCoverBlock as expandCoverBlock2 } from "@bendyline/squisq/doc";
33774
34280
  import { resolveTransformStyle } from "@bendyline/squisq/transform";
33775
34281
  import { jsx as jsx71, jsxs as jsxs56 } from "react/jsx-runtime";
33776
34282
  function chunk(values, size) {
@@ -33801,17 +34307,17 @@ function PrintPreview({
33801
34307
  const { blocks } = useDocPlayback(previewDoc, 0, { viewport, theme });
33802
34308
  const coverBlock = useMemo47(() => {
33803
34309
  if (!showCover || !previewDoc?.startBlock) return null;
33804
- const context = createTemplateContext(theme, 0, 1, viewport);
34310
+ const context = createTemplateContext2(theme, 0, 1, viewport);
33805
34311
  return {
33806
34312
  id: "cover-block",
33807
34313
  startTime: -1,
33808
34314
  duration: 0,
33809
34315
  audioSegment: -1,
33810
- layers: expandCoverBlock(previewDoc.startBlock, context)
34316
+ layers: expandCoverBlock2(previewDoc.startBlock, context)
33811
34317
  };
33812
34318
  }, [previewDoc?.startBlock, showCover, theme, viewport]);
33813
34319
  const isTextDocument = displayMode === "page" || displayMode === "narrate";
33814
- useEffect51(() => {
34320
+ useEffect52(() => {
33815
34321
  if (!isTextDocument) return registerPrintHandler(null);
33816
34322
  return registerPrintHandler(() => {
33817
34323
  const frameWindow = documentFrameRef.current?.contentWindow;
@@ -33874,7 +34380,7 @@ function PrintPreview({
33874
34380
  "data-slides-per-page": density,
33875
34381
  "aria-label": `Print page ${pageIndex + 1}`,
33876
34382
  children: sheet.map((block) => /* @__PURE__ */ jsx71("div", { className: "squisq-print-slide-cell", children: /* @__PURE__ */ jsx71("div", { className: "squisq-print-slide-frame", children: /* @__PURE__ */ jsx71(
33877
- BlockRenderer4,
34383
+ BlockRenderer5,
33878
34384
  {
33879
34385
  block,
33880
34386
  blockTime: Math.max(0, block.duration),
@@ -33899,7 +34405,9 @@ function PreviewPanel({
33899
34405
  basePath = "/",
33900
34406
  className,
33901
34407
  workspaceContainer,
33902
- onLinkClick
34408
+ onLinkClick,
34409
+ showCodeCopyButton = false,
34410
+ onCopyCode
33903
34411
  }) {
33904
34412
  const {
33905
34413
  doc,
@@ -33928,11 +34436,14 @@ function PreviewPanel({
33928
34436
  activePipShape,
33929
34437
  activePipPosition,
33930
34438
  activeVideoLoop,
33931
- activeCoverSlide
34439
+ activeCoverSlide,
34440
+ activeCoverSlideTemplate,
34441
+ activeCoverSlideDuration,
34442
+ activeCoverSlidePlayback
33932
34443
  } = usePreviewSettings();
33933
34444
  const mainSurfaceRef = useRef52(null);
33934
34445
  const popupSurfaceRef = useRef52(null);
33935
- const [playbackState, setPlaybackState] = useState61(null);
34446
+ const [playbackState, setPlaybackState] = useState62(null);
33936
34447
  const handlePlaybackStateChange = useCallback49((next) => {
33937
34448
  setPlaybackState(next);
33938
34449
  }, []);
@@ -33970,11 +34481,11 @@ function PreviewPanel({
33970
34481
  const isDocumentMode = activeDisplayMode === "page";
33971
34482
  const isPageMode = activeDisplayMode === "linear";
33972
34483
  const isNarrateMode = activeDisplayMode === "narrate";
33973
- useEffect52(() => {
34484
+ useEffect53(() => {
33974
34485
  if (presentation?.activeTarget === "window") return;
33975
34486
  setPlaybackState(null);
33976
34487
  }, [presentation?.activeTarget]);
33977
- useEffect52(() => {
34488
+ useEffect53(() => {
33978
34489
  if (presentation?.activeTarget !== "window" || !presentation.popupRoot) return;
33979
34490
  const mainRoot = mainSurfaceRef.current;
33980
34491
  const followerRoot = popupSurfaceRef.current;
@@ -34078,7 +34589,9 @@ function PreviewPanel({
34078
34589
  mediaRevision,
34079
34590
  theme: activeTheme,
34080
34591
  globalKeyboardShortcuts: !audience,
34081
- onLinkClick
34592
+ onLinkClick,
34593
+ showCodeCopyButton,
34594
+ onCopyCode
34082
34595
  }
34083
34596
  );
34084
34597
  }
@@ -34112,7 +34625,9 @@ function PreviewPanel({
34112
34625
  theme: activeTheme,
34113
34626
  globalKeyboardShortcuts: !audience,
34114
34627
  showCover: activeCoverSlide,
34115
- transformPage: activeTransformStyle ? resolveTransformStyle2(activeTransformStyle).page : void 0
34628
+ transformPage: activeTransformStyle ? resolveTransformStyle2(activeTransformStyle).page : void 0,
34629
+ showCodeCopyButton,
34630
+ onCopyCode
34116
34631
  }
34117
34632
  );
34118
34633
  }
@@ -34139,10 +34654,15 @@ function PreviewPanel({
34139
34654
  captionStyle: audienceCaptionMode === "social" ? "social" : activeCaptionStyle,
34140
34655
  captionsEnabled: audience && audienceCaptionMode ? audienceCaptionMode !== "off" : activeCaptionsEnabled,
34141
34656
  showCoverSlide: activeCoverSlide,
34657
+ coverSlideTemplate: activeCoverSlideTemplate,
34658
+ coverSlideDuration: activeCoverSlideDuration,
34659
+ coverSlidePlayback: activeCoverSlidePlayback,
34142
34660
  coverVisible: audience ? playbackState?.isCoverVisible : void 0,
34143
34661
  audioController: audience ? followerAudioController : void 0,
34144
34662
  enableSwipe: !audience,
34145
34663
  globalKeyboardShortcuts: !audience,
34664
+ showCodeCopyButton,
34665
+ onCopyCode,
34146
34666
  onPlaybackStateChange: !audience && audienceWindowOpen ? handlePlaybackStateChange : void 0
34147
34667
  },
34148
34668
  `${audience ? "audience" : "primary"}-${activeTransformStyle || "none"}`
@@ -34198,7 +34718,7 @@ function PreviewPanel({
34198
34718
  }
34199
34719
 
34200
34720
  // src/MediaBin.tsx
34201
- import { useState as useState62, useEffect as useEffect53, useRef as useRef53, useCallback as useCallback50 } from "react";
34721
+ import { useState as useState63, useEffect as useEffect54, useRef as useRef53, useCallback as useCallback50 } from "react";
34202
34722
  import { jsx as jsx73, jsxs as jsxs58 } from "react/jsx-runtime";
34203
34723
  function formatSize(bytes) {
34204
34724
  if (bytes < 1024) return `${bytes} B`;
@@ -34263,12 +34783,12 @@ function MediaBin({
34263
34783
  isRecorderOpen = false,
34264
34784
  allowBinaryDownloads = true
34265
34785
  }) {
34266
- const [entries, setEntries] = useState62([]);
34267
- const [thumbUrls, setThumbUrls] = useState62({});
34268
- const [loading, setLoading] = useState62(false);
34269
- const [isDropActive, setIsDropActive] = useState62(false);
34270
- const [downloadingPath, setDownloadingPath] = useState62(null);
34271
- const [contextMenu, setContextMenu] = useState62(null);
34786
+ const [entries, setEntries] = useState63([]);
34787
+ const [thumbUrls, setThumbUrls] = useState63({});
34788
+ const [loading, setLoading] = useState63(false);
34789
+ const [isDropActive, setIsDropActive] = useState63(false);
34790
+ const [downloadingPath, setDownloadingPath] = useState63(null);
34791
+ const [contextMenu, setContextMenu] = useState63(null);
34272
34792
  const fileInputRef = useRef53(null);
34273
34793
  const contextMenuRef = useRef53(null);
34274
34794
  const dropDepthRef = useRef53(0);
@@ -34290,7 +34810,7 @@ function MediaBin({
34290
34810
  },
34291
34811
  [onCountChange]
34292
34812
  );
34293
- useEffect53(() => {
34813
+ useEffect54(() => {
34294
34814
  if (!contextMenu) return;
34295
34815
  const handlePointerDown = (event) => {
34296
34816
  if (contextMenuRef.current?.contains(event.target)) return;
@@ -34311,7 +34831,7 @@ function MediaBin({
34311
34831
  window.removeEventListener("resize", close);
34312
34832
  };
34313
34833
  }, [contextMenu]);
34314
- useEffect53(() => {
34834
+ useEffect54(() => {
34315
34835
  if (!mediaProvider) {
34316
34836
  setEntries([]);
34317
34837
  setThumbUrls({});
@@ -34653,7 +35173,7 @@ ${formatSize(entry.size)}`,
34653
35173
  }
34654
35174
 
34655
35175
  // src/DropZoneOverlay.tsx
34656
- import { useState as useState63 } from "react";
35176
+ import { useState as useState64 } from "react";
34657
35177
  import { Fragment as Fragment21, jsx as jsx74, jsxs as jsxs59 } from "react/jsx-runtime";
34658
35178
  function DropZoneOverlay({
34659
35179
  dragContentType,
@@ -34710,7 +35230,7 @@ function DropZone({
34710
35230
  disabled,
34711
35231
  variant
34712
35232
  }) {
34713
- const [isHovering, setIsHovering] = useState63(false);
35233
+ const [isHovering, setIsHovering] = useState64(false);
34714
35234
  const props = zoneProps(target);
34715
35235
  return /* @__PURE__ */ jsxs59(
34716
35236
  "div",
@@ -34754,7 +35274,7 @@ function DropZone({
34754
35274
  }
34755
35275
 
34756
35276
  // src/Tooltip.tsx
34757
- import { useEffect as useEffect54, useLayoutEffect as useLayoutEffect10, useRef as useRef54, useState as useState64 } from "react";
35277
+ import { useEffect as useEffect55, useLayoutEffect as useLayoutEffect11, useRef as useRef54, useState as useState65 } from "react";
34758
35278
  import { createPortal as createPortal14 } from "react-dom";
34759
35279
 
34760
35280
  // src/tooltipPlacement.ts
@@ -34770,12 +35290,12 @@ function clampTooltipLeft(anchorX, tooltipWidth, viewportWidth, edgePadding = ED
34770
35290
  import { jsx as jsx75 } from "react/jsx-runtime";
34771
35291
  var SHOW_DELAY_MS = 180;
34772
35292
  function TooltipLayer() {
34773
- const [state, setState3] = useState64(null);
35293
+ const [state, setState3] = useState65(null);
34774
35294
  const tooltipRef = useRef54(null);
34775
35295
  const timerRef = useRef54(null);
34776
35296
  const currentTargetRef = useRef54(null);
34777
35297
  const visibleRef = useRef54(false);
34778
- useLayoutEffect10(() => {
35298
+ useLayoutEffect11(() => {
34779
35299
  if (!state) return;
34780
35300
  const node2 = tooltipRef.current;
34781
35301
  if (!node2) return;
@@ -34786,7 +35306,7 @@ function TooltipLayer() {
34786
35306
  node2.style.left = `${left}px`;
34787
35307
  node2.style.visibility = "visible";
34788
35308
  }, [state]);
34789
- useEffect54(() => {
35309
+ useEffect55(() => {
34790
35310
  const clearTimer = () => {
34791
35311
  if (timerRef.current) {
34792
35312
  clearTimeout(timerRef.current);
@@ -34872,7 +35392,7 @@ function TooltipLayer() {
34872
35392
  }
34873
35393
 
34874
35394
  // src/EditorShell.tsx
34875
- import { useEffect as useEffect55, useRef as useRef55, useState as useState65, useCallback as useCallback51, useMemo as useMemo50 } from "react";
35395
+ import { useEffect as useEffect56, useRef as useRef55, useState as useState66, useCallback as useCallback51, useMemo as useMemo50 } from "react";
34876
35396
 
34877
35397
  // src/BlockPreviewPanel.tsx
34878
35398
  import { useMemo as useMemo49 } from "react";
@@ -35153,7 +35673,7 @@ import {
35153
35673
  scopeContainer,
35154
35674
  createMediaProviderFromContainer
35155
35675
  } from "@bendyline/squisq/storage";
35156
- import { MediaContext as MediaContext6, useMediaClipDurations } from "@bendyline/squisq-react";
35676
+ import { MediaContext as MediaContext7, useMediaClipDurations } from "@bendyline/squisq-react";
35157
35677
  import { Fragment as Fragment22, jsx as jsx77, jsxs as jsxs60 } from "react/jsx-runtime";
35158
35678
  function EditorShell({
35159
35679
  initialMarkdown = "",
@@ -35164,6 +35684,8 @@ function EditorShell({
35164
35684
  basePath = "/",
35165
35685
  onChange,
35166
35686
  onLinkClick,
35687
+ showCodeCopyButton = false,
35688
+ onCopyCode,
35167
35689
  colorScheme = "light",
35168
35690
  className,
35169
35691
  height = "100vh",
@@ -35177,6 +35699,8 @@ function EditorShell({
35177
35699
  versioningAutoSaveIdleMs,
35178
35700
  onSaveVersion,
35179
35701
  showFilesToggle,
35702
+ showFormattingControls = hostMode !== "chat",
35703
+ showInsertControls = true,
35180
35704
  allowBinaryDownloads = true,
35181
35705
  toolbarSlotLeft,
35182
35706
  toolbarSlotAfterActions,
@@ -35230,7 +35754,7 @@ function EditorShell({
35230
35754
  }, [mediaProvider, effectiveContainer]);
35231
35755
  const filesToggleEnabled = showFilesToggle ?? effectiveMediaProvider !== void 0;
35232
35756
  const effectiveInitialView = hostMode === "chat" || !showPlayTab && initialView === "preview" ? "wysiwyg" : initialView;
35233
- return /* @__PURE__ */ jsx77(MediaContext6.Provider, { value: effectiveMediaProvider ?? null, children: /* @__PURE__ */ jsx77(
35757
+ return /* @__PURE__ */ jsx77(MediaContext7.Provider, { value: effectiveMediaProvider ?? null, children: /* @__PURE__ */ jsx77(
35234
35758
  EditorProvider,
35235
35759
  {
35236
35760
  initialMarkdown,
@@ -35270,6 +35794,8 @@ function EditorShell({
35270
35794
  defaultViewportPreset,
35271
35795
  onChange,
35272
35796
  onLinkClick,
35797
+ showCodeCopyButton,
35798
+ onCopyCode,
35273
35799
  className,
35274
35800
  height,
35275
35801
  minHeight,
@@ -35278,6 +35804,8 @@ function EditorShell({
35278
35804
  mediaProvider: effectiveMediaProvider ?? null,
35279
35805
  workspaceContainer: effectiveContainer,
35280
35806
  filesToggleEnabled,
35807
+ showFormattingControls,
35808
+ showInsertControls,
35281
35809
  allowBinaryDownloads,
35282
35810
  toolbarSlotLeft,
35283
35811
  toolbarSlotAfterActions,
@@ -35340,6 +35868,8 @@ function EditorShellInner({
35340
35868
  defaultViewportPreset,
35341
35869
  onChange,
35342
35870
  onLinkClick,
35871
+ showCodeCopyButton,
35872
+ onCopyCode,
35343
35873
  className,
35344
35874
  height,
35345
35875
  minHeight,
@@ -35348,6 +35878,8 @@ function EditorShellInner({
35348
35878
  mediaProvider,
35349
35879
  workspaceContainer,
35350
35880
  filesToggleEnabled,
35881
+ showFormattingControls,
35882
+ showInsertControls,
35351
35883
  allowBinaryDownloads,
35352
35884
  toolbarSlotLeft,
35353
35885
  toolbarSlotAfterActions,
@@ -35446,13 +35978,13 @@ function EditorShellInner({
35446
35978
  );
35447
35979
  const timelineClock = useTimelineClock(isTimelineMode ? timelineDuration : 0);
35448
35980
  const hasTimelineVideo = timelineVideoSchedule.length > 0;
35449
- const [timelineVideoVisible, setTimelineVideoVisible] = useState65(false);
35450
- const [timelineCompositionVisible, setTimelineCompositionVisible] = useState65(false);
35981
+ const [timelineVideoVisible, setTimelineVideoVisible] = useState66(false);
35982
+ const [timelineCompositionVisible, setTimelineCompositionVisible] = useState66(false);
35451
35983
  const timelinePreviewCount = Number(timelineVideoVisible) + Number(timelineCompositionVisible);
35452
- const [showFiles, setShowFiles] = useState65(false);
35453
- const [mediaRefreshKey, setMediaRefreshKey] = useState65(0);
35454
- const [mediaCount, setMediaCount] = useState65(0);
35455
- const [mediaBinRecorderOpen, setMediaBinRecorderOpen] = useState65(false);
35984
+ const [showFiles, setShowFiles] = useState66(false);
35985
+ const [mediaRefreshKey, setMediaRefreshKey] = useState66(0);
35986
+ const [mediaCount, setMediaCount] = useState66(0);
35987
+ const [mediaBinRecorderOpen, setMediaBinRecorderOpen] = useState66(false);
35456
35988
  const mediaListRefreshKey = mediaRefreshKey + mediaRevision;
35457
35989
  const usedMediaPaths = useMemo50(
35458
35990
  () => collectMediaReferencesFromMarkdown(markdownSource),
@@ -35464,13 +35996,13 @@ function EditorShellInner({
35464
35996
  }
35465
35997
  const imageEditFallbackContainer = imageEditFallbackContainerRef.current;
35466
35998
  const isDark = colorScheme === "dark";
35467
- useEffect55(() => {
35999
+ useEffect56(() => {
35468
36000
  if (!isTimelineMode || !hasTimelineVideo) setTimelineVideoVisible(false);
35469
36001
  }, [isTimelineMode, hasTimelineVideo]);
35470
- useEffect55(() => {
36002
+ useEffect56(() => {
35471
36003
  if (!isTimelineMode || !doc?.blocks.length) setTimelineCompositionVisible(false);
35472
36004
  }, [isTimelineMode, doc]);
35473
- useEffect55(() => {
36005
+ useEffect56(() => {
35474
36006
  if (!mediaProvider) {
35475
36007
  setMediaCount(0);
35476
36008
  return;
@@ -35579,7 +36111,7 @@ ${snippet}` : snippet);
35579
36111
  onDrop: handleFileDrop,
35580
36112
  enabled: !readOnly
35581
36113
  });
35582
- useEffect55(() => {
36114
+ useEffect56(() => {
35583
36115
  onChange?.(markdownSource);
35584
36116
  }, [markdownSource, onChange]);
35585
36117
  const handleShellKeyDown = useCallback51(
@@ -35695,6 +36227,8 @@ ${snippet}` : snippet);
35695
36227
  slotAfterActions: toolbarSlotAfterActions,
35696
36228
  slotRight: toolbarSlotRight,
35697
36229
  showPlayTab,
36230
+ showFormattingControls,
36231
+ showInsertControls,
35698
36232
  hostMode
35699
36233
  }
35700
36234
  ) }),
@@ -35807,7 +36341,9 @@ ${snippet}` : snippet);
35807
36341
  {
35808
36342
  basePath,
35809
36343
  workspaceContainer,
35810
- onLinkClick
36344
+ onLinkClick,
36345
+ showCodeCopyButton,
36346
+ onCopyCode
35811
36347
  }
35812
36348
  )
35813
36349
  ]
@@ -35941,9 +36477,9 @@ function ImageEditModal({
35941
36477
  const parent = container ?? new MemoryContentContainer();
35942
36478
  return scopeContainer(parent, `.imageEdits/${scopedName}`);
35943
36479
  }, [container, relativePath]);
35944
- const [initialSrc, setInitialSrc] = useState65(null);
35945
- const [resolveError, setResolveError] = useState65(null);
35946
- useEffect55(() => {
36480
+ const [initialSrc, setInitialSrc] = useState66(null);
36481
+ const [resolveError, setResolveError] = useState66(null);
36482
+ useEffect56(() => {
35947
36483
  let cancelled = false;
35948
36484
  setInitialSrc(null);
35949
36485
  setResolveError(null);