@bendyline/squisq-editor-react 2.7.3 → 2.8.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.
@@ -384,6 +384,8 @@ function EditorProvider({
384
384
  allowNarrate = true,
385
385
  preserveSourceWrapping = true,
386
386
  fileName,
387
+ saveCoverImageOutput,
388
+ saveDashboardImageOutput,
387
389
  language,
388
390
  findMode: controlledFindMode,
389
391
  onFindModeChange,
@@ -869,6 +871,8 @@ function EditorProvider({
869
871
  fenceRenderers,
870
872
  linkSchemes,
871
873
  fileName,
874
+ saveCoverImageOutput,
875
+ saveDashboardImageOutput,
872
876
  setMarkdownSource,
873
877
  setEditorSource,
874
878
  setLayoutMode,
@@ -931,6 +935,8 @@ function EditorProvider({
931
935
  fenceRenderers,
932
936
  linkSchemes,
933
937
  fileName,
938
+ saveCoverImageOutput,
939
+ saveDashboardImageOutput,
934
940
  setMarkdownSource,
935
941
  setEditorSource,
936
942
  setLayoutMode,
@@ -3289,9 +3295,16 @@ import {
3289
3295
  COVER_SLIDE_TEMPLATE_OPTIONS,
3290
3296
  createTemplateContext,
3291
3297
  expandCoverBlock,
3292
- resolveCoverSlideSettings
3298
+ resolveCoverSlideSettings,
3299
+ DASHBOARD_AUTO_LAYOUT_ID,
3300
+ DASHBOARD_STYLES,
3301
+ listDashboardLayouts,
3302
+ resolveDashboardStyleId
3293
3303
  } from "@bendyline/squisq/doc";
3294
3304
  import { CoverImageExportModal } from "@bendyline/squisq-video-react/cover-image";
3305
+ import {
3306
+ DashboardImageExportModal
3307
+ } from "@bendyline/squisq-video-react/dashboard-image";
3295
3308
 
3296
3309
  // src/transformStyleId.ts
3297
3310
  import { getTransformStyleSummaries } from "@bendyline/squisq/transform";
@@ -3304,7 +3317,12 @@ function resolvePersistedTransformStyleId(value) {
3304
3317
  }
3305
3318
 
3306
3319
  // src/frontmatterSettings.ts
3307
- import { COVER_SLIDE_FRONTMATTER_KEYS, DEFAULT_COVER_SLIDE_SETTINGS } from "@bendyline/squisq/doc";
3320
+ import {
3321
+ COVER_SLIDE_FRONTMATTER_KEYS,
3322
+ DASHBOARD_FRONTMATTER_KEYS,
3323
+ DEFAULT_COVER_SLIDE_SETTINGS,
3324
+ DEFAULT_DASHBOARD_SETTINGS
3325
+ } from "@bendyline/squisq/doc";
3308
3326
  var FRONTMATTER_SETTING_KEYS = {
3309
3327
  theme: { canonical: "squisq-theme", legacy: ["themeId", "theme"] },
3310
3328
  transform: { canonical: "squisq-transform", legacy: "transform-style" },
@@ -3320,7 +3338,10 @@ var FRONTMATTER_SETTING_KEYS = {
3320
3338
  },
3321
3339
  pipSize: { canonical: "squisq-pip-size", legacy: "pip-size" },
3322
3340
  pipShape: { canonical: "squisq-pip-shape", legacy: "pip-shape" },
3323
- pipPosition: { canonical: "squisq-pip-position", legacy: "pip-position" }
3341
+ pipPosition: { canonical: "squisq-pip-position", legacy: "pip-position" },
3342
+ dashboardLayout: DASHBOARD_FRONTMATTER_KEYS.layout,
3343
+ dashboardTitle: DASHBOARD_FRONTMATTER_KEYS.showTitle,
3344
+ dashboardStyle: DASHBOARD_FRONTMATTER_KEYS.style
3324
3345
  };
3325
3346
  var FRONTMATTER_SETTING_DEFAULTS = {
3326
3347
  theme: "standard",
@@ -3334,7 +3355,10 @@ var FRONTMATTER_SETTING_DEFAULTS = {
3334
3355
  videoPresentation: "background",
3335
3356
  pipSize: "small",
3336
3357
  pipShape: "square",
3337
- pipPosition: "bottom-right"
3358
+ pipPosition: "bottom-right",
3359
+ dashboardLayout: DEFAULT_DASHBOARD_SETTINGS.layout,
3360
+ dashboardTitle: DEFAULT_DASHBOARD_SETTINGS.showTitle,
3361
+ dashboardStyle: DEFAULT_DASHBOARD_SETTINGS.style
3338
3362
  };
3339
3363
  function omitFrontmatterDefault(value, defaultValue) {
3340
3364
  return value === defaultValue ? null : value;
@@ -3375,6 +3399,7 @@ function resolveDisplayMode(value) {
3375
3399
  if (v2 === "video" || v2 === "slideshow" || v2 === "linear" || v2 === "narrate") return v2;
3376
3400
  if (v2 === "slides" || v2 === "presentation" || v2 === "deck") return "slideshow";
3377
3401
  if (v2 === "teleprompter" || v2 === "prompter") return "narrate";
3402
+ if (v2 === "dashboard" || v2 === "dash") return "dashboard";
3378
3403
  if (v2 === "page" || v2 === "paged") return "linear";
3379
3404
  if (v2 === "document" || v2 === "scroll" || v2 === "html" || v2 === "plain" || v2 === "reader") {
3380
3405
  return "page";
@@ -3451,6 +3476,13 @@ function resolveFrontmatterBoolean(value) {
3451
3476
  if (v2 === "false" || v2 === "no" || v2 === "off" || v2 === "hide" || v2 === "hidden") return false;
3452
3477
  return null;
3453
3478
  }
3479
+ function resolveDashboardLayoutId(value) {
3480
+ if (typeof value !== "string") return null;
3481
+ const normalized = value.trim().toLowerCase();
3482
+ if (!normalized) return null;
3483
+ if (normalized === "auto" || normalized === "default") return DASHBOARD_AUTO_LAYOUT_ID;
3484
+ return normalized;
3485
+ }
3454
3486
  function readFrontmatterKey(fm, canonical, legacy) {
3455
3487
  if (!fm) return void 0;
3456
3488
  return Object.prototype.hasOwnProperty.call(fm, canonical) ? fm[canonical] : fm[legacy];
@@ -3499,7 +3531,7 @@ function PreviewSettingsProvider({
3499
3531
  );
3500
3532
  const [selectedPreset, setSelectedPreset] = useState9(null);
3501
3533
  useEffect8(() => setSelectedPreset(null), [fmPreset]);
3502
- const playbackDefaultPreset = activeDisplayMode === "slideshow" || activeDisplayMode === "video" ? defaultViewportPreset : "landscape";
3534
+ const playbackDefaultPreset = activeDisplayMode === "slideshow" || activeDisplayMode === "video" || activeDisplayMode === "dashboard" ? defaultViewportPreset : "landscape";
3503
3535
  const activePreset = selectedPreset ?? fmPreset ?? playbackDefaultPreset;
3504
3536
  const activeViewport = VIEWPORT_PRESETS[activePreset];
3505
3537
  const custom = useCustomThemes();
@@ -3879,6 +3911,92 @@ function PreviewSettingsProvider({
3879
3911
  } : null,
3880
3912
  [designer.open, designer.editing, handleDesignerSave, closeThemeDesigner]
3881
3913
  );
3914
+ const fmDashboardLayout = useMemo8(
3915
+ () => resolveDashboardLayoutId(
3916
+ readFrontmatterKey(
3917
+ frontmatter,
3918
+ FRONTMATTER_SETTING_KEYS.dashboardLayout.canonical,
3919
+ FRONTMATTER_SETTING_KEYS.dashboardLayout.legacy
3920
+ )
3921
+ ),
3922
+ [frontmatter]
3923
+ );
3924
+ const [selectedDashboardLayout, setSelectedDashboardLayout] = useState9(null);
3925
+ useEffect8(() => setSelectedDashboardLayout(null), [fmDashboardLayout]);
3926
+ const activeDashboardLayout = selectedDashboardLayout ?? fmDashboardLayout ?? FRONTMATTER_SETTING_DEFAULTS.dashboardLayout;
3927
+ const handleSetDashboardLayout = useCallback9(
3928
+ (id) => {
3929
+ const next = resolveDashboardLayoutId(id) ?? FRONTMATTER_SETTING_DEFAULTS.dashboardLayout;
3930
+ setSelectedDashboardLayout(next);
3931
+ persistFrontmatter({
3932
+ [FRONTMATTER_SETTING_KEYS.dashboardLayout.canonical]: omitFrontmatterDefault(
3933
+ next,
3934
+ FRONTMATTER_SETTING_DEFAULTS.dashboardLayout
3935
+ ),
3936
+ [FRONTMATTER_SETTING_KEYS.dashboardLayout.legacy]: null
3937
+ });
3938
+ },
3939
+ [persistFrontmatter]
3940
+ );
3941
+ const fmDashboardTitle = useMemo8(
3942
+ () => resolveFrontmatterBoolean(
3943
+ readFrontmatterKey(
3944
+ frontmatter,
3945
+ FRONTMATTER_SETTING_KEYS.dashboardTitle.canonical,
3946
+ FRONTMATTER_SETTING_KEYS.dashboardTitle.legacy
3947
+ )
3948
+ ),
3949
+ [frontmatter]
3950
+ );
3951
+ const [selectedDashboardTitle, setSelectedDashboardTitle] = useState9(null);
3952
+ useEffect8(() => setSelectedDashboardTitle(null), [fmDashboardTitle]);
3953
+ const activeDashboardTitle = selectedDashboardTitle ?? fmDashboardTitle ?? FRONTMATTER_SETTING_DEFAULTS.dashboardTitle;
3954
+ const handleSetDashboardTitleEnabled = useCallback9(
3955
+ (enabled) => {
3956
+ setSelectedDashboardTitle(enabled);
3957
+ persistFrontmatter({
3958
+ [FRONTMATTER_SETTING_KEYS.dashboardTitle.canonical]: omitFrontmatterDefault(
3959
+ enabled,
3960
+ FRONTMATTER_SETTING_DEFAULTS.dashboardTitle
3961
+ ),
3962
+ [FRONTMATTER_SETTING_KEYS.dashboardTitle.legacy]: null
3963
+ });
3964
+ },
3965
+ [persistFrontmatter]
3966
+ );
3967
+ const fmDashboardStyle = useMemo8(
3968
+ () => resolveDashboardStyleId(
3969
+ readFrontmatterKey(
3970
+ frontmatter,
3971
+ FRONTMATTER_SETTING_KEYS.dashboardStyle.canonical,
3972
+ FRONTMATTER_SETTING_KEYS.dashboardStyle.legacy
3973
+ )
3974
+ ),
3975
+ [frontmatter]
3976
+ );
3977
+ const [selectedDashboardStyle, setSelectedDashboardStyle] = useState9(
3978
+ null
3979
+ );
3980
+ useEffect8(() => setSelectedDashboardStyle(null), [fmDashboardStyle]);
3981
+ const activeDashboardStyle = selectedDashboardStyle ?? fmDashboardStyle ?? FRONTMATTER_SETTING_DEFAULTS.dashboardStyle;
3982
+ const handleSetDashboardStyle = useCallback9(
3983
+ (id) => {
3984
+ const next = resolveDashboardStyleId(id) ?? FRONTMATTER_SETTING_DEFAULTS.dashboardStyle;
3985
+ setSelectedDashboardStyle(next);
3986
+ persistFrontmatter({
3987
+ [FRONTMATTER_SETTING_KEYS.dashboardStyle.canonical]: omitFrontmatterDefault(
3988
+ next,
3989
+ FRONTMATTER_SETTING_DEFAULTS.dashboardStyle
3990
+ ),
3991
+ [FRONTMATTER_SETTING_KEYS.dashboardStyle.legacy]: null
3992
+ });
3993
+ },
3994
+ [persistFrontmatter]
3995
+ );
3996
+ const dashboardLayoutOptions = useMemo8(
3997
+ () => listDashboardLayouts({ frontmatter }),
3998
+ [frontmatter]
3999
+ );
3882
4000
  const value = useMemo8(
3883
4001
  () => ({
3884
4002
  activePreset,
@@ -3905,6 +4023,13 @@ function PreviewSettingsProvider({
3905
4023
  setPipPosition: handlePipPosition,
3906
4024
  activeVideoLoop,
3907
4025
  setVideoLoopEnabled: handleSetVideoLoopEnabled,
4026
+ activeDashboardLayout,
4027
+ setDashboardLayout: handleSetDashboardLayout,
4028
+ activeDashboardTitle,
4029
+ setDashboardTitleEnabled: handleSetDashboardTitleEnabled,
4030
+ activeDashboardStyle,
4031
+ setDashboardStyle: handleSetDashboardStyle,
4032
+ dashboardLayoutOptions,
3908
4033
  activeCoverSlide,
3909
4034
  setCoverSlideEnabled: handleSetCoverSlideEnabled,
3910
4035
  activeCoverSlideTemplate,
@@ -3934,6 +4059,10 @@ function PreviewSettingsProvider({
3934
4059
  activePipShape,
3935
4060
  activePipPosition,
3936
4061
  activeVideoLoop,
4062
+ activeDashboardLayout,
4063
+ activeDashboardTitle,
4064
+ activeDashboardStyle,
4065
+ dashboardLayoutOptions,
3937
4066
  activeCoverSlide,
3938
4067
  activeCoverSlideTemplate,
3939
4068
  activeCoverSlideDuration,
@@ -3946,6 +4075,9 @@ function PreviewSettingsProvider({
3946
4075
  handlePipShape,
3947
4076
  handlePipPosition,
3948
4077
  handleSetVideoLoopEnabled,
4078
+ handleSetDashboardLayout,
4079
+ handleSetDashboardTitleEnabled,
4080
+ handleSetDashboardStyle,
3949
4081
  handleSetCoverSlideEnabled,
3950
4082
  handleSetCoverSlideTemplate,
3951
4083
  handleSetCoverSlideDuration,
@@ -4019,6 +4151,12 @@ var DISPLAY_MODE_OPTIONS = [
4019
4151
  icon: "fa-solid fa-window-maximize",
4020
4152
  summary: "Scroll through the fully designed page."
4021
4153
  },
4154
+ {
4155
+ key: "dashboard",
4156
+ label: "Dashboard",
4157
+ icon: "fa-solid fa-table-cells-large",
4158
+ summary: "See every block arranged on one canvas."
4159
+ },
4022
4160
  {
4023
4161
  key: "page",
4024
4162
  label: "Document",
@@ -4042,15 +4180,20 @@ var TRANSFORM_STYLE_OPTIONS = [
4042
4180
  var SUMMARIZE_TOOLTIP = "Extract and summarize content for presentation with these Use modes. Your underlying content is not changed.";
4043
4181
  var CONTROL_KEYS = [
4044
4182
  "format",
4183
+ "layout",
4045
4184
  "video",
4046
4185
  "theme",
4047
4186
  "transform",
4048
4187
  "captions",
4049
4188
  "loop",
4050
- "cover"
4189
+ "cover",
4190
+ "dashboardImage"
4051
4191
  ];
4052
4192
  function controlKeysForMode(displayMode, hasVideoMedia) {
4053
- let keys = CONTROL_KEYS;
4193
+ if (displayMode === "dashboard") {
4194
+ return ["format", "layout", "theme", "transform", "dashboardImage"];
4195
+ }
4196
+ let keys = CONTROL_KEYS.filter((key) => key !== "layout" && key !== "dashboardImage");
4054
4197
  if (displayMode !== "video") {
4055
4198
  keys = keys.filter((key) => key !== "loop");
4056
4199
  }
@@ -4097,9 +4240,54 @@ var selectStyle = {
4097
4240
  var COVER_MENU_WIDTH = 336;
4098
4241
  var COVER_MENU_GAP = 4;
4099
4242
  var COVER_MENU_MARGIN = 8;
4243
+ var DASHBOARD_EXPORT_RESOLUTION_BY_PRESET = {
4244
+ landscape: "fhd",
4245
+ portrait: "portrait",
4246
+ square: "square",
4247
+ standard: "standard"
4248
+ };
4249
+ function DashboardImageExportControl({ compact }) {
4250
+ const settings = usePreviewSettings();
4251
+ const { colorScheme, doc, fileName, mediaProvider, saveDashboardImageOutput } = useEditorContext();
4252
+ const [exportOpen, setExportOpen] = useState9(false);
4253
+ return /* @__PURE__ */ jsxs6("div", { className: `squisq-preview-control${compact ? " squisq-preview-control--compact" : ""}`, children: [
4254
+ compact && /* @__PURE__ */ jsx8("label", { style: labelStyle, children: "Export:" }),
4255
+ /* @__PURE__ */ jsx8(
4256
+ "button",
4257
+ {
4258
+ type: "button",
4259
+ className: "squisq-toolbar-button",
4260
+ disabled: !doc,
4261
+ onClick: () => setExportOpen(true),
4262
+ "aria-label": "Export dashboard as image",
4263
+ title: "Export dashboard as image",
4264
+ children: /* @__PURE__ */ jsx8(Icon, { icon: "fa-solid fa-file-image" })
4265
+ }
4266
+ ),
4267
+ exportOpen && doc && createPortal2(
4268
+ /* @__PURE__ */ jsx8(
4269
+ DashboardImageExportModal,
4270
+ {
4271
+ doc,
4272
+ mediaProvider,
4273
+ theme: settings.activeTheme,
4274
+ defaultResolution: DASHBOARD_EXPORT_RESOLUTION_BY_PRESET[settings.activePreset],
4275
+ defaultLayout: settings.activeDashboardLayout,
4276
+ defaultShowTitle: settings.activeDashboardTitle,
4277
+ defaultStyle: settings.activeDashboardStyle,
4278
+ defaultFileName: fileName,
4279
+ colorScheme,
4280
+ saveOutput: saveDashboardImageOutput,
4281
+ onClose: () => setExportOpen(false)
4282
+ }
4283
+ ),
4284
+ document.body
4285
+ )
4286
+ ] });
4287
+ }
4100
4288
  function CoverSlideMenuControl({ compact }) {
4101
4289
  const settings = usePreviewSettings();
4102
- const { colorScheme, doc, fileName, mediaProvider } = useEditorContext();
4290
+ const { colorScheme, doc, fileName, mediaProvider, saveCoverImageOutput } = useEditorContext();
4103
4291
  const triggerRef = useRef7(null);
4104
4292
  const menuRef = useRef7(null);
4105
4293
  const [open, setOpen] = useState9(false);
@@ -4361,6 +4549,7 @@ function CoverSlideMenuControl({ compact }) {
4361
4549
  defaultHeight: settings.activeViewport.height,
4362
4550
  defaultFileName: fileName,
4363
4551
  colorScheme,
4552
+ saveOutput: saveCoverImageOutput,
4364
4553
  onClose: () => setExportOpen(false)
4365
4554
  }
4366
4555
  ),
@@ -4472,6 +4661,56 @@ function PreviewToolbarControls({ displayMode } = {}) {
4472
4661
  },
4473
4662
  "format"
4474
4663
  );
4664
+ case "layout": {
4665
+ const builtIns = s.dashboardLayoutOptions.filter((option) => !option.custom);
4666
+ const customs = s.dashboardLayoutOptions.filter((option) => option.custom);
4667
+ return /* @__PURE__ */ jsxs6(
4668
+ "div",
4669
+ {
4670
+ className: `squisq-preview-control${compact ? " squisq-preview-control--compact" : ""}`,
4671
+ children: [
4672
+ /* @__PURE__ */ jsx8("label", { style: labelStyle, children: "Layout:" }),
4673
+ /* @__PURE__ */ jsxs6(
4674
+ "select",
4675
+ {
4676
+ "aria-label": "Dashboard layout",
4677
+ style: selectStyle,
4678
+ value: s.activeDashboardLayout,
4679
+ onChange: (event) => s.setDashboardLayout(event.target.value),
4680
+ children: [
4681
+ /* @__PURE__ */ jsx8("option", { value: DASHBOARD_AUTO_LAYOUT_ID, children: "Auto" }),
4682
+ builtIns.map((option) => /* @__PURE__ */ jsx8("option", { value: option.id, children: `${option.label} (${option.capacity})` }, option.id)),
4683
+ customs.length > 0 && /* @__PURE__ */ jsx8("optgroup", { label: "Custom", children: customs.map((option) => /* @__PURE__ */ jsx8("option", { value: option.id, children: `${option.label} (${option.capacity})` }, option.id)) })
4684
+ ]
4685
+ }
4686
+ ),
4687
+ /* @__PURE__ */ jsx8("label", { style: labelStyle, children: "Style:" }),
4688
+ /* @__PURE__ */ jsx8(
4689
+ "select",
4690
+ {
4691
+ "aria-label": "Dashboard cell style",
4692
+ style: selectStyle,
4693
+ value: s.activeDashboardStyle,
4694
+ onChange: (event) => s.setDashboardStyle(event.target.value),
4695
+ children: DASHBOARD_STYLES.map((option) => /* @__PURE__ */ jsx8("option", { value: option.id, title: option.description, children: option.label }, option.id))
4696
+ }
4697
+ ),
4698
+ /* @__PURE__ */ jsxs6("label", { className: "squisq-preview-checkbox", children: [
4699
+ /* @__PURE__ */ jsx8(
4700
+ "input",
4701
+ {
4702
+ type: "checkbox",
4703
+ checked: s.activeDashboardTitle,
4704
+ onChange: (event) => s.setDashboardTitleEnabled(event.target.checked)
4705
+ }
4706
+ ),
4707
+ /* @__PURE__ */ jsx8("span", { children: "Title" })
4708
+ ] })
4709
+ ]
4710
+ },
4711
+ "layout"
4712
+ );
4713
+ }
4475
4714
  case "video": {
4476
4715
  const showPipOptions = s.activeVideoPresentation === "picture-in-picture";
4477
4716
  return /* @__PURE__ */ jsxs6(
@@ -4663,6 +4902,8 @@ function PreviewToolbarControls({ displayMode } = {}) {
4663
4902
  );
4664
4903
  case "cover":
4665
4904
  return /* @__PURE__ */ jsx8(CoverSlideMenuControl, { compact }, "cover");
4905
+ case "dashboardImage":
4906
+ return /* @__PURE__ */ jsx8(DashboardImageExportControl, { compact }, "dashboardImage");
4666
4907
  }
4667
4908
  };
4668
4909
  const hasOverflow = visibleCount < controlKeys.length;
@@ -33341,7 +33582,7 @@ function collectImageRefs(doc) {
33341
33582
  // src/PreviewPanel.tsx
33342
33583
  import { useState as useState64, useEffect as useEffect55, useMemo as useMemo50, useCallback as useCallback49, useRef as useRef52 } from "react";
33343
33584
  import { createPortal as createPortal13 } from "react-dom";
33344
- import { DocPlayer as DocPlayer2, LinearDocView as LinearDocView2, useMediaProvider as useMediaProvider2 } from "@bendyline/squisq-react";
33585
+ import { DashboardView, DocPlayer as DocPlayer2, LinearDocView as LinearDocView2, useMediaProvider as useMediaProvider2 } from "@bendyline/squisq-react";
33345
33586
  import { resolveTransformStyle as resolveTransformStyle2 } from "@bendyline/squisq/transform";
33346
33587
 
33347
33588
  // src/buildDocumentPreviewMarkdown.ts
@@ -34409,7 +34650,10 @@ function PreviewPanel({
34409
34650
  activeCoverSlide,
34410
34651
  activeCoverSlideTemplate,
34411
34652
  activeCoverSlideDuration,
34412
- activeCoverSlidePlayback
34653
+ activeCoverSlidePlayback,
34654
+ activeDashboardLayout,
34655
+ activeDashboardTitle,
34656
+ activeDashboardStyle
34413
34657
  } = usePreviewSettings();
34414
34658
  const mainSurfaceRef = useRef52(null);
34415
34659
  const popupSurfaceRef = useRef52(null);
@@ -34451,6 +34695,7 @@ function PreviewPanel({
34451
34695
  const isDocumentMode = activeDisplayMode === "page";
34452
34696
  const isPageMode = activeDisplayMode === "linear";
34453
34697
  const isNarrateMode = activeDisplayMode === "narrate";
34698
+ const isDashboardMode = activeDisplayMode === "dashboard";
34454
34699
  useEffect55(() => {
34455
34700
  if (presentation?.activeTarget === "window") return;
34456
34701
  setPlaybackState(null);
@@ -34602,6 +34847,21 @@ function PreviewPanel({
34602
34847
  }
34603
34848
  );
34604
34849
  }
34850
+ if (isDashboardMode) {
34851
+ return /* @__PURE__ */ jsx73("div", { className: "squisq-dashboard-fit", children: /* @__PURE__ */ jsx73(
34852
+ DashboardView,
34853
+ {
34854
+ doc: contentDoc ?? doc,
34855
+ basePath,
34856
+ viewport: activeViewport,
34857
+ theme: activeTheme,
34858
+ layout: activeDashboardLayout,
34859
+ showTitle: activeDashboardTitle,
34860
+ style: activeDashboardStyle,
34861
+ documentTitle: documentTitleFromFileName(fileName)
34862
+ }
34863
+ ) });
34864
+ }
34605
34865
  if (audience && !followerAudioController) {
34606
34866
  return /* @__PURE__ */ jsx73("div", { className: "squisq-presentation-connecting", children: "Connecting presentation..." });
34607
34867
  }
@@ -35690,6 +35950,8 @@ function EditorShell({
35690
35950
  showStatusBar = true,
35691
35951
  imageDisplayMode = "inline",
35692
35952
  fileName,
35953
+ saveCoverImageOutput,
35954
+ saveDashboardImageOutput,
35693
35955
  language,
35694
35956
  findMode,
35695
35957
  onFindModeChange,
@@ -35749,6 +36011,8 @@ function EditorShell({
35749
36011
  allowNarrate,
35750
36012
  preserveSourceWrapping,
35751
36013
  fileName,
36014
+ saveCoverImageOutput,
36015
+ saveDashboardImageOutput,
35752
36016
  language,
35753
36017
  findMode,
35754
36018
  onFindModeChange,
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { C as CodeContext, E as EditorHostMode, S as SceneTextChannel } from './shell-595XbANu.js';
2
- export { B as BlockTagVisibility, a as CodeContextSection, D as DocumentLinkCandidate, b as DocumentLinkProvider, c as EditorActions, d as EditorColorScheme, e as EditorContextValue, f as EditorMode, g as EditorProvider, h as EditorProviderProps, i as EditorShell, j as EditorShellProps, k as EditorState, l as EditorView, I as ImageDisplayMode, L as LayoutMode, M as MentionCandidate, m as MentionProvider, P as PreviewPanel, n as PreviewPanelProps, R as RawEditor, o as RawEditorProps, T as ThemeInheritance, V as ViewPreferences, W as WriteCanvasSettings, p as WysiwygEditor, q as WysiwygEditorProps, u as useEditorContext } from './shell-595XbANu.js';
1
+ import { C as CodeContext, E as EditorHostMode, S as SceneTextChannel } from './shell-Bv6neeA6.js';
2
+ export { B as BlockTagVisibility, a as CodeContextSection, b as CoverImageSaveOutput, D as DashboardImageSaveOutput, c as DocumentLinkCandidate, d as DocumentLinkProvider, e as EditorActions, f as EditorColorScheme, g as EditorContextValue, h as EditorMode, i as EditorProvider, j as EditorProviderProps, k as EditorShell, l as EditorShellProps, m as EditorState, n as EditorView, I as ImageDisplayMode, L as LayoutMode, M as MentionCandidate, o as MentionProvider, P as PreviewPanel, p as PreviewPanelProps, R as RawEditor, q as RawEditorProps, T as ThemeInheritance, V as ViewPreferences, W as WriteCanvasSettings, r as WysiwygEditor, s as WysiwygEditorProps, u as useEditorContext } from './shell-Bv6neeA6.js';
3
3
  import * as react_jsx_runtime from 'react/jsx-runtime';
4
4
  import * as react from 'react';
5
5
  import { ReactNode, CSSProperties } from 'react';
@@ -8,7 +8,7 @@ import { ContentContainer } from '@bendyline/squisq/storage';
8
8
  export { CanvasRect, ImageEditor, ImageEditorAction, ImageEditorProps, ImageEditorState, ImageEditorTool, ImageViewer, ImageViewerProps, UseImageEditorOptions, UseImageEditorReturn, imageEditorReducer, initialImageEditorState, useImageEditor } from './image-editor/index.js';
9
9
  import { CodeBlockCopyHandler, DisplayMode, CaptionStyle, VideoPresentation, PipSize, PipShape, PipPosition } from '@bendyline/squisq-react';
10
10
  import { IconFamily } from '@bendyline/squisq/icons';
11
- import { CoverSlideTemplate, CoverSlidePlayback, ConnectorRouting, AsciiDiagram, Tree, AsciiTimeline, AsciiTimelineSide, AsciiTimelineMarker } from '@bendyline/squisq/doc';
11
+ import { CoverSlideTemplate, CoverSlidePlayback, DashboardStyleId, DashboardLayoutSummary, ConnectorRouting, AsciiDiagram, Tree, AsciiTimeline, AsciiTimelineSide, AsciiTimelineMarker } from '@bendyline/squisq/doc';
12
12
  export { BuildPreviewDocOptions, buildPreviewDoc, documentTitleFromFileName } from '@bendyline/squisq/doc';
13
13
  import { MarkdownWrapState } from '@bendyline/squisq/markdown';
14
14
  import * as monaco_editor from 'monaco-editor';
@@ -842,6 +842,20 @@ interface PreviewSettings {
842
842
  activeVideoLoop: boolean;
843
843
  /** Enable/disable automatic Video-mode playback restart. */
844
844
  setVideoLoopEnabled: (enabled: boolean) => void;
845
+ /** Dashboard layout id, or `'auto'` for the block-count auto-pick. */
846
+ activeDashboardLayout: string;
847
+ /** Set (and persist) the dashboard layout; null resets to auto. */
848
+ setDashboardLayout: (id: string | null) => void;
849
+ /** Whether the dashboard renders the document-title band. */
850
+ activeDashboardTitle: boolean;
851
+ /** Enable/disable (and persist) the dashboard title band. */
852
+ setDashboardTitleEnabled: (enabled: boolean) => void;
853
+ /** Dashboard cell style variant — the dressing axis beside the layout. */
854
+ activeDashboardStyle: DashboardStyleId;
855
+ /** Set (and persist) the dashboard cell style; null resets to `basic`. */
856
+ setDashboardStyle: (id: string | null) => void;
857
+ /** Layouts available to the doc: customs first, then built-ins. */
858
+ dashboardLayoutOptions: DashboardLayoutSummary[];
845
859
  /** User-authored themes (doc + browser library) for the picker's "Custom" group. */
846
860
  customThemes: Theme[];
847
861
  /** Open the custom-theme designer for a theme (or null to create a new one). */
package/dist/index.js CHANGED
@@ -205,7 +205,7 @@ import {
205
205
  usePreviewSettings,
206
206
  useTimelineData,
207
207
  useTreeViewData
208
- } from "./chunk-7K4LSSNL.js";
208
+ } from "./chunk-SUXRSZ4Y.js";
209
209
  import "./chunk-V4NBQF5C.js";
210
210
  import {
211
211
  JsonEditor
@@ -1,4 +1,4 @@
1
- export { B as BlockTagVisibility, D as DocumentLinkCandidate, b as DocumentLinkProvider, c as EditorActions, d as EditorColorScheme, e as EditorContextValue, E as EditorHostMode, f as EditorMode, g as EditorProvider, h as EditorProviderProps, i as EditorShell, j as EditorShellProps, k as EditorState, l as EditorView, I as ImageDisplayMode, L as LayoutMode, M as MentionCandidate, m as MentionProvider, P as PreviewPanel, n as PreviewPanelProps, R as RawEditor, o as RawEditorProps, T as ThemeInheritance, V as ViewPreferences, p as WysiwygEditor, q as WysiwygEditorProps, u as useEditorContext } from '../shell-595XbANu.js';
1
+ export { B as BlockTagVisibility, c as DocumentLinkCandidate, d as DocumentLinkProvider, e as EditorActions, f as EditorColorScheme, g as EditorContextValue, E as EditorHostMode, h as EditorMode, i as EditorProvider, j as EditorProviderProps, k as EditorShell, l as EditorShellProps, m as EditorState, n as EditorView, I as ImageDisplayMode, L as LayoutMode, M as MentionCandidate, o as MentionProvider, P as PreviewPanel, p as PreviewPanelProps, R as RawEditor, q as RawEditorProps, T as ThemeInheritance, V as ViewPreferences, r as WysiwygEditor, s as WysiwygEditorProps, u as useEditorContext } from '../shell-Bv6neeA6.js';
2
2
  import 'react/jsx-runtime';
3
3
  import 'react';
4
4
  import '@bendyline/squisq/schemas';
@@ -5,7 +5,7 @@ import {
5
5
  RawEditor,
6
6
  WysiwygEditor,
7
7
  useEditorContext
8
- } from "../chunk-7K4LSSNL.js";
8
+ } from "../chunk-SUXRSZ4Y.js";
9
9
  import "../chunk-V4NBQF5C.js";
10
10
  import "../chunk-K2WJYVS4.js";
11
11
  import "../chunk-V44VP242.js";
@@ -301,6 +301,16 @@ interface EditorActions {
301
301
  */
302
302
  bumpMediaRevision: () => void;
303
303
  }
304
+ /**
305
+ * Host-owned persistence for a rendered cover image. Return `false` when the
306
+ * user cancels so the export modal stays open without reporting an error.
307
+ */
308
+ type CoverImageSaveOutput = (blob: Blob, filename: string) => boolean | void | Promise<boolean | void>;
309
+ /**
310
+ * Host-owned persistence for a rendered dashboard image — the same contract
311
+ * as {@link CoverImageSaveOutput} (`false` keeps the export dialog open).
312
+ */
313
+ type DashboardImageSaveOutput = CoverImageSaveOutput;
304
314
  interface EditorContextValue extends EditorState, EditorActions {
305
315
  /** The live Tiptap editor instance (null when WYSIWYG is not mounted) */
306
316
  tiptapEditor: Editor | null;
@@ -382,6 +392,10 @@ interface EditorContextValue extends EditorState, EditorActions {
382
392
  * document has no frontmatter `title:`.
383
393
  */
384
394
  fileName: string | undefined;
395
+ /** Optional host save flow for cover-slide image exports. */
396
+ saveCoverImageOutput: CoverImageSaveOutput | undefined;
397
+ /** Optional host save flow for dashboard image exports. */
398
+ saveDashboardImageOutput: DashboardImageSaveOutput | undefined;
385
399
  }
386
400
  type ImageDisplayMode = 'inline' | 'thumbnail';
387
401
  /**
@@ -481,6 +495,10 @@ interface EditorProviderProps {
481
495
  * language and decide between markdown vs. code mode.
482
496
  */
483
497
  fileName?: string;
498
+ /** Optional host save flow for cover-slide image exports. */
499
+ saveCoverImageOutput?: CoverImageSaveOutput;
500
+ /** Optional host save flow for dashboard image exports. */
501
+ saveDashboardImageOutput?: DashboardImageSaveOutput;
484
502
  /** Explicit Monaco language ID — wins over the fileName-derived one. */
485
503
  language?: string;
486
504
  /**
@@ -569,7 +587,7 @@ interface ViewPreferences {
569
587
  /** Document vs. block-at-a-time layout. */
570
588
  layoutMode?: LayoutMode;
571
589
  }
572
- declare function EditorProvider({ initialMarkdown, initialView, articleId, colorScheme: initialColorScheme, workspaceContainer, allowVersioning, versionBasename, versioningPrunePolicy, versioningAutoSaveIdleMs, onSaveVersion, mediaProvider, imageDisplayMode, mentionProvider, documentLinkProvider, fenceRenderers, linkSchemes, allowRecording, allowNarrate, preserveSourceWrapping, fileName, language, findMode: controlledFindMode, onFindModeChange, inlinePreview, showStatusBar, outline, blockTags, blockTagVisibility: initialBlockTagVisibility, themeInheritance, layoutMode, viewPreferences, onViewPreferencesChange, children, }: EditorProviderProps): react_jsx_runtime.JSX.Element;
590
+ declare function EditorProvider({ initialMarkdown, initialView, articleId, colorScheme: initialColorScheme, workspaceContainer, allowVersioning, versionBasename, versioningPrunePolicy, versioningAutoSaveIdleMs, onSaveVersion, mediaProvider, imageDisplayMode, mentionProvider, documentLinkProvider, fenceRenderers, linkSchemes, allowRecording, allowNarrate, preserveSourceWrapping, fileName, saveCoverImageOutput, saveDashboardImageOutput, language, findMode: controlledFindMode, onFindModeChange, inlinePreview, showStatusBar, outline, blockTags, blockTagVisibility: initialBlockTagVisibility, themeInheritance, layoutMode, viewPreferences, onViewPreferencesChange, children, }: EditorProviderProps): react_jsx_runtime.JSX.Element;
573
591
 
574
592
  /**
575
593
  * Host-supplied context sections rendered INSIDE the Monaco (raw/code)
@@ -908,6 +926,16 @@ interface EditorShellProps {
908
926
  * experience. Omit to get today's markdown behavior unchanged.
909
927
  */
910
928
  fileName?: string;
929
+ /**
930
+ * Optional host save flow for cover-slide image exports. When omitted, the
931
+ * editor uses the browser File System Access picker or download fallback.
932
+ */
933
+ saveCoverImageOutput?: CoverImageSaveOutput;
934
+ /**
935
+ * Optional host save flow for dashboard image exports. When omitted, the
936
+ * editor uses the browser File System Access picker or download fallback.
937
+ */
938
+ saveDashboardImageOutput?: DashboardImageSaveOutput;
911
939
  /**
912
940
  * Explicit Monaco language ID override (e.g. `'typescript'`,
913
941
  * `'python'`, `'json'`). Wins over the language derived from
@@ -1108,7 +1136,7 @@ interface EditorShellProps {
1108
1136
  * Complete markdown editor shell with toolbar, view switcher, and three
1109
1137
  * editing modes: Raw (Monaco), WYSIWYG (Tiptap), and Preview.
1110
1138
  */
1111
- declare function EditorShell({ initialMarkdown, initialView, hostMode, defaultViewportPreset, articleId, basePath, onChange, onLinkClick, showCodeCopyButton, onCopyCode, colorScheme, className, height, minHeight, maxHeight, mediaProvider, workspaceContainer, allowVersioning, versionBasename, versioningPrunePolicy, versioningAutoSaveIdleMs, onSaveVersion, showFilesToggle, showFormattingControls, showInsertControls, allowBinaryDownloads, toolbarSlotLeft, toolbarSlotAfterActions, toolbarSlotRight, statusBarSlotRight, showPlayTab, allowPresentationWindow, allowPresentationFullscreen, allowPrint, submitOnEnter, codeContext, fullWidth, uxFont, thinMargins, writeCanvasSettings, showStatusBar, imageDisplayMode, fileName, language, findMode, onFindModeChange, mentionProvider, documentLinkProvider, fenceRenderers, linkSchemes, allowRecording, allowNarrate, preserveSourceWrapping, placeholder, readOnly, imageSrc, imageAlt, imageMode, imageEditorContainer, onImageExport, inlinePreview, inlinePreviewWidth, outline, outlineWidth, blockTags, blockTagVisibility, themeInheritance, viewPreferences, onViewPreferencesChange, themeOverride, }: EditorShellProps): react_jsx_runtime.JSX.Element;
1139
+ declare function EditorShell({ initialMarkdown, initialView, hostMode, defaultViewportPreset, articleId, basePath, onChange, onLinkClick, showCodeCopyButton, onCopyCode, colorScheme, className, height, minHeight, maxHeight, mediaProvider, workspaceContainer, allowVersioning, versionBasename, versioningPrunePolicy, versioningAutoSaveIdleMs, onSaveVersion, showFilesToggle, showFormattingControls, showInsertControls, allowBinaryDownloads, toolbarSlotLeft, toolbarSlotAfterActions, toolbarSlotRight, statusBarSlotRight, showPlayTab, allowPresentationWindow, allowPresentationFullscreen, allowPrint, submitOnEnter, codeContext, fullWidth, uxFont, thinMargins, writeCanvasSettings, showStatusBar, imageDisplayMode, fileName, saveCoverImageOutput, saveDashboardImageOutput, language, findMode, onFindModeChange, mentionProvider, documentLinkProvider, fenceRenderers, linkSchemes, allowRecording, allowNarrate, preserveSourceWrapping, placeholder, readOnly, imageSrc, imageAlt, imageMode, imageEditorContainer, onImageExport, inlinePreview, inlinePreviewWidth, outline, outlineWidth, blockTags, blockTagVisibility, themeInheritance, viewPreferences, onViewPreferencesChange, themeOverride, }: EditorShellProps): react_jsx_runtime.JSX.Element;
1112
1140
 
1113
1141
  /**
1114
1142
  * RawEditor
@@ -1198,4 +1226,4 @@ interface PreviewPanelProps {
1198
1226
  */
1199
1227
  declare function PreviewPanel({ basePath, className, workspaceContainer, onLinkClick, showCodeCopyButton, onCopyCode, }: PreviewPanelProps): react_jsx_runtime.JSX.Element;
1200
1228
 
1201
- export { type BlockTagVisibility as B, type CodeContext as C, type DocumentLinkCandidate as D, type EditorHostMode as E, type ImageDisplayMode as I, type LayoutMode as L, type MentionCandidate as M, PreviewPanel as P, RawEditor as R, type SceneTextChannel as S, type ThemeInheritance as T, type ViewPreferences as V, type WriteCanvasSettings as W, type CodeContextSection as a, type DocumentLinkProvider as b, type EditorActions as c, type EditorColorScheme as d, type EditorContextValue as e, type EditorMode as f, EditorProvider as g, type EditorProviderProps as h, EditorShell as i, type EditorShellProps as j, type EditorState as k, type EditorView as l, type MentionProvider as m, type PreviewPanelProps as n, type RawEditorProps as o, WysiwygEditor as p, type WysiwygEditorProps as q, useEditorContext as u };
1229
+ export { type BlockTagVisibility as B, type CodeContext as C, type DashboardImageSaveOutput as D, type EditorHostMode as E, type ImageDisplayMode as I, type LayoutMode as L, type MentionCandidate as M, PreviewPanel as P, RawEditor as R, type SceneTextChannel as S, type ThemeInheritance as T, type ViewPreferences as V, type WriteCanvasSettings as W, type CodeContextSection as a, type CoverImageSaveOutput as b, type DocumentLinkCandidate as c, type DocumentLinkProvider as d, type EditorActions as e, type EditorColorScheme as f, type EditorContextValue as g, type EditorMode as h, EditorProvider as i, type EditorProviderProps as j, EditorShell as k, type EditorShellProps as l, type EditorState as m, type EditorView as n, type MentionProvider as o, type PreviewPanelProps as p, type RawEditorProps as q, WysiwygEditor as r, type WysiwygEditorProps as s, useEditorContext as u };
@@ -9715,6 +9715,19 @@
9715
9715
  background: #f5f5f5;
9716
9716
  padding: 16px;
9717
9717
  }
9718
+ .squisq-dashboard-fit {
9719
+ width: 100%;
9720
+ height: 100%;
9721
+ min-width: 0;
9722
+ min-height: 0;
9723
+ container-type: size;
9724
+ display: flex;
9725
+ align-items: center;
9726
+ justify-content: center;
9727
+ }
9728
+ .squisq-dashboard-fit .squisq-dashboard {
9729
+ --squisq-dashboard-fit-width: min(100%, calc(100cqh * var(--squisq-dashboard-aspect, 1.7778)));
9730
+ }
9718
9731
  .squisq-editor-shell[data-print-preview=true] > .squisq-status-bar,
9719
9732
  .squisq-editor-shell[data-print-preview=true] > .squisq-timeline,
9720
9733
  .squisq-editor-shell[data-print-preview=true] .squisq-media-bin,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bendyline/squisq-editor-react",
3
- "version": "2.7.3",
3
+ "version": "2.8.0",
4
4
  "description": "React editor shell with raw/WYSIWYG/preview modes for Squisq documents",
5
5
  "license": "MIT",
6
6
  "author": "Bendyline",
@@ -84,10 +84,10 @@
84
84
  "react-dom": "^18.0.0 || ^19.0.0"
85
85
  },
86
86
  "dependencies": {
87
- "@bendyline/squisq": "2.7.1",
88
- "@bendyline/squisq-formats": "2.4.4",
89
- "@bendyline/squisq-react": "2.7.2",
90
- "@bendyline/squisq-video-react": "2.3.5",
87
+ "@bendyline/squisq": "2.8.0",
88
+ "@bendyline/squisq-formats": "2.4.5",
89
+ "@bendyline/squisq-react": "2.8.0",
90
+ "@bendyline/squisq-video-react": "2.4.0",
91
91
  "@fortawesome/fontawesome-free": "7.2.0",
92
92
  "@tiptap/core": "2.27.2",
93
93
  "@tiptap/extension-heading": "2.27.2",