@bendyline/squisq-editor-react 1.5.3 → 1.6.1

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.
Files changed (186) hide show
  1. package/README.md +57 -10
  2. package/dist/index.d.ts +1114 -52
  3. package/dist/index.js +18446 -8050
  4. package/dist/index.js.map +1 -1
  5. package/dist/styles/fa-brands-400-AHOAZHCU.woff2 +0 -0
  6. package/dist/styles/fa-regular-400-VRZYIBIZ.woff2 +0 -0
  7. package/dist/styles/fa-solid-900-MDEYK55F.woff2 +0 -0
  8. package/dist/styles/fa-v4compatibility-ETEVP6IB.woff2 +0 -0
  9. package/dist/styles/index.css +13867 -0
  10. package/package.json +15 -7
  11. package/src/BlockCardView.tsx +121 -0
  12. package/src/BlockPreviewPanel.tsx +69 -0
  13. package/src/BlockPropertiesPopover.tsx +191 -0
  14. package/src/EditorContext.tsx +165 -16
  15. package/src/EditorShell.tsx +265 -144
  16. package/src/FolderView.tsx +131 -0
  17. package/src/Icon.tsx +26 -0
  18. package/src/ImageEditor.tsx +69 -22
  19. package/src/OutlinePanel.tsx +64 -7
  20. package/src/PlainHtmlPreview.tsx +30 -3
  21. package/src/PreviewControls.tsx +491 -122
  22. package/src/PreviewPanel.tsx +15 -9
  23. package/src/RawEditor.tsx +179 -17
  24. package/src/RecorderEntry.tsx +9 -16
  25. package/src/TemplateAnnotation.ts +44 -0
  26. package/src/TemplatePicker.tsx +329 -54
  27. package/src/ThemeCustomizerPanel.tsx +30 -336
  28. package/src/ThemePicker.tsx +112 -3
  29. package/src/TimelineBlockPreview.tsx +37 -0
  30. package/src/TimelineTrack.tsx +671 -0
  31. package/src/Toolbar.tsx +544 -180
  32. package/src/Tooltip.tsx +22 -4
  33. package/src/TransitionPicker.tsx +351 -0
  34. package/src/VersionHistoryPanel.tsx +4 -16
  35. package/src/ViewMenuPanel.tsx +17 -14
  36. package/src/WysiwygEditor.tsx +161 -65
  37. package/src/__tests__/blockProperties.test.ts +92 -0
  38. package/src/__tests__/blockRange.test.ts +105 -0
  39. package/src/__tests__/buildPreviewDocTransition.test.ts +73 -0
  40. package/src/__tests__/codeContextSectionView.test.tsx +95 -0
  41. package/src/__tests__/codeContextZoneManager.test.ts +127 -0
  42. package/src/__tests__/createShapeLayer.test.ts +46 -0
  43. package/src/__tests__/diffContextSections.test.ts +39 -0
  44. package/src/__tests__/drawingShapeRoundTrip.test.ts +49 -0
  45. package/src/__tests__/editorShellCodeContext.test.tsx +86 -0
  46. package/src/__tests__/editorShellProps.test.tsx +96 -0
  47. package/src/__tests__/embeddedMedia.test.ts +48 -0
  48. package/src/__tests__/headingTransition.test.ts +138 -0
  49. package/src/__tests__/layoutChildRoundTrip.test.ts +71 -0
  50. package/src/__tests__/plainHtmlPreview.test.tsx +10 -8
  51. package/src/__tests__/previewControls.test.tsx +70 -0
  52. package/src/__tests__/recorderMediaInsert.test.ts +86 -0
  53. package/src/__tests__/templateAnnotationRoundTrip.test.ts +18 -0
  54. package/src/__tests__/templatePickerMetadata.test.ts +32 -0
  55. package/src/__tests__/timelineSource.test.ts +134 -0
  56. package/src/__tests__/tiptapBridge.test.ts +92 -0
  57. package/src/__tests__/tiptapBridgeConformance.test.ts +47 -0
  58. package/src/__tests__/tooltip.test.tsx +72 -0
  59. package/src/__tests__/transitionCatalog.test.ts +64 -0
  60. package/src/__tests__/useBlockNavigator.test.tsx +67 -0
  61. package/src/__tests__/useJsonEditorTokens.test.ts +59 -0
  62. package/src/__tests__/useMediaRecorder.test.ts +41 -0
  63. package/src/__tests__/useTimelineClock.test.ts +21 -0
  64. package/src/blockProperties.ts +88 -0
  65. package/src/blockRange.ts +132 -0
  66. package/src/buildPreviewDoc.ts +98 -9
  67. package/src/codeContext/CodeContextSectionView.tsx +124 -0
  68. package/src/codeContext/CodeContextZoneManager.ts +149 -0
  69. package/src/codeContext/CodeContextZones.tsx +121 -0
  70. package/src/codeContext/diffContextSections.ts +38 -0
  71. package/src/codeContext/types.ts +75 -0
  72. package/src/customTemplates/AddBin.tsx +126 -0
  73. package/src/customTemplates/CustomLayoutManager.tsx +233 -0
  74. package/src/customTemplates/CustomTemplateContext.tsx +182 -0
  75. package/src/customTemplates/LayerToolbar.tsx +580 -0
  76. package/src/customTemplates/ShapeGlyph.tsx +47 -0
  77. package/src/customTemplates/TemplateDesigner.tsx +430 -0
  78. package/src/customTemplates/__tests__/library.test.ts +88 -0
  79. package/src/customTemplates/__tests__/normalizePositions.test.ts +109 -0
  80. package/src/customTemplates/__tests__/shapeDefs.test.ts +49 -0
  81. package/src/customTemplates/__tests__/useMemoryLayerAdapter.test.ts +95 -0
  82. package/src/customTemplates/designer.css +673 -0
  83. package/src/customTemplates/index.ts +31 -0
  84. package/src/customTemplates/library.ts +97 -0
  85. package/src/customTemplates/normalizePositions.ts +75 -0
  86. package/src/customTemplates/shapeDefs.ts +131 -0
  87. package/src/customTemplates/thumbnail.tsx +63 -0
  88. package/src/customTemplates/tokenDefs.ts +60 -0
  89. package/src/customTemplates/useDocCustomTemplates.ts +52 -0
  90. package/src/customTemplates/useMemoryLayerAdapter.ts +123 -0
  91. package/src/customThemes/CustomThemeContext.tsx +179 -0
  92. package/src/customThemes/CustomThemeDialog.tsx +286 -0
  93. package/src/customThemes/__tests__/CustomThemeContext.test.tsx +64 -0
  94. package/src/customThemes/__tests__/CustomThemeDialog.test.tsx +47 -0
  95. package/src/customThemes/__tests__/customThemeLibrary.test.ts +51 -0
  96. package/src/customThemes/customThemeLibrary.ts +97 -0
  97. package/src/customThemes/index.ts +31 -0
  98. package/src/customThemes/themeControls.tsx +229 -0
  99. package/src/customThemes/themeDraft.ts +272 -0
  100. package/src/customThemes/useDocCustomThemes.ts +49 -0
  101. package/src/diagram/DiagramCanvas.tsx +240 -0
  102. package/src/diagram/DiagramExtension.ts +209 -0
  103. package/src/diagram/DiagramMaximizedOverlay.tsx +46 -0
  104. package/src/diagram/DiagramWidget.tsx +270 -0
  105. package/src/diagram/diagramCommands.ts +604 -0
  106. package/src/diagram/diagramConstants.ts +17 -0
  107. package/src/diagram/useDiagramData.ts +126 -0
  108. package/src/embeddedMedia.ts +78 -0
  109. package/src/frontmatter.ts +29 -0
  110. package/src/headingTransition.ts +231 -0
  111. package/src/imageEditor/CanvasSurface.tsx +383 -88
  112. package/src/imageEditor/PropertiesPanel.tsx +47 -1
  113. package/src/imageEditor/Toolbar.tsx +229 -16
  114. package/src/imageEditor/createShapeLayer.ts +280 -0
  115. package/src/imageEditor/icons.tsx +34 -114
  116. package/src/imageEditor/image-editor.css +54 -5
  117. package/src/imageEditor/state.ts +23 -3
  118. package/src/index.ts +109 -1
  119. package/src/jsonEditor/useJsonEditorTokens.ts +13 -43
  120. package/src/recorder/RecorderModal.tsx +120 -53
  121. package/src/recorder/RecorderPanel.tsx +2 -26
  122. package/src/recorder/hooks/useMediaRecorder.ts +17 -11
  123. package/src/recorder/insertMediaBlock.ts +30 -0
  124. package/src/resolveBlockVisual.ts +33 -0
  125. package/src/scene/Scene.tsx +540 -0
  126. package/src/scene/SceneBlockExtension.ts +198 -0
  127. package/src/scene/SceneBlockToolbar.tsx +201 -0
  128. package/src/scene/SceneBlockWidget.tsx +434 -0
  129. package/src/scene/ScenePropsBar.tsx +85 -0
  130. package/src/scene/SceneSelection.tsx +107 -0
  131. package/src/scene/SceneViewport.tsx +102 -0
  132. package/src/scene/ShapePalette.tsx +181 -0
  133. package/src/scene/__tests__/DiagramAdapter.test.ts +56 -0
  134. package/src/scene/__tests__/bezierEdit.test.ts +85 -0
  135. package/src/scene/__tests__/blockLayers.test.ts +57 -0
  136. package/src/scene/__tests__/shapeLayers.test.ts +106 -0
  137. package/src/scene/__tests__/useSceneHitTest.test.ts +90 -0
  138. package/src/scene/__tests__/useScenePanZoom.test.ts +103 -0
  139. package/src/scene/adapters/DiagramAdapter.ts +168 -0
  140. package/src/scene/adapters/DrawingAdapter.ts +415 -0
  141. package/src/scene/adapters/LayoutAdapter.ts +310 -0
  142. package/src/scene/adapters/blockLayers.ts +159 -0
  143. package/src/scene/commands/SceneCommand.ts +70 -0
  144. package/src/scene/commands/drawingCommands.ts +318 -0
  145. package/src/scene/commands/layoutCommands.ts +301 -0
  146. package/src/scene/hooks/useSceneHitTest.ts +105 -0
  147. package/src/scene/hooks/useScenePanZoom.ts +147 -0
  148. package/src/scene/hooks/useSceneSelection.ts +62 -0
  149. package/src/scene/index.ts +95 -0
  150. package/src/scene/layers/DiagramEdges.tsx +127 -0
  151. package/src/scene/layers/edgeGeometry.ts +77 -0
  152. package/src/scene/layers/nodeCard.tsx +145 -0
  153. package/src/scene/layers/renderLayer.tsx +70 -0
  154. package/src/scene/layers/shapeLayers.ts +201 -0
  155. package/src/scene/paths/bezierEdit.ts +208 -0
  156. package/src/scene/scene.css +649 -0
  157. package/src/scene/text/SceneTextOverlay.tsx +161 -0
  158. package/src/scene/text/sceneTextChannel.ts +40 -0
  159. package/src/scene/text/sceneTextConfig.ts +27 -0
  160. package/src/scene/text/sceneTiptap.ts +36 -0
  161. package/src/scene/text/useSceneTextEditing.ts +39 -0
  162. package/src/scene/tools/ConnectTool.ts +111 -0
  163. package/src/scene/tools/DrawingConnectTool.ts +161 -0
  164. package/src/scene/tools/PathTool.ts +158 -0
  165. package/src/scene/tools/PlaceTool.ts +47 -0
  166. package/src/scene/tools/SceneTool.ts +75 -0
  167. package/src/scene/tools/SelectTool.ts +284 -0
  168. package/src/scene/tools/ShapeTool.ts +144 -0
  169. package/src/scene/tools/TextTool.ts +72 -0
  170. package/src/scene/tools/TokenTool.ts +95 -0
  171. package/src/scene/tools/createDrawShapeTool.ts +82 -0
  172. package/src/styles/code-context.css +155 -0
  173. package/src/styles/diagram.css +183 -0
  174. package/src/styles/editor.css +1708 -150
  175. package/src/styles/folder-view.css +210 -0
  176. package/src/styles/image-edit-affordance.css +2 -2
  177. package/src/styles/index.css +5 -0
  178. package/src/timelineSource.ts +244 -0
  179. package/src/tiptapBridge.ts +115 -34
  180. package/src/tooltipPlacement.ts +13 -0
  181. package/src/transitionCatalog.ts +159 -0
  182. package/src/types/monaco-shims.d.ts +10 -0
  183. package/src/useBlockNavigator.ts +153 -0
  184. package/src/useMonacoLoader.ts +23 -1
  185. package/src/useTimelineClock.ts +76 -0
  186. package/src/utils/dropUtils.ts +1 -1
