@bendyline/squisq-editor-react 2.0.0 → 2.0.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.
- package/dist/index.d.ts +75 -33
- package/dist/index.js +1717 -935
- package/dist/index.js.map +1 -1
- package/dist/styles/index.css +145 -5
- package/package.json +4 -4
- package/src/DocumentSettingsDialog.tsx +32 -18
- package/src/EditorShell.tsx +1 -1
- package/src/PreviewControls.tsx +57 -24
- package/src/RecorderEntry.tsx +2 -0
- package/src/Toolbar.tsx +164 -19
- package/src/__tests__/codeContextSectionView.test.tsx +8 -6
- package/src/__tests__/documentSettingsDialog.test.tsx +22 -0
- package/src/__tests__/mediaAttachmentFlow.test.ts +2 -2
- package/src/__tests__/previewControls.test.tsx +164 -0
- package/src/__tests__/recorderTheme.test.tsx +42 -0
- package/src/__tests__/selectionConversions.test.ts +80 -0
- package/src/__tests__/tiptapBridge.test.ts +48 -7
- package/src/__tests__/tiptapImageRoundTrip.test.ts +1 -1
- package/src/__tests__/toolbarSelectionConversion.test.tsx +164 -0
- package/src/asciiDiagram/AsciiDiagramWidget.tsx +34 -4
- package/src/asciiDiagram/__tests__/asciiDiagramCommands.test.ts +58 -1
- package/src/asciiDiagram/asciiDiagramCommands.ts +36 -10
- package/src/asciiDiagram/asciiDiagramData.ts +33 -0
- package/src/asciiDiagram/asciiDiagramOps.ts +19 -0
- package/src/codeContext/types.ts +1 -1
- package/src/customTemplates/__tests__/useMemoryLayerAdapter.test.ts +13 -0
- package/src/customTemplates/useMemoryLayerAdapter.ts +7 -1
- package/src/diagram/DiagramCanvas.tsx +16 -2
- package/src/frontmatterSettings.ts +23 -0
- package/src/index.ts +7 -1
- package/src/recorder/RecorderButton.tsx +9 -1
- package/src/recorder/RecorderModal.tsx +84 -41
- package/src/recorder/RecorderPanel.tsx +9 -1
- package/src/scene/__tests__/sceneIsolation.test.tsx +27 -1
- package/src/scene/adapters/DrawingAdapter.ts +6 -1
- package/src/scene/adapters/LayoutAdapter.ts +3 -0
- package/src/scene/commands/SceneCommand.ts +13 -2
- package/src/scene/tools/SelectTool.ts +5 -5
- package/src/selectionConversions.ts +155 -0
- package/src/styles/ascii-timeline.css +101 -4
- package/src/styles/editor.css +30 -0
- package/src/styles/tree-view.css +51 -1
- package/src/timeline/TimelineEditorWidget.tsx +200 -41
- package/src/timeline/__tests__/TimelineEditorWidget.test.tsx +61 -2
- package/src/timeline/__tests__/timelineCommands.test.ts +32 -0
- package/src/timeline/__tests__/timelineOps.test.ts +55 -0
- package/src/timeline/timelineCommands.ts +54 -0
- package/src/timeline/timelineOps.ts +107 -3
- package/src/tiptapBridge.ts +23 -5
- package/src/treeview/TreeOutlineWidget.tsx +153 -3
- package/src/treeview/__tests__/TreeOutlineWidget.test.tsx +156 -0
- package/src/treeview/__tests__/treeOps.test.ts +52 -0
- package/src/treeview/__tests__/treeViewCommands.test.ts +16 -0
- package/src/treeview/treeOps.ts +59 -0
- package/src/treeview/treeViewCommands.ts +5 -0
package/dist/styles/index.css
CHANGED
|
@@ -7560,6 +7560,7 @@
|
|
|
7560
7560
|
z-index: 100;
|
|
7561
7561
|
min-width: 200px;
|
|
7562
7562
|
padding: 4px 0;
|
|
7563
|
+
font-family: var( --squisq-ux-font, -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif );
|
|
7563
7564
|
background: #fff;
|
|
7564
7565
|
border: 1px solid #e5e7eb;
|
|
7565
7566
|
border-radius: 6px;
|
|
@@ -7574,6 +7575,11 @@
|
|
|
7574
7575
|
color: #9ca3af;
|
|
7575
7576
|
user-select: none;
|
|
7576
7577
|
}
|
|
7578
|
+
.squisq-insert-menu-header--separated {
|
|
7579
|
+
margin-top: 4px;
|
|
7580
|
+
padding-top: 8px;
|
|
7581
|
+
border-top: 1px solid #e5e7eb;
|
|
7582
|
+
}
|
|
7577
7583
|
.squisq-insert-menu[data-theme=dark] {
|
|
7578
7584
|
background: #1f2937;
|
|
7579
7585
|
border-color: #374151;
|
|
@@ -7588,6 +7594,9 @@
|
|
|
7588
7594
|
.squisq-insert-menu[data-theme=dark] .squisq-insert-menu-header {
|
|
7589
7595
|
color: #6b7280;
|
|
7590
7596
|
}
|
|
7597
|
+
.squisq-insert-menu[data-theme=dark] .squisq-insert-menu-header--separated {
|
|
7598
|
+
border-top-color: #374151;
|
|
7599
|
+
}
|
|
7591
7600
|
.squisq-template-picker {
|
|
7592
7601
|
display: flex;
|
|
7593
7602
|
align-items: center;
|
|
@@ -8686,6 +8695,9 @@
|
|
|
8686
8695
|
margin: 0.5em 0;
|
|
8687
8696
|
color: var(--squisq-theme-text-muted, #6b7280);
|
|
8688
8697
|
}
|
|
8698
|
+
.squisq-wysiwyg-editor blockquote > p {
|
|
8699
|
+
margin: 0;
|
|
8700
|
+
}
|
|
8689
8701
|
.squisq-wysiwyg-editor pre {
|
|
8690
8702
|
background: var(--squisq-theme-bg-muted, #1e293b);
|
|
8691
8703
|
color: var(--squisq-theme-text, #e2e8f0);
|
|
@@ -14080,7 +14092,7 @@
|
|
|
14080
14092
|
.squisq-tree-btn:hover {
|
|
14081
14093
|
border-color: var(--squisq-text-muted, #94a3b8);
|
|
14082
14094
|
}
|
|
14083
|
-
.squisq-tree-rows {
|
|
14095
|
+
.squisq-tree-outline .squisq-tree-rows {
|
|
14084
14096
|
list-style: none;
|
|
14085
14097
|
margin: 0;
|
|
14086
14098
|
padding: 0;
|
|
@@ -14089,15 +14101,45 @@
|
|
|
14089
14101
|
border-left: 1px solid var(--squisq-border, #e2e8f0);
|
|
14090
14102
|
margin-left: 8px;
|
|
14091
14103
|
}
|
|
14104
|
+
.squisq-tree-item {
|
|
14105
|
+
position: relative;
|
|
14106
|
+
}
|
|
14092
14107
|
.squisq-tree-row {
|
|
14108
|
+
position: relative;
|
|
14093
14109
|
display: flex;
|
|
14094
14110
|
align-items: center;
|
|
14095
14111
|
gap: 4px;
|
|
14096
14112
|
padding: 1px 0;
|
|
14113
|
+
border-radius: 4px;
|
|
14097
14114
|
}
|
|
14098
14115
|
.squisq-tree-row:hover .squisq-tree-controls {
|
|
14099
14116
|
opacity: 1;
|
|
14100
14117
|
}
|
|
14118
|
+
.squisq-tree-item--dragging {
|
|
14119
|
+
opacity: 0.42;
|
|
14120
|
+
}
|
|
14121
|
+
.squisq-tree-item--drop-before > .squisq-tree-row::before,
|
|
14122
|
+
.squisq-tree-item--drop-after::after {
|
|
14123
|
+
content: "";
|
|
14124
|
+
position: absolute;
|
|
14125
|
+
z-index: 2;
|
|
14126
|
+
left: 0;
|
|
14127
|
+
right: 0;
|
|
14128
|
+
height: 2px;
|
|
14129
|
+
border-radius: 999px;
|
|
14130
|
+
background: #6366f1;
|
|
14131
|
+
pointer-events: none;
|
|
14132
|
+
}
|
|
14133
|
+
.squisq-tree-item--drop-before > .squisq-tree-row::before {
|
|
14134
|
+
top: -1px;
|
|
14135
|
+
}
|
|
14136
|
+
.squisq-tree-item--drop-after::after {
|
|
14137
|
+
bottom: -1px;
|
|
14138
|
+
}
|
|
14139
|
+
.squisq-tree-item--drop-child > .squisq-tree-row {
|
|
14140
|
+
background: rgba(99, 102, 241, 0.12);
|
|
14141
|
+
box-shadow: inset 0 0 0 1px #6366f1;
|
|
14142
|
+
}
|
|
14101
14143
|
.squisq-tree-chevron {
|
|
14102
14144
|
flex: 0 0 auto;
|
|
14103
14145
|
width: 16px;
|
|
@@ -14142,6 +14184,18 @@
|
|
|
14142
14184
|
opacity: 0;
|
|
14143
14185
|
transition: opacity 0.12s;
|
|
14144
14186
|
}
|
|
14187
|
+
.squisq-tree-drag-handle {
|
|
14188
|
+
flex: 0 0 auto;
|
|
14189
|
+
display: inline-flex;
|
|
14190
|
+
align-items: center;
|
|
14191
|
+
justify-content: center;
|
|
14192
|
+
width: 16px;
|
|
14193
|
+
color: var(--squisq-text-muted, #64748b);
|
|
14194
|
+
cursor: grab;
|
|
14195
|
+
}
|
|
14196
|
+
.squisq-tree-drag-handle:active {
|
|
14197
|
+
cursor: grabbing;
|
|
14198
|
+
}
|
|
14145
14199
|
.squisq-tree-controls button {
|
|
14146
14200
|
border: none;
|
|
14147
14201
|
background: transparent;
|
|
@@ -14173,7 +14227,7 @@
|
|
|
14173
14227
|
}
|
|
14174
14228
|
.squisq-ascii-timeline-editor {
|
|
14175
14229
|
--squisq-ascii-timeline-accent: var(--squisq-theme-primary, #6366f1);
|
|
14176
|
-
--squisq-ascii-timeline-label-width:
|
|
14230
|
+
--squisq-ascii-timeline-label-width: 160px;
|
|
14177
14231
|
border: 1px solid var(--squisq-border, #e2e8f0);
|
|
14178
14232
|
border-radius: 10px;
|
|
14179
14233
|
background: var(--squisq-input-bg, #fff);
|
|
@@ -14200,6 +14254,30 @@
|
|
|
14200
14254
|
.squisq-ascii-timeline-header small {
|
|
14201
14255
|
color: var(--squisq-text-muted, #64748b);
|
|
14202
14256
|
}
|
|
14257
|
+
.squisq-ascii-timeline-header-actions {
|
|
14258
|
+
display: flex;
|
|
14259
|
+
align-items: center;
|
|
14260
|
+
justify-content: flex-end;
|
|
14261
|
+
gap: 10px;
|
|
14262
|
+
}
|
|
14263
|
+
.squisq-ascii-timeline-header-actions button {
|
|
14264
|
+
display: inline-flex;
|
|
14265
|
+
align-items: center;
|
|
14266
|
+
gap: 5px;
|
|
14267
|
+
padding: 4px 8px;
|
|
14268
|
+
border: 1px solid var(--squisq-border, #cbd5e1);
|
|
14269
|
+
border-radius: 5px;
|
|
14270
|
+
background: var(--squisq-input-bg, #fff);
|
|
14271
|
+
color: var(--squisq-ascii-timeline-accent);
|
|
14272
|
+
font: inherit;
|
|
14273
|
+
font-size: 12px;
|
|
14274
|
+
cursor: pointer;
|
|
14275
|
+
}
|
|
14276
|
+
.squisq-ascii-timeline-header-actions button:hover,
|
|
14277
|
+
.squisq-ascii-timeline-header-actions button:focus-visible {
|
|
14278
|
+
border-color: var(--squisq-ascii-timeline-accent);
|
|
14279
|
+
outline: 2px solid color-mix(in srgb, var(--squisq-ascii-timeline-accent) 22%, transparent);
|
|
14280
|
+
}
|
|
14203
14281
|
.squisq-ascii-timeline-canvas {
|
|
14204
14282
|
position: relative;
|
|
14205
14283
|
min-width: 600px;
|
|
@@ -14225,9 +14303,64 @@
|
|
|
14225
14303
|
text-align: right;
|
|
14226
14304
|
white-space: nowrap;
|
|
14227
14305
|
}
|
|
14228
|
-
.squisq-ascii-timeline-track-
|
|
14306
|
+
.squisq-ascii-timeline-track-name {
|
|
14307
|
+
min-width: 0;
|
|
14308
|
+
flex: 1 1 auto;
|
|
14309
|
+
padding: 3px 4px;
|
|
14229
14310
|
overflow: hidden;
|
|
14311
|
+
border: 1px solid transparent;
|
|
14312
|
+
border-radius: 4px;
|
|
14313
|
+
background: transparent;
|
|
14314
|
+
color: inherit;
|
|
14315
|
+
font: inherit;
|
|
14316
|
+
text-align: right;
|
|
14230
14317
|
text-overflow: ellipsis;
|
|
14318
|
+
white-space: nowrap;
|
|
14319
|
+
}
|
|
14320
|
+
button.squisq-ascii-timeline-track-name {
|
|
14321
|
+
cursor: text;
|
|
14322
|
+
}
|
|
14323
|
+
button.squisq-ascii-timeline-track-name:hover,
|
|
14324
|
+
button.squisq-ascii-timeline-track-name:focus-visible {
|
|
14325
|
+
border-color: var(--squisq-border, #cbd5e1);
|
|
14326
|
+
background: var(--squisq-input-bg, #fff);
|
|
14327
|
+
outline: none;
|
|
14328
|
+
}
|
|
14329
|
+
.squisq-ascii-timeline-track-name-input {
|
|
14330
|
+
width: 100%;
|
|
14331
|
+
min-width: 0;
|
|
14332
|
+
flex: 1 1 auto;
|
|
14333
|
+
padding: 3px 4px;
|
|
14334
|
+
border: 1px solid var(--squisq-ascii-timeline-accent);
|
|
14335
|
+
border-radius: 4px;
|
|
14336
|
+
background: var(--squisq-input-bg, #fff);
|
|
14337
|
+
color: var(--squisq-text, #1e293b);
|
|
14338
|
+
font: inherit;
|
|
14339
|
+
text-align: right;
|
|
14340
|
+
outline: 2px solid color-mix(in srgb, var(--squisq-ascii-timeline-accent) 22%, transparent);
|
|
14341
|
+
}
|
|
14342
|
+
.squisq-ascii-timeline-track-delete {
|
|
14343
|
+
display: grid;
|
|
14344
|
+
width: 22px;
|
|
14345
|
+
height: 22px;
|
|
14346
|
+
flex: 0 0 22px;
|
|
14347
|
+
place-items: center;
|
|
14348
|
+
padding: 0;
|
|
14349
|
+
border: 0;
|
|
14350
|
+
border-radius: 4px;
|
|
14351
|
+
background: transparent;
|
|
14352
|
+
color: var(--squisq-text-muted, #64748b);
|
|
14353
|
+
cursor: pointer;
|
|
14354
|
+
}
|
|
14355
|
+
.squisq-ascii-timeline-track-delete:hover,
|
|
14356
|
+
.squisq-ascii-timeline-track-delete:focus-visible {
|
|
14357
|
+
background: color-mix(in srgb, #ef4444 12%, transparent);
|
|
14358
|
+
color: #dc2626;
|
|
14359
|
+
outline: 2px solid color-mix(in srgb, #ef4444 25%, transparent);
|
|
14360
|
+
}
|
|
14361
|
+
.squisq-ascii-timeline-track-delete:disabled {
|
|
14362
|
+
opacity: 0.35;
|
|
14363
|
+
cursor: not-allowed;
|
|
14231
14364
|
}
|
|
14232
14365
|
.squisq-ascii-timeline-track-add {
|
|
14233
14366
|
display: grid;
|
|
@@ -14244,7 +14377,8 @@
|
|
|
14244
14377
|
cursor: pointer;
|
|
14245
14378
|
}
|
|
14246
14379
|
.squisq-ascii-timeline-track-add:hover,
|
|
14247
|
-
.squisq-ascii-timeline-track-add:focus-visible
|
|
14380
|
+
.squisq-ascii-timeline-track-add:focus-visible,
|
|
14381
|
+
.squisq-ascii-timeline-track-add[aria-pressed=true] {
|
|
14248
14382
|
border-color: var(--squisq-ascii-timeline-accent);
|
|
14249
14383
|
outline: 2px solid color-mix(in srgb, var(--squisq-ascii-timeline-accent) 22%, transparent);
|
|
14250
14384
|
}
|
|
@@ -14253,6 +14387,9 @@
|
|
|
14253
14387
|
height: 96px;
|
|
14254
14388
|
min-width: 420px;
|
|
14255
14389
|
outline: none;
|
|
14390
|
+
cursor: default;
|
|
14391
|
+
}
|
|
14392
|
+
.squisq-ascii-timeline-rail--adding {
|
|
14256
14393
|
cursor: crosshair;
|
|
14257
14394
|
}
|
|
14258
14395
|
.squisq-ascii-timeline-rail--readonly {
|
|
@@ -14554,7 +14691,7 @@
|
|
|
14554
14691
|
}
|
|
14555
14692
|
@media (max-width: 760px) {
|
|
14556
14693
|
.squisq-ascii-timeline-editor {
|
|
14557
|
-
--squisq-ascii-timeline-label-width:
|
|
14694
|
+
--squisq-ascii-timeline-label-width: 132px;
|
|
14558
14695
|
}
|
|
14559
14696
|
.squisq-ascii-timeline-inspector {
|
|
14560
14697
|
grid-template-columns: 1fr 1fr;
|
|
@@ -14566,6 +14703,9 @@
|
|
|
14566
14703
|
.squisq-ascii-timeline-delete {
|
|
14567
14704
|
grid-column: 2 / 3;
|
|
14568
14705
|
}
|
|
14706
|
+
.squisq-ascii-timeline-header-actions small {
|
|
14707
|
+
display: none;
|
|
14708
|
+
}
|
|
14569
14709
|
}
|
|
14570
14710
|
@media (prefers-reduced-motion: reduce) {
|
|
14571
14711
|
.squisq-ascii-timeline-gap-add {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bendyline/squisq-editor-react",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"description": "React editor shell with raw/WYSIWYG/preview modes for Squisq documents",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Bendyline",
|
|
@@ -56,9 +56,9 @@
|
|
|
56
56
|
}
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
|
-
"@bendyline/squisq": "2.0.
|
|
60
|
-
"@bendyline/squisq-formats": "2.0.
|
|
61
|
-
"@bendyline/squisq-react": "2.0.
|
|
59
|
+
"@bendyline/squisq": "2.0.1",
|
|
60
|
+
"@bendyline/squisq-formats": "2.0.1",
|
|
61
|
+
"@bendyline/squisq-react": "2.0.1",
|
|
62
62
|
"@fortawesome/fontawesome-free": "7.2.0",
|
|
63
63
|
"@monaco-editor/react": "4.7.0",
|
|
64
64
|
"@tiptap/extension-image": "2.27.2",
|
|
@@ -25,17 +25,14 @@ import {
|
|
|
25
25
|
import { getTransformStyleSummaries } from '@bendyline/squisq/transform';
|
|
26
26
|
import { ThemePicker } from './ThemePicker';
|
|
27
27
|
import { resolvePersistedTransformStyleId } from './transformStyleId';
|
|
28
|
+
import {
|
|
29
|
+
FRONTMATTER_SETTING_DEFAULTS,
|
|
30
|
+
FRONTMATTER_SETTING_KEYS,
|
|
31
|
+
omitFrontmatterDefault,
|
|
32
|
+
} from './frontmatterSettings';
|
|
28
33
|
|
|
29
34
|
// ── Frontmatter key constants ─────────────────────────────────────
|
|
30
35
|
|
|
31
|
-
/** Mirror of `FM_KEYS` in PreviewControls.tsx. Canonical names are the
|
|
32
|
-
* ones we write; legacy aliases are still read so older docs work. */
|
|
33
|
-
const FM = {
|
|
34
|
-
theme: { canonical: 'squisq-theme', legacy: ['themeId', 'theme'] },
|
|
35
|
-
transform: { canonical: 'squisq-transform', legacy: 'transform-style' },
|
|
36
|
-
captions: { canonical: 'squisq-captions', legacy: 'caption-style' },
|
|
37
|
-
} as const;
|
|
38
|
-
|
|
39
36
|
function readFm(
|
|
40
37
|
fm: Record<string, unknown> | undefined,
|
|
41
38
|
canonical: string,
|
|
@@ -80,9 +77,17 @@ export function DocumentSettingsDialog({
|
|
|
80
77
|
const currentTheme = readFrontmatterThemeId(frontmatter) ?? '';
|
|
81
78
|
const currentTransform =
|
|
82
79
|
resolvePersistedTransformStyleId(
|
|
83
|
-
readFm(
|
|
80
|
+
readFm(
|
|
81
|
+
frontmatter,
|
|
82
|
+
FRONTMATTER_SETTING_KEYS.transform.canonical,
|
|
83
|
+
FRONTMATTER_SETTING_KEYS.transform.legacy,
|
|
84
|
+
),
|
|
84
85
|
) ?? '';
|
|
85
|
-
const currentCaptions = readFm(
|
|
86
|
+
const currentCaptions = readFm(
|
|
87
|
+
frontmatter,
|
|
88
|
+
FRONTMATTER_SETTING_KEYS.captions.canonical,
|
|
89
|
+
FRONTMATTER_SETTING_KEYS.captions.legacy,
|
|
90
|
+
);
|
|
86
91
|
|
|
87
92
|
const [title, setTitle] = useState(currentTitle);
|
|
88
93
|
const [theme, setTheme] = useState(currentTheme);
|
|
@@ -132,20 +137,29 @@ export function DocumentSettingsDialog({
|
|
|
132
137
|
|
|
133
138
|
const updates: Record<string, string | null> = {
|
|
134
139
|
title: nextTitle,
|
|
135
|
-
[
|
|
140
|
+
[FRONTMATTER_SETTING_KEYS.theme.canonical]: omitFrontmatterDefault(
|
|
141
|
+
theme || FRONTMATTER_SETTING_DEFAULTS.theme,
|
|
142
|
+
FRONTMATTER_SETTING_DEFAULTS.theme,
|
|
143
|
+
),
|
|
136
144
|
// Saving settings canonicalizes all older theme spellings.
|
|
137
|
-
[
|
|
138
|
-
[
|
|
139
|
-
[
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
145
|
+
[FRONTMATTER_SETTING_KEYS.theme.legacy[0]]: null,
|
|
146
|
+
[FRONTMATTER_SETTING_KEYS.theme.legacy[1]]: null,
|
|
147
|
+
[FRONTMATTER_SETTING_KEYS.transform.canonical]: omitFrontmatterDefault(
|
|
148
|
+
transform,
|
|
149
|
+
FRONTMATTER_SETTING_DEFAULTS.transform,
|
|
150
|
+
),
|
|
151
|
+
[FRONTMATTER_SETTING_KEYS.transform.legacy]: null,
|
|
152
|
+
[FRONTMATTER_SETTING_KEYS.captions.canonical]: omitFrontmatterDefault(
|
|
153
|
+
captions || FRONTMATTER_SETTING_DEFAULTS.captions,
|
|
154
|
+
FRONTMATTER_SETTING_DEFAULTS.captions,
|
|
155
|
+
),
|
|
156
|
+
[FRONTMATTER_SETTING_KEYS.captions.legacy]: null,
|
|
143
157
|
};
|
|
144
158
|
|
|
145
159
|
const nextSource = setFrontmatterValues(markdownSource, updates);
|
|
146
160
|
onSave(nextSource);
|
|
147
161
|
},
|
|
148
|
-
[title, theme, transform, captions, inferredTitle, markdownSource,
|
|
162
|
+
[title, theme, transform, captions, inferredTitle, markdownSource, onSave],
|
|
149
163
|
);
|
|
150
164
|
|
|
151
165
|
return (
|
package/src/EditorShell.tsx
CHANGED
|
@@ -716,7 +716,7 @@ function EditorShellInner({
|
|
|
716
716
|
*
|
|
717
717
|
* Without this, upload-via-MediaBin and upload-via-drop both
|
|
718
718
|
* added the file to the bin and nowhere else — the composer sent
|
|
719
|
-
* an empty body and the downstream
|
|
719
|
+
* an empty body and the downstream consumer reported "nothing came
|
|
720
720
|
* through."
|
|
721
721
|
*/
|
|
722
722
|
const insertMediaRef = useCallback(
|
package/src/PreviewControls.tsx
CHANGED
|
@@ -47,6 +47,11 @@ import {
|
|
|
47
47
|
} from './customThemes';
|
|
48
48
|
import { Icon } from './Icon';
|
|
49
49
|
import { resolvePersistedTransformStyleId } from './transformStyleId';
|
|
50
|
+
import {
|
|
51
|
+
FRONTMATTER_SETTING_DEFAULTS,
|
|
52
|
+
FRONTMATTER_SETTING_KEYS,
|
|
53
|
+
omitFrontmatterDefault,
|
|
54
|
+
} from './frontmatterSettings';
|
|
50
55
|
|
|
51
56
|
// ── Context ──────────────────────────────────────────────────────
|
|
52
57
|
|
|
@@ -198,16 +203,6 @@ export interface PreviewSettingsProviderProps {
|
|
|
198
203
|
themeOverride?: Theme | null;
|
|
199
204
|
}
|
|
200
205
|
|
|
201
|
-
/** Frontmatter keys we read/write for preview settings. The squisq-prefixed
|
|
202
|
-
* keys are the canonical names; the legacy keys are still read so existing
|
|
203
|
-
* documents keep working. Persistence (writes) uses only the squisq names. */
|
|
204
|
-
const FM_KEYS = {
|
|
205
|
-
theme: { canonical: 'squisq-theme', legacy: ['themeId', 'theme'] as const },
|
|
206
|
-
transform: { canonical: 'squisq-transform', legacy: 'transform-style' as const },
|
|
207
|
-
captions: { canonical: 'squisq-captions', legacy: 'caption-style' as const },
|
|
208
|
-
coverSlide: { canonical: 'squisq-cover-slide', legacy: 'cover-slide' as const },
|
|
209
|
-
} as const;
|
|
210
|
-
|
|
211
206
|
function readFrontmatterKey(
|
|
212
207
|
fm: Record<string, unknown> | undefined,
|
|
213
208
|
canonical: string,
|
|
@@ -226,7 +221,7 @@ export function PreviewSettingsProvider({
|
|
|
226
221
|
const { markdownSource, setMarkdownSource, allowNarrate } = useEditorContext();
|
|
227
222
|
|
|
228
223
|
const persistFrontmatter = useCallback(
|
|
229
|
-
(updates: Record<string, string | null>) => {
|
|
224
|
+
(updates: Record<string, string | number | boolean | null | undefined>) => {
|
|
230
225
|
const next = setFrontmatterValues(markdownSource, updates);
|
|
231
226
|
if (next !== markdownSource) {
|
|
232
227
|
setMarkdownSource(next);
|
|
@@ -271,7 +266,7 @@ export function PreviewSettingsProvider({
|
|
|
271
266
|
);
|
|
272
267
|
const [selectedThemeId, setSelectedThemeId] = useState<string | null>(null);
|
|
273
268
|
useEffect(() => setSelectedThemeId(null), [fmTheme]);
|
|
274
|
-
const resolvedThemeId = selectedThemeId ?? fmTheme ??
|
|
269
|
+
const resolvedThemeId = selectedThemeId ?? fmTheme ?? FRONTMATTER_SETTING_DEFAULTS.theme;
|
|
275
270
|
// Doc themes precede browser-library themes in `allThemes`; choosing a
|
|
276
271
|
// library-only entry copies it into the document below for portable export.
|
|
277
272
|
const resolvedTheme = useMemo(
|
|
@@ -300,9 +295,12 @@ export function PreviewSettingsProvider({
|
|
|
300
295
|
const selectedCustom = customThemes.find((theme) => theme.id === id);
|
|
301
296
|
const alreadyDocScoped = docThemes.some((theme) => theme.id === id);
|
|
302
297
|
const updates: Record<string, string | null> = {
|
|
303
|
-
[
|
|
304
|
-
|
|
305
|
-
|
|
298
|
+
[FRONTMATTER_SETTING_KEYS.theme.canonical]: omitFrontmatterDefault(
|
|
299
|
+
id,
|
|
300
|
+
FRONTMATTER_SETTING_DEFAULTS.theme,
|
|
301
|
+
),
|
|
302
|
+
[FRONTMATTER_SETTING_KEYS.theme.legacy[0]]: null,
|
|
303
|
+
[FRONTMATTER_SETTING_KEYS.theme.legacy[1]]: null,
|
|
306
304
|
};
|
|
307
305
|
if (selectedCustom && !alreadyDocScoped) {
|
|
308
306
|
updates[FRONTMATTER_CUSTOM_THEMES_KEY] =
|
|
@@ -337,7 +335,7 @@ export function PreviewSettingsProvider({
|
|
|
337
335
|
idx >= 0 ? docThemes.map((t, i) => (i === idx ? theme : t)) : [...docThemes, theme];
|
|
338
336
|
const updates: Record<string, string | null> = {
|
|
339
337
|
[FRONTMATTER_CUSTOM_THEMES_KEY]: writeCustomThemesToFrontmatter(nextThemes) ?? null,
|
|
340
|
-
[
|
|
338
|
+
[FRONTMATTER_SETTING_KEYS.theme.canonical]: theme.id,
|
|
341
339
|
};
|
|
342
340
|
if (extras?.templates && extras.templates.length > 0) {
|
|
343
341
|
const existing = doc?.customTemplates ?? [];
|
|
@@ -367,7 +365,11 @@ export function PreviewSettingsProvider({
|
|
|
367
365
|
const fmTransform = useMemo(
|
|
368
366
|
() =>
|
|
369
367
|
resolvePersistedTransformStyleId(
|
|
370
|
-
readFrontmatterKey(
|
|
368
|
+
readFrontmatterKey(
|
|
369
|
+
frontmatter,
|
|
370
|
+
FRONTMATTER_SETTING_KEYS.transform.canonical,
|
|
371
|
+
FRONTMATTER_SETTING_KEYS.transform.legacy,
|
|
372
|
+
),
|
|
371
373
|
),
|
|
372
374
|
[frontmatter],
|
|
373
375
|
);
|
|
@@ -379,7 +381,13 @@ export function PreviewSettingsProvider({
|
|
|
379
381
|
setSelectedTransformStyle(id);
|
|
380
382
|
if (id !== null) {
|
|
381
383
|
// Empty string = "None" — remove the key rather than writing a blank value.
|
|
382
|
-
persistFrontmatter({
|
|
384
|
+
persistFrontmatter({
|
|
385
|
+
[FRONTMATTER_SETTING_KEYS.transform.canonical]: omitFrontmatterDefault(
|
|
386
|
+
id,
|
|
387
|
+
FRONTMATTER_SETTING_DEFAULTS.transform,
|
|
388
|
+
),
|
|
389
|
+
[FRONTMATTER_SETTING_KEYS.transform.legacy]: null,
|
|
390
|
+
});
|
|
383
391
|
}
|
|
384
392
|
},
|
|
385
393
|
[persistFrontmatter],
|
|
@@ -391,19 +399,30 @@ export function PreviewSettingsProvider({
|
|
|
391
399
|
const fmCaptionMode = useMemo(
|
|
392
400
|
() =>
|
|
393
401
|
resolveFrontmatterCaptionMode(
|
|
394
|
-
readFrontmatterKey(
|
|
402
|
+
readFrontmatterKey(
|
|
403
|
+
frontmatter,
|
|
404
|
+
FRONTMATTER_SETTING_KEYS.captions.canonical,
|
|
405
|
+
FRONTMATTER_SETTING_KEYS.captions.legacy,
|
|
406
|
+
),
|
|
395
407
|
),
|
|
396
408
|
[frontmatter],
|
|
397
409
|
);
|
|
398
410
|
const [selectedCaptionMode, setSelectedCaptionMode] = useState<CaptionMode | null>(null);
|
|
399
411
|
useEffect(() => setSelectedCaptionMode(null), [fmCaptionMode]);
|
|
400
|
-
const activeCaptionMode =
|
|
412
|
+
const activeCaptionMode =
|
|
413
|
+
selectedCaptionMode ?? fmCaptionMode ?? FRONTMATTER_SETTING_DEFAULTS.captions;
|
|
401
414
|
const activeCaptionsEnabled = activeCaptionMode !== 'off';
|
|
402
415
|
const activeCaptionStyle: CaptionStyle = activeCaptionMode === 'social' ? 'social' : 'standard';
|
|
403
416
|
const handleSetCaptionMode = useCallback(
|
|
404
417
|
(mode: CaptionMode) => {
|
|
405
418
|
setSelectedCaptionMode(mode);
|
|
406
|
-
persistFrontmatter({
|
|
419
|
+
persistFrontmatter({
|
|
420
|
+
[FRONTMATTER_SETTING_KEYS.captions.canonical]: omitFrontmatterDefault(
|
|
421
|
+
mode,
|
|
422
|
+
FRONTMATTER_SETTING_DEFAULTS.captions,
|
|
423
|
+
),
|
|
424
|
+
[FRONTMATTER_SETTING_KEYS.captions.legacy]: null,
|
|
425
|
+
});
|
|
407
426
|
},
|
|
408
427
|
[persistFrontmatter],
|
|
409
428
|
);
|
|
@@ -413,17 +432,31 @@ export function PreviewSettingsProvider({
|
|
|
413
432
|
const fmCoverSlide = useMemo(
|
|
414
433
|
() =>
|
|
415
434
|
resolveFrontmatterBoolean(
|
|
416
|
-
readFrontmatterKey(
|
|
435
|
+
readFrontmatterKey(
|
|
436
|
+
frontmatter,
|
|
437
|
+
FRONTMATTER_SETTING_KEYS.coverSlide.canonical,
|
|
438
|
+
FRONTMATTER_SETTING_KEYS.coverSlide.legacy,
|
|
439
|
+
),
|
|
417
440
|
),
|
|
418
441
|
[frontmatter],
|
|
419
442
|
);
|
|
420
443
|
const [selectedCoverSlide, setSelectedCoverSlide] = useState<boolean | null>(null);
|
|
421
444
|
useEffect(() => setSelectedCoverSlide(null), [fmCoverSlide]);
|
|
422
|
-
const activeCoverSlide =
|
|
445
|
+
const activeCoverSlide =
|
|
446
|
+
selectedCoverSlide ?? fmCoverSlide ?? FRONTMATTER_SETTING_DEFAULTS.coverSlide;
|
|
423
447
|
const handleSetCoverSlideEnabled = useCallback(
|
|
424
448
|
(enabled: boolean) => {
|
|
425
449
|
setSelectedCoverSlide(enabled);
|
|
426
|
-
persistFrontmatter({
|
|
450
|
+
persistFrontmatter({
|
|
451
|
+
// The cover is enabled by default, so only persist the non-default
|
|
452
|
+
// state. Pass the boolean through so YAML writes `false`, not
|
|
453
|
+
// the string `"false"`.
|
|
454
|
+
[FRONTMATTER_SETTING_KEYS.coverSlide.canonical]: omitFrontmatterDefault(
|
|
455
|
+
enabled,
|
|
456
|
+
FRONTMATTER_SETTING_DEFAULTS.coverSlide,
|
|
457
|
+
),
|
|
458
|
+
[FRONTMATTER_SETTING_KEYS.coverSlide.legacy]: null,
|
|
459
|
+
});
|
|
427
460
|
},
|
|
428
461
|
[persistFrontmatter],
|
|
429
462
|
);
|
package/src/RecorderEntry.tsx
CHANGED
|
@@ -81,6 +81,7 @@ export function RecorderEntry() {
|
|
|
81
81
|
bumpMediaRevision,
|
|
82
82
|
markdownSource,
|
|
83
83
|
setMarkdownSource,
|
|
84
|
+
colorScheme,
|
|
84
85
|
} = useEditorContext();
|
|
85
86
|
|
|
86
87
|
const handleSave = useCallback(
|
|
@@ -150,6 +151,7 @@ export function RecorderEntry() {
|
|
|
150
151
|
<RecorderPanel
|
|
151
152
|
mediaProvider={mediaProvider}
|
|
152
153
|
container={workspaceContainer}
|
|
154
|
+
colorScheme={colorScheme}
|
|
153
155
|
onSave={handleSave}
|
|
154
156
|
className="squisq-toolbar-button"
|
|
155
157
|
/>
|