@@ -1,144 +1,64 @@
1
1
  /**
2
- * Inline SVG line icons for the image editor chrome. Stroke-based,
3
- * `currentColor`-driven so they pick up `--squisq-image-editor-text` etc.
4
- * automatically. Sized via `font-size`/`em` so they scale with their
5
- * button label. Kept inline (no sprite, no font) so the package has zero
6
- * extra runtime cost and tree-shakes per-icon.
2
+ * Image-editor chrome icons. Thin wrappers around the shared {@link Icon}
3
+ * component, which renders Font Awesome Free glyphs via the webfont CSS
4
+ * imported in `styles/index.css`. Color and size flow from the parent's
5
+ * `color` / `font-size` (e.g. `--squisq-image-editor-text`), matching the
6
+ * toolbar icons so the whole editor shares one icon style.
7
7
  */
8
8
 
9
- import type { SVGProps } from 'react';
9
+ import { Icon, type IconProps } from '../Icon';
10
10
 
11
- type IconProps = Omit<SVGProps<SVGSVGElement>, 'children'>;
11
+ type Props = Omit<IconProps, 'icon'>;
12
12
 
13
- function Svg({ children, ...rest }: SVGProps<SVGSVGElement>) {
14
- return (
15
- <svg
16
- width="1em"
17
- height="1em"
18
- viewBox="0 0 24 24"
19
- fill="none"
20
- stroke="currentColor"
21
- strokeWidth={1.75}
22
- strokeLinecap="round"
23
- strokeLinejoin="round"
24
- aria-hidden="true"
25
- focusable="false"
26
- {...rest}
27
- >
28
- {children}
29
- </svg>
30
- );
13
+ export function EyeIcon(props: Props) {
14
+ return <Icon icon="fa-solid fa-eye" {...props} />;
31
15
  }
32
16
 
33
- export function EyeIcon(props: IconProps) {
34
- return (
35
- <Svg {...props}>
36
- <path d="M2.5 12s3.5-6.5 9.5-6.5S21.5 12 21.5 12s-3.5 6.5-9.5 6.5S2.5 12 2.5 12Z" />
37
- <circle cx="12" cy="12" r="2.75" />
38
- </Svg>
39
- );
17
+ export function EyeOffIcon(props: Props) {
18
+ return <Icon icon="fa-solid fa-eye-slash" {...props} />;
40
19
  }
41
20
 
42
- export function EyeOffIcon(props: IconProps) {
43
- return (
44
- <Svg {...props}>
45
- <path d="M3.5 3.5l17 17" />
46
- <path d="M9.5 5.7A10.6 10.6 0 0 1 12 5.5c6 0 9.5 6.5 9.5 6.5a17 17 0 0 1-3.2 4" />
47
- <path d="M6.2 7.6A17 17 0 0 0 2.5 12s3.5 6.5 9.5 6.5a10.6 10.6 0 0 0 4.3-.9" />
48
- <path d="M9.9 9.9a3 3 0 0 0 4.2 4.2" />
49
- </Svg>
50
- );
21
+ export function LockIcon(props: Props) {
22
+ return <Icon icon="fa-solid fa-lock" {...props} />;
51
23
  }
52
24
 
53
- export function LockIcon(props: IconProps) {
54
- return (
55
- <Svg {...props}>
56
- <rect x="5" y="11" width="14" height="9" rx="1.5" />
57
- <path d="M8 11V8a4 4 0 0 1 8 0v3" />
58
- </Svg>
59
- );
25
+ export function UnlockIcon(props: Props) {
26
+ return <Icon icon="fa-solid fa-lock-open" {...props} />;
60
27
  }
61
28
 
62
- export function UnlockIcon(props: IconProps) {
63
- return (
64
- <Svg {...props}>
65
- <rect x="5" y="11" width="14" height="9" rx="1.5" />
66
- <path d="M8 11V8a4 4 0 0 1 7.6-1.7" />
67
- </Svg>
68
- );
29
+ export function ChevronUpIcon(props: Props) {
30
+ return <Icon icon="fa-solid fa-chevron-up" {...props} />;
69
31
  }
70
32
 
71
- export function ChevronUpIcon(props: IconProps) {
72
- return (
73
- <Svg {...props}>
74
- <path d="M6 14l6-6 6 6" />
75
- </Svg>
76
- );
33
+ export function ChevronDownIcon(props: Props) {
34
+ return <Icon icon="fa-solid fa-chevron-down" {...props} />;
77
35
  }
78
36
 
79
- export function ChevronDownIcon(props: IconProps) {
80
- return (
81
- <Svg {...props}>
82
- <path d="M6 10l6 6 6-6" />
83
- </Svg>
84
- );
37
+ export function CloseIcon(props: Props) {
38
+ return <Icon icon="fa-solid fa-xmark" {...props} />;
85
39
  }
86
40
 
87
- export function CloseIcon(props: IconProps) {
88
- return (
89
- <Svg {...props}>
90
- <path d="M6 6l12 12M18 6L6 18" />
91
- </Svg>
92
- );
41
+ export function CursorIcon(props: Props) {
42
+ return <Icon icon="fa-solid fa-arrow-pointer" {...props} />;
93
43
  }
94
44
 
95
- export function CursorIcon(props: IconProps) {
96
- return (
97
- <Svg {...props}>
98
- <path d="M5 4l6.5 14.5 2.2-6 6-2.2L5 4Z" />
99
- </Svg>
100
- );
45
+ export function TextIcon(props: Props) {
46
+ return <Icon icon="fa-solid fa-font" {...props} />;
101
47
  }
102
48
 
103
- export function TextIcon(props: IconProps) {
104
- return (
105
- <Svg {...props}>
106
- <path d="M5 6h14M12 6v13M9 19h6" />
107
- </Svg>
108
- );
49
+ export function ShapeIcon(props: Props) {
50
+ return <Icon icon="fa-solid fa-shapes" {...props} />;
109
51
  }
110
52
 
111
- export function ShapeIcon(props: IconProps) {
112
- return (
113
- <Svg {...props}>
114
- <rect x="4" y="4" width="16" height="16" rx="2" />
115
- </Svg>
116
- );
53
+ export function CropIcon(props: Props) {
54
+ return <Icon icon="fa-solid fa-crop-simple" {...props} />;
117
55
  }
118
56
 
119
- export function CropIcon(props: IconProps) {
120
- return (
121
- <Svg {...props}>
122
- <path d="M7 2v15a1 1 0 0 0 1 1h15" />
123
- <path d="M2 7h15a1 1 0 0 1 1 1v15" />
124
- </Svg>
125
- );
57
+ export function PlusIcon(props: Props) {
58
+ return <Icon icon="fa-solid fa-plus" {...props} />;
126
59
  }
127
60
 
128
- export function PlusIcon(props: IconProps) {
129
- return (
130
- <Svg {...props}>
131
- <path d="M12 5v14M5 12h14" />
132
- </Svg>
133
- );
134
- }
135
-
136
- export function NoneIcon(props: IconProps) {
137
- // Used for "no fill / transparent" affordance.
138
- return (
139
- <Svg {...props}>
140
- <circle cx="12" cy="12" r="8" />
141
- <path d="M6.3 6.3l11.4 11.4" />
142
- </Svg>
143
- );
61
+ export function NoneIcon(props: Props) {
62
+ // "No fill / transparent" affordance.
63
+ return <Icon icon="fa-solid fa-ban" {...props} />;
144
64
  }
@@ -60,6 +60,19 @@
60
60
  .squisq-image-editor-tool-group--right {
61
61
  margin-left: auto;
62
62
  }
63
+ /* Read-only property value (e.g. a path shape's kind). */
64
+ .squisq-image-editor-readonly-value {
65
+ text-transform: capitalize;
66
+ opacity: 0.8;
67
+ }
68
+ /* Anchor for the shape palette popover that drops from the Shape tool. */
69
+ .squisq-image-editor-shape-trigger {
70
+ position: relative;
71
+ display: inline-flex;
72
+ }
73
+ .squisq-image-editor-shape-trigger .squisq-shape-palette {
74
+ z-index: 20;
75
+ }
63
76
  .squisq-image-editor-tool-button {
64
77
  background: var(--squisq-image-editor-control-bg);
65
78
  color: inherit;
@@ -133,21 +146,28 @@
133
146
  /* ── Canvas surface ────────────────────────────────────────────────── */
134
147
  .squisq-image-editor-surface {
135
148
  flex: 1 1 auto;
149
+ min-width: 0;
150
+ min-height: 0;
151
+ overflow: auto;
152
+ background: var(--squisq-image-editor-workspace-bg);
153
+ }
154
+ .squisq-image-editor-canvas-wrap {
136
155
  display: flex;
137
156
  align-items: center;
138
157
  justify-content: center;
139
- padding: 1rem;
140
- overflow: auto;
141
- background: var(--squisq-image-editor-workspace-bg);
158
+ min-width: 100%;
159
+ min-height: 100%;
160
+ padding: 16px;
161
+ box-sizing: border-box;
142
162
  }
143
163
  .squisq-image-editor-canvas {
144
164
  display: block;
145
- max-width: 100%;
146
- max-height: 100%;
165
+ flex: 0 0 auto;
147
166
  background: transparent;
148
167
  box-shadow: 0 4px 24px rgba(0, 0, 0, 0.4);
149
168
  user-select: none;
150
169
  touch-action: none;
170
+ image-rendering: pixelated;
151
171
  }
152
172
  .squisq-image-editor-canvas--tool-text,
153
173
  .squisq-image-editor-canvas--tool-shape {
@@ -156,6 +176,9 @@
156
176
  .squisq-image-editor-canvas--tool-crop {
157
177
  cursor: crosshair;
158
178
  }
179
+ .squisq-image-editor-canvas--tool-zoom-rect {
180
+ cursor: zoom-in;
181
+ }
159
182
 
160
183
  /* ── Layers panel ──────────────────────────────────────────────────── */
161
184
  .squisq-image-editor-panel-header {
@@ -328,6 +351,32 @@
328
351
  justify-content: center;
329
352
  }
330
353
 
354
+ /* ── Zoom controls ──────────────────────────────────────────────────── */
355
+ .squisq-image-editor-tool-group--zoom {
356
+ gap: 0.15rem;
357
+ }
358
+ .squisq-image-editor-zoom-input {
359
+ width: 44px;
360
+ text-align: center;
361
+ background: var(--squisq-image-editor-control-bg);
362
+ border: 1px solid var(--squisq-image-editor-control-border);
363
+ color: inherit;
364
+ border-radius: 3px;
365
+ padding: 3px 2px;
366
+ font: inherit;
367
+ font-size: 12px;
368
+ -moz-appearance: textfield;
369
+ }
370
+ .squisq-image-editor-zoom-input::-webkit-inner-spin-button,
371
+ .squisq-image-editor-zoom-input::-webkit-outer-spin-button {
372
+ -webkit-appearance: none;
373
+ }
374
+ .squisq-image-editor-zoom-label {
375
+ font-size: 12px;
376
+ color: var(--squisq-image-editor-text-muted);
377
+ padding: 0 1px;
378
+ }
379
+
331
380
  /* -- Version history dropdown --------------------------------------- */
332
381
  .squisq-image-editor-version-dropdown {
333
382
  position: relative;
@@ -16,15 +16,18 @@ import {
16
16
  touch,
17
17
  } from '@bendyline/squisq/imageEdit';
18
18
 
19
+ // Distributive Omit: applied per union member so the discriminant (`type`) is preserved.
20
+ type DOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never;
21
+
19
22
  /**
20
23
  * Layer payload accepted by the `add-layer` action — the `id` field is
21
24
  * optional and will be assigned by the underlying `addLayer` helper if
22
25
  * the caller doesn't supply one.
23
26
  */
24
- export type ImageEditLayerInput = ImageEditLayer | (Omit<ImageEditLayer, 'id'> & { id?: string });
27
+ export type ImageEditLayerInput = ImageEditLayer | (DOmit<ImageEditLayer, 'id'> & { id?: string });
25
28
 
26
29
  /** The currently active interaction tool. */
27
- export type ImageEditorTool = 'select' | 'text' | 'shape' | 'image' | 'crop';
30
+ export type ImageEditorTool = 'select' | 'text' | 'shape' | 'image' | 'crop' | 'zoom-rect';
28
31
 
29
32
  /** A pixel-space rectangle in canvas coordinates. */
30
33
  export interface CanvasRect {
@@ -41,6 +44,12 @@ export interface ImageEditorState {
41
44
  selectedLayerId: string | null;
42
45
  /** Active tool. */
43
46
  tool: ImageEditorTool;
47
+ /**
48
+ * The shape kind the shape tool will drop next (a drawing palette kind,
49
+ * e.g. `'rectangle'`, `'diamond'`, `'arrow-right'`). Set when the user
50
+ * picks from the shape palette; defaults to `'rectangle'`.
51
+ */
52
+ shapeKind: string;
44
53
  /**
45
54
  * Dirty flag — true when the in-memory doc has unsaved changes
46
55
  * relative to the last `markClean()` call. The hook uses this to
@@ -53,6 +62,7 @@ export type ImageEditorAction =
53
62
  | { type: 'load'; doc: ImageEditDoc }
54
63
  | { type: 'mark-clean' }
55
64
  | { type: 'set-tool'; tool: ImageEditorTool }
65
+ | { type: 'set-shape-kind'; kind: string }
56
66
  | { type: 'select'; layerId: string | null }
57
67
  | { type: 'set-canvas'; canvas: ImageEditDoc['canvas'] }
58
68
  | { type: 'add-layer'; layer: ImageEditLayerInput; select?: boolean }
@@ -67,6 +77,7 @@ export function initialImageEditorState(doc: ImageEditDoc): ImageEditorState {
67
77
  doc,
68
78
  selectedLayerId: null,
69
79
  tool: 'select',
80
+ shapeKind: 'rectangle',
70
81
  dirty: false,
71
82
  };
72
83
  }
@@ -77,7 +88,13 @@ export function imageEditorReducer(
77
88
  ): ImageEditorState {
78
89
  switch (action.type) {
79
90
  case 'load':
80
- return { doc: action.doc, selectedLayerId: null, tool: 'select', dirty: false };
91
+ return {
92
+ doc: action.doc,
93
+ selectedLayerId: null,
94
+ tool: 'select',
95
+ shapeKind: state.shapeKind,
96
+ dirty: false,
97
+ };
81
98
 
82
99
  case 'mark-clean':
83
100
  return state.dirty ? { ...state, dirty: false } : state;
@@ -85,6 +102,9 @@ export function imageEditorReducer(
85
102
  case 'set-tool':
86
103
  return state.tool === action.tool ? state : { ...state, tool: action.tool };
87
104
 
105
+ case 'set-shape-kind':
106
+ return state.shapeKind === action.kind ? state : { ...state, shapeKind: action.kind };
107
+
88
108
  case 'select':
89
109
  return state.selectedLayerId === action.layerId
90
110
  ? state
package/src/index.ts CHANGED
@@ -19,7 +19,15 @@
19
19
 
20
20
  // Shell (top-level component)
21
21
  export { EditorShell } from './EditorShell.js';
22
- export type { EditorShellProps, EditorTheme } from './EditorShell.js';
22
+ export type { EditorShellProps, EditorColorScheme } from './EditorShell.js';
23
+
24
+ // Code-context sections (host-supplied markdown injected into the code surface)
25
+ export { CodeContextZones } from './codeContext/CodeContextZones.js';
26
+ export type { CodeContext, CodeContextSection } from './codeContext/types.js';
27
+
28
+ // FolderView — standalone folder browser surface (companion to the shell)
29
+ export { FolderView } from './FolderView.js';
30
+ export type { FolderViewProps, FolderEntry } from './FolderView.js';
23
31
 
24
32
  // Context
25
33
  export { EditorProvider, useEditorContext } from './EditorContext.js';
@@ -37,8 +45,33 @@ export type {
37
45
  DocumentLinkProvider,
38
46
  ViewPreferences,
39
47
  ThemeInheritance,
48
+ LayoutMode,
40
49
  } from './EditorContext.js';
41
50
 
51
+ // Block-at-a-time primitives — reusable outside EditorShell. `useBlockNavigator`
52
+ // drives a one-block-at-a-time editing channel from any `(source, setSource)`
53
+ // pair; `BlockCardView` is the matching card chrome; the `blockRange` utilities
54
+ // are pure source-slicing helpers.
55
+ export { useBlockNavigator } from './useBlockNavigator.js';
56
+ export type { BlockNavigator, UseBlockNavigatorOptions } from './useBlockNavigator.js';
57
+ export { BlockCardView } from './BlockCardView.js';
58
+ export type { BlockCardViewProps } from './BlockCardView.js';
59
+ export {
60
+ getBlockSlices,
61
+ spliceBlock,
62
+ lineToOffset,
63
+ offsetToLine,
64
+ sliceIndexAtOffset,
65
+ } from './blockRange.js';
66
+ export type { BlockRange, BlockSlice } from './blockRange.js';
67
+
68
+ // Timeline view — the horizontal block + media track and the line-level
69
+ // markdown write-back helpers it commits edits through.
70
+ export { TimelineTrack } from './TimelineTrack.js';
71
+ export type { TimelineTrackProps } from './TimelineTrack.js';
72
+ export { formatSeconds, setBlockDurationInSource, setMediaClipInSource } from './timelineSource.js';
73
+ export type { MediaClipPatch } from './timelineSource.js';
74
+
42
75
  // File-kind detection — useful for hosts that want to pre-decide chrome
43
76
  // around the editor based on whether a file is markdown or code.
44
77
  export { resolveFileKind, detectLanguageFromFileName } from './fileKind.js';
@@ -71,6 +104,8 @@ export type { EmojiEntry, EmojiCategory } from './emojiData.js';
71
104
  export {
72
105
  PreviewSettingsProvider,
73
106
  PreviewToolbarControls,
107
+ PreviewModeSwitch,
108
+ PreviewFormatSwitch,
74
109
  usePreviewSettings,
75
110
  } from './PreviewControls.js';
76
111
  export type { PreviewSettings } from './PreviewControls.js';
@@ -89,6 +124,35 @@ export type { OutlinePanelProps } from './OutlinePanel.js';
89
124
  export { ThemeCustomizerPanel } from './ThemeCustomizerPanel.js';
90
125
  export type { ThemeCustomizerPanelProps } from './ThemeCustomizerPanel.js';
91
126
  export { TemplatePicker, templateLabel } from './TemplatePicker.js';
127
+ export { TransitionPicker } from './TransitionPicker.js';
128
+ export type { TransitionPickerProps } from './TransitionPicker.js';
129
+ export {
130
+ TRANSITION_GROUPS,
131
+ TRANSITION_ENTRIES,
132
+ transitionLabel,
133
+ findTransitionEntry,
134
+ } from './transitionCatalog.js';
135
+ export type {
136
+ TransitionGroup,
137
+ TransitionCatalogEntry,
138
+ DirectionModel,
139
+ } from './transitionCatalog.js';
140
+ export {
141
+ readHeadingLineTransition,
142
+ setHeadingLineTransition,
143
+ readBlockAttrsTransition,
144
+ setBlockAttrsTransition,
145
+ EMPTY_TRANSITION,
146
+ } from './headingTransition.js';
147
+ export type { TransitionFields } from './headingTransition.js';
148
+ export {
149
+ readBlockAttrsParams,
150
+ readBlockAttrsValue,
151
+ setBlockAttrsValue,
152
+ summarizeBlockProps,
153
+ } from './blockProperties.js';
154
+ export { BlockPropertiesPopover } from './BlockPropertiesPopover.js';
155
+ export type { BlockPropertiesPopoverProps } from './BlockPropertiesPopover.js';
92
156
  export { InlinePreviewGutter } from './InlinePreviewGutter.js';
93
157
  export type { InlinePreviewGutterProps } from './InlinePreviewGutter.js';
94
158
 
@@ -120,6 +184,31 @@ export {
120
184
  processTextFiles,
121
185
  } from './utils/dropUtils.js';
122
186
 
187
+ // Monaco lazy loader — exported so hosts embedding RawEditor-like surfaces
188
+ // can share the same load-once Monaco bootstrap.
189
+ export { useMonacoLoader } from './useMonacoLoader.js';
190
+ export type { UseMonacoLoaderResult } from './useMonacoLoader.js';
191
+
192
+ // Custom themes — provider stack (doc frontmatter + browser-local library)
193
+ export { CustomThemeProvider, useCustomThemes, useDocCustomThemes } from './customThemes/index.js';
194
+ export type {
195
+ CustomThemeContextValue,
196
+ CustomThemeProviderProps,
197
+ DocCustomThemes,
198
+ } from './customThemes/index.js';
199
+
200
+ // Custom templates — provider stack (doc frontmatter + browser-local library)
201
+ export {
202
+ CustomTemplateProvider,
203
+ useCustomTemplates,
204
+ useDocCustomTemplates,
205
+ } from './customTemplates/index.js';
206
+ export type {
207
+ CustomTemplateContextValue,
208
+ CustomTemplateProviderProps,
209
+ DocCustomTemplates,
210
+ } from './customTemplates/index.js';
211
+
123
212
  // Bridge utilities
124
213
  export { markdownToTiptap, tiptapToMarkdown } from './tiptapBridge.js';
125
214
 
@@ -129,6 +218,25 @@ export { buildPreviewDoc } from './buildPreviewDoc.js';
129
218
  // Tiptap extension: Heading with template annotation support
130
219
  export { HeadingWithTemplate } from './TemplateAnnotation.js';
131
220
 
221
+ // Diagram editor — Tiptap extension that mounts a React-Flow canvas
222
+ // below any `### Title {[diagram]}` heading and hides the section's
223
+ // child headings (they're rendered as nodes in the canvas).
224
+ export { DiagramExtension } from './diagram/DiagramExtension.js';
225
+ export { DiagramCanvas } from './diagram/DiagramCanvas.js';
226
+ export type { DiagramCommand } from './diagram/DiagramCanvas.js';
227
+ export { DiagramWidget } from './diagram/DiagramWidget.js';
228
+ export { useDiagramData } from './diagram/useDiagramData.js';
229
+ export type { DiagramData, DiagramRFNode, DiagramRFEdge } from './diagram/useDiagramData.js';
230
+ export {
231
+ moveNode,
232
+ addConnection,
233
+ removeConnection,
234
+ renameNode,
235
+ addNode,
236
+ removeNode,
237
+ listDiagramChildren,
238
+ } from './diagram/diagramCommands.js';
239
+
132
240
  // JSON Form — editable component
133
241
  export { JsonEditor } from './jsonEditor/index.js';
134
242
  export type { JsonEditorProps } from './jsonEditor/index.js';
@@ -6,15 +6,9 @@
6
6
 
7
7
  import { useMemo } from 'react';
8
8
  import type { CSSProperties } from 'react';
9
- import {
10
- applySurface,
11
- resolveFontFamily,
12
- type SurfaceScheme,
13
- type Theme,
14
- DEFAULT_THEME,
15
- DARK_SURFACE,
16
- LIGHT_SURFACE,
17
- } from '@bendyline/squisq/schemas';
9
+ import { type SurfaceScheme, type Theme } from '@bendyline/squisq/schemas';
10
+ import { buildJsonFormTokens, resolveJsonFormTheme } from '@bendyline/squisq/jsonForm';
11
+ import { useAutoSurface } from '@bendyline/squisq-react';
18
12
 
19
13
  export interface JsonEditorTokens {
20
14
  style: CSSProperties;
@@ -25,39 +19,15 @@ export function useJsonEditorTokens(
25
19
  theme: Theme | undefined,
26
20
  surface: SurfaceScheme | 'auto' | undefined,
27
21
  ): JsonEditorTokens {
28
- return useMemo(() => {
29
- const baseTheme = theme ?? DEFAULT_THEME;
30
- const resolvedSurface =
31
- surface === 'auto'
32
- ? typeof window !== 'undefined' &&
33
- window.matchMedia?.('(prefers-color-scheme: dark)').matches
34
- ? DARK_SURFACE
35
- : LIGHT_SURFACE
36
- : (surface ?? undefined);
37
- const finalTheme = resolvedSurface ? applySurface(baseTheme, resolvedSurface) : baseTheme;
38
-
39
- const titleFont = resolveFontFamily(finalTheme.typography.titleFont, 'system-ui, sans-serif');
40
- const bodyFont = resolveFontFamily(finalTheme.typography.bodyFont, 'system-ui, sans-serif');
41
- const monoFont = resolveFontFamily(
42
- finalTheme.typography.monoFont,
43
- 'ui-monospace, Consolas, monospace',
44
- );
22
+ // Reactive `prefers-color-scheme` tracking (matches `<JsonView>`), so the
23
+ // editor re-themes live when the OS switches light/dark under `'auto'`.
24
+ const auto = useAutoSurface(surface === 'auto');
25
+ const effectiveSurface = surface === 'auto' ? auto : (surface ?? undefined);
45
26
 
46
- const style: CSSProperties = {
47
- ['--squisq-jsonform-bg' as string]: finalTheme.colors.background,
48
- ['--squisq-jsonform-text' as string]: finalTheme.colors.text,
49
- ['--squisq-jsonform-muted' as string]: finalTheme.colors.textMuted,
50
- ['--squisq-jsonform-primary' as string]: finalTheme.colors.primary,
51
- ['--squisq-jsonform-accent' as string]: finalTheme.colors.secondary,
52
- ['--squisq-jsonform-warning' as string]: finalTheme.colors.warning,
53
- ['--squisq-jsonform-border' as string]: `color-mix(in srgb, ${finalTheme.colors.textMuted} 35%, transparent)`,
54
- ['--squisq-jsonform-input-bg' as string]: finalTheme.colors.backgroundLight,
55
- ['--squisq-jsonform-title-font' as string]: titleFont,
56
- ['--squisq-jsonform-body-font' as string]: bodyFont,
57
- ['--squisq-jsonform-mono-font' as string]: monoFont,
58
- ['--squisq-jsonform-radius' as string]: `${finalTheme.style.borderRadius ?? 8}px`,
59
- };
60
-
61
- return { style, theme: finalTheme };
62
- }, [theme, surface]);
27
+ return useMemo(() => {
28
+ const style = buildJsonFormTokens(theme, effectiveSurface, {
29
+ prefix: '--squisq-jsonform',
30
+ }) as unknown as CSSProperties;
31
+ return { style, theme: resolveJsonFormTheme(theme, effectiveSurface) };
32
+ }, [theme, effectiveSurface]);
63
33
  }