@bendyline/squisq-editor-react 1.6.0 → 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 (37) hide show
  1. package/README.md +57 -10
  2. package/dist/index.d.ts +401 -76
  3. package/dist/index.js +1334 -930
  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/EditorContext.tsx +22 -16
  12. package/src/EditorShell.tsx +68 -27
  13. package/src/OutlinePanel.tsx +26 -4
  14. package/src/PreviewControls.tsx +338 -141
  15. package/src/PreviewPanel.tsx +15 -9
  16. package/src/RawEditor.tsx +10 -4
  17. package/src/Toolbar.tsx +21 -11
  18. package/src/VersionHistoryPanel.tsx +2 -2
  19. package/src/__tests__/codeContextSectionView.test.tsx +95 -0
  20. package/src/__tests__/codeContextZoneManager.test.ts +127 -0
  21. package/src/__tests__/diffContextSections.test.ts +39 -0
  22. package/src/__tests__/editorShellCodeContext.test.tsx +86 -0
  23. package/src/__tests__/editorShellProps.test.tsx +96 -0
  24. package/src/__tests__/previewControls.test.tsx +70 -0
  25. package/src/__tests__/useJsonEditorTokens.test.ts +59 -0
  26. package/src/__tests__/useMediaRecorder.test.ts +17 -0
  27. package/src/codeContext/CodeContextSectionView.tsx +124 -0
  28. package/src/codeContext/CodeContextZoneManager.ts +149 -0
  29. package/src/codeContext/CodeContextZones.tsx +121 -0
  30. package/src/codeContext/diffContextSections.ts +38 -0
  31. package/src/codeContext/types.ts +75 -0
  32. package/src/index.ts +32 -1
  33. package/src/jsonEditor/useJsonEditorTokens.ts +13 -43
  34. package/src/recorder/hooks/useMediaRecorder.ts +9 -10
  35. package/src/styles/code-context.css +155 -0
  36. package/src/styles/editor.css +149 -3
  37. package/src/styles/index.css +1 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bendyline/squisq-editor-react",
3
- "version": "1.6.0",
3
+ "version": "1.6.1",
4
4
  "description": "React editor shell with raw/WYSIWYG/preview modes for Squisq documents",
5
5
  "license": "MIT",
6
6
  "author": "Bendyline",
@@ -34,10 +34,10 @@
34
34
  "types": "./dist/index.d.ts",
35
35
  "import": "./dist/index.js"
36
36
  },
37
- "./styles": "./src/styles/index.css"
37
+ "./styles": "./dist/styles/index.css"
38
38
  },
39
39
  "scripts": {
40
- "build": "tsup",
40
+ "build": "tsup && node ../../scripts/build-styles.mjs",
41
41
  "dev": "concurrently -n js,dts -c blue,gray -r \"tsup --watch --no-dts --no-clean\" \"tsup --watch --dts-only --no-clean\"",
42
42
  "typecheck": "tsc --noEmit"
43
43
  },
@@ -46,10 +46,15 @@
46
46
  "react": "^18.0.0 || ^19.0.0",
47
47
  "react-dom": "^18.0.0 || ^19.0.0"
48
48
  },
49
+ "peerDependenciesMeta": {
50
+ "monaco-editor": {
51
+ "optional": true
52
+ }
53
+ },
49
54
  "dependencies": {
50
- "@bendyline/squisq": "1.5.0",
51
- "@bendyline/squisq-formats": "1.4.0",
52
- "@bendyline/squisq-react": "1.4.0",
55
+ "@bendyline/squisq": "1.5.1",
56
+ "@bendyline/squisq-formats": "1.4.1",
57
+ "@bendyline/squisq-react": "1.4.1",
53
58
  "@fortawesome/fontawesome-free": "7.2.0",
54
59
  "@monaco-editor/react": "4.7.0",
55
60
  "@tiptap/extension-image": "2.27.2",
@@ -74,5 +79,8 @@
74
79
  "react-dom": "18.3.1",
75
80
  "tsup": "8.5.1",
76
81
  "typescript": "5.9.3"
77
- }
82
+ },
83
+ "sideEffects": [
84
+ "**/*.css"
85
+ ]
78
86
  }
@@ -91,7 +91,13 @@ export type DocumentLinkProvider = (query: string) => Promise<DocumentLinkCandid
91
91
  // ─── Types ───────────────────────────────────────────────
92
92
 
93
93
  export type EditorView = 'raw' | 'wysiwyg' | 'preview';
94
- export type EditorTheme = 'light' | 'dark';
94
+ /**
95
+ * Light/dark chrome mode for the editor shell (toolbar, tabs, status bar,
96
+ * side panes). This is the editor's *UI color scheme* — distinct from a
97
+ * Squisq `Theme` object, which styles the rendered document. Renamed from
98
+ * the former `EditorTheme` to remove that ambiguity.
99
+ */
100
+ export type EditorColorScheme = 'light' | 'dark';
95
101
  /**
96
102
  * Document layout mode. `'document'` shows the whole markdown document in
97
103
  * the active view (the historical behavior). `'block'` is the
@@ -128,8 +134,8 @@ export interface EditorState {
128
134
  parseError: string | null;
129
135
  /** Whether a parse is pending */
130
136
  isParsing: boolean;
131
- /** Current color theme */
132
- theme: EditorTheme;
137
+ /** Current light/dark chrome color scheme for the editor shell. */
138
+ colorScheme: EditorColorScheme;
133
139
  /** Operating mode — 'markdown' for the full shell, 'code' for Monaco-only. */
134
140
  editorMode: EditorMode;
135
141
  /** Monaco language ID for the Raw editor. */
@@ -240,8 +246,8 @@ export interface EditorActions {
240
246
  setTiptapEditor: (editor: TiptapEditor | null) => void;
241
247
  /** Register / unregister the Monaco editor instance (called by RawEditor) */
242
248
  setMonacoEditor: (editor: MonacoEditor | null) => void;
243
- /** Set the color theme */
244
- setTheme: (theme: EditorTheme) => void;
249
+ /** Set the light/dark chrome color scheme for the editor shell. */
250
+ setColorScheme: (colorScheme: EditorColorScheme) => void;
245
251
  /** Show or hide the inline preview gutter at runtime (driven by the View menu). */
246
252
  setInlinePreviewVisible: (visible: boolean) => void;
247
253
  /** Show or hide the bottom status bar at runtime (driven by the View menu). */
@@ -360,8 +366,8 @@ export interface EditorProviderProps {
360
366
  initialView?: EditorView;
361
367
  /** Article ID used when generating the Doc */
362
368
  articleId?: string;
363
- /** Color theme */
364
- theme?: EditorTheme;
369
+ /** Light/dark chrome color scheme for the editor shell. */
370
+ colorScheme?: EditorColorScheme;
365
371
  /**
366
372
  * Workspace-scoped `ContentContainer` for this document — the folder
367
373
  * holding the doc, its `_files/` sidecar, sibling documents, and any
@@ -509,7 +515,7 @@ export function EditorProvider({
509
515
  initialMarkdown = '',
510
516
  initialView = 'raw',
511
517
  articleId = 'untitled',
512
- theme: initialTheme = 'light',
518
+ colorScheme: initialColorScheme = 'light',
513
519
  workspaceContainer = null,
514
520
  allowVersioning = false,
515
521
  versionBasename,
@@ -570,7 +576,7 @@ export function EditorProvider({
570
576
  );
571
577
  const [parseError, setParseError] = useState<string | null>(null);
572
578
  const [isParsing, setIsParsing] = useState(false);
573
- const [theme, setTheme] = useState<EditorTheme>(initialTheme);
579
+ const [colorScheme, setColorScheme] = useState<EditorColorScheme>(initialColorScheme);
574
580
  const [inlinePreviewVisible, setInlinePreviewVisibleRaw] =
575
581
  useState<boolean>(effectiveInlinePreview);
576
582
  // Sync visibility when the host changes the prop (e.g., toggle from outside).
@@ -731,10 +737,10 @@ export function EditorProvider({
731
737
  const articleIdRef = useRef(articleId);
732
738
  articleIdRef.current = articleId;
733
739
 
734
- // Sync theme when prop changes
740
+ // Sync color scheme when prop changes
735
741
  useEffect(() => {
736
- setTheme(initialTheme);
737
- }, [initialTheme]);
742
+ setColorScheme(initialColorScheme);
743
+ }, [initialColorScheme]);
738
744
 
739
745
  // Debounced parse on markdown source change
740
746
  const parseTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
@@ -971,7 +977,7 @@ export function EditorProvider({
971
977
  activeView,
972
978
  parseError,
973
979
  isParsing,
974
- theme,
980
+ colorScheme,
975
981
  editorMode,
976
982
  language: resolvedLanguage,
977
983
  inlinePreviewVisible,
@@ -1009,7 +1015,7 @@ export function EditorProvider({
1009
1015
  setActiveView,
1010
1016
  setTiptapEditor,
1011
1017
  setMonacoEditor,
1012
- setTheme,
1018
+ setColorScheme,
1013
1019
  setInlinePreviewVisible,
1014
1020
  setStatusBarVisible,
1015
1021
  setOutlineVisible,
@@ -1028,7 +1034,7 @@ export function EditorProvider({
1028
1034
  activeView,
1029
1035
  parseError,
1030
1036
  isParsing,
1031
- theme,
1037
+ colorScheme,
1032
1038
  editorMode,
1033
1039
  resolvedLanguage,
1034
1040
  inlinePreviewVisible,
@@ -1063,7 +1069,7 @@ export function EditorProvider({
1063
1069
  setActiveView,
1064
1070
  setTiptapEditor,
1065
1071
  setMonacoEditor,
1066
- setTheme,
1072
+ setColorScheme,
1067
1073
  setInlinePreviewVisible,
1068
1074
  setStatusBarVisible,
1069
1075
  setOutlineVisible,
@@ -23,7 +23,9 @@ import { RawEditor } from './RawEditor';
23
23
  import { WysiwygEditor } from './WysiwygEditor';
24
24
  import { InlinePreviewGutter } from './InlinePreviewGutter';
25
25
  import { BlockPreviewPanel } from './BlockPreviewPanel';
26
- import { OutlinePanel } from './OutlinePanel';
26
+ import { OutlinePanel, OUTLINE_RESPONSIVE_WIDTH } from './OutlinePanel';
27
+ import { CodeContextZones } from './codeContext/CodeContextZones';
28
+ import type { CodeContext } from './codeContext/types';
27
29
  import { BlockCardView } from './BlockCardView';
28
30
  import { TimelineTrack } from './TimelineTrack';
29
31
  import { PreviewPanel } from './PreviewPanel';
@@ -32,6 +34,8 @@ import { ImageEditor } from './ImageEditor';
32
34
  import {
33
35
  PreviewSettingsProvider,
34
36
  PreviewToolbarControls,
37
+ PreviewModeSwitch,
38
+ PreviewFormatSwitch,
35
39
  ThemeDesignerDock,
36
40
  } from './PreviewControls';
37
41
  import { CustomThemeProvider, useDocCustomThemes } from './customThemes';
@@ -56,7 +60,7 @@ import {
56
60
  import type { PrunePolicy, SaveVersionResult } from '@bendyline/squisq/versions';
57
61
  import type { CSSProperties, ReactNode } from 'react';
58
62
 
59
- export type { EditorTheme } from './EditorContext';
63
+ export type { EditorColorScheme } from './EditorContext';
60
64
 
61
65
  export interface EditorShellProps {
62
66
  /** Initial markdown content */
@@ -70,8 +74,13 @@ export interface EditorShellProps {
70
74
  basePath?: string;
71
75
  /** Called when markdown source changes */
72
76
  onChange?: (source: string) => void;
73
- /** Color theme: 'light' or 'dark' (default: 'light') */
74
- theme?: 'light' | 'dark';
77
+ /**
78
+ * Light/dark chrome color scheme for the editor shell — toolbar, tabs,
79
+ * status bar, and side panes (default: `'light'`). This is the editor's
80
+ * UI mode, **not** a Squisq `Theme` object; the rendered document's
81
+ * styling is controlled separately via `themeOverride` / `Doc.themeId`.
82
+ */
83
+ colorScheme?: 'light' | 'dark';
75
84
  /** Additional class name */
76
85
  className?: string;
77
86
  /** CSS height for the shell container (default: '100vh') */
@@ -166,6 +175,13 @@ export interface EditorShellProps {
166
175
  * (Slack, Discord). When omitted, the editor behaves normally.
167
176
  */
168
177
  submitOnEnter?: () => void;
178
+ /**
179
+ * Host-supplied context dictionary rendered inside the Monaco (raw / code)
180
+ * surface: collapsible markdown sections injected above anchor lines, plus
181
+ * an optional file-top summary. See {@link CodeContext}. Ignored in
182
+ * WYSIWYG / preview / image surfaces.
183
+ */
184
+ codeContext?: CodeContext;
169
185
  /**
170
186
  * Let the WYSIWYG editing surface fill its container instead of rendering
171
187
  * as a centered 800px "page" column. Useful when embedding in chat
@@ -324,9 +340,11 @@ export interface EditorShellProps {
324
340
  */
325
341
  outline?: boolean;
326
342
  /**
327
- * Width in pixels for the outline pane. Defaults to 240. Only takes
328
- * effect when {@link EditorShellProps.outline} is true (or the View
329
- * menu has toggled it on).
343
+ * Fixed width in pixels for the outline pane. When omitted, the pane sizes
344
+ * responsively never narrower than 260px, growing with the window and
345
+ * capped at 460px — so it stretches out when there's horizontal space to
346
+ * spare. Only takes effect when {@link EditorShellProps.outline} is true (or
347
+ * the View menu has toggled it on).
330
348
  */
331
349
  outlineWidth?: number;
332
350
  /**
@@ -378,7 +396,7 @@ export function EditorShell({
378
396
  articleId = 'untitled',
379
397
  basePath = '/',
380
398
  onChange,
381
- theme = 'light',
399
+ colorScheme = 'light',
382
400
  className,
383
401
  height = '100vh',
384
402
  minHeight,
@@ -397,6 +415,7 @@ export function EditorShell({
397
415
  toolbarSlotRight,
398
416
  showPlayTab = true,
399
417
  submitOnEnter,
418
+ codeContext,
400
419
  fullWidth = false,
401
420
  uxFont,
402
421
  thinMargins = false,
@@ -417,7 +436,7 @@ export function EditorShell({
417
436
  inlinePreview = false,
418
437
  inlinePreviewWidth = 320,
419
438
  outline = false,
420
- outlineWidth = 240,
439
+ outlineWidth,
421
440
  blockTags = true,
422
441
  themeInheritance = 'fonts',
423
442
  viewPreferences,
@@ -449,7 +468,7 @@ export function EditorShell({
449
468
  initialMarkdown={initialMarkdown}
450
469
  initialView={effectiveInitialView}
451
470
  articleId={articleId}
452
- theme={theme}
471
+ colorScheme={colorScheme}
453
472
  workspaceContainer={effectiveContainer}
454
473
  allowVersioning={allowVersioning}
455
474
  versionBasename={versionBasename}
@@ -487,6 +506,7 @@ export function EditorShell({
487
506
  toolbarSlotRight={toolbarSlotRight}
488
507
  showPlayTab={showPlayTab}
489
508
  submitOnEnter={submitOnEnter}
509
+ codeContext={codeContext}
490
510
  fullWidth={fullWidth}
491
511
  uxFont={uxFont}
492
512
  thinMargins={thinMargins}
@@ -522,6 +542,7 @@ interface EditorShellInnerProps {
522
542
  toolbarSlotRight?: ReactNode;
523
543
  showPlayTab: boolean;
524
544
  submitOnEnter?: () => void;
545
+ codeContext?: CodeContext;
525
546
  fullWidth: boolean;
526
547
  uxFont?: string;
527
548
  thinMargins: boolean;
@@ -534,7 +555,7 @@ interface EditorShellInnerProps {
534
555
  allowVersioning: boolean;
535
556
  versioningAutoSaveIdleMs?: number;
536
557
  inlinePreviewWidth: number;
537
- outlineWidth: number;
558
+ outlineWidth?: number;
538
559
  themeOverride: Theme | null;
539
560
  }
540
561
 
@@ -554,6 +575,7 @@ function EditorShellInner({
554
575
  toolbarSlotRight,
555
576
  showPlayTab,
556
577
  submitOnEnter,
578
+ codeContext,
557
579
  fullWidth,
558
580
  uxFont,
559
581
  thinMargins,
@@ -573,7 +595,7 @@ function EditorShellInner({
573
595
  activeView,
574
596
  markdownSource,
575
597
  doc,
576
- theme,
598
+ colorScheme,
577
599
  editorMode,
578
600
  insertAtCursor,
579
601
  replaceAll,
@@ -620,7 +642,7 @@ function EditorShellInner({
620
642
  imageEditFallbackContainerRef.current = new MemoryContentContainer();
621
643
  }
622
644
  const imageEditFallbackContainer = imageEditFallbackContainerRef.current;
623
- const isDark = theme === 'dark';
645
+ const isDark = colorScheme === 'dark';
624
646
 
625
647
  const handleToggleFiles = useCallback(() => {
626
648
  setShowFiles((prev) => !prev);
@@ -761,7 +783,7 @@ function EditorShellInner({
761
783
  return (
762
784
  <div
763
785
  className={`squisq-editor-shell ${className || ''}`}
764
- data-theme={theme}
786
+ data-theme={colorScheme}
765
787
  data-full-width={fullWidth ? 'true' : undefined}
766
788
  data-thin-margins={thinMargins ? 'true' : undefined}
767
789
  data-outline-visible={isMarkdownMode && outlineVisible ? 'true' : undefined}
@@ -775,13 +797,16 @@ function EditorShellInner({
775
797
  // tabs, status bar) consume `--squisq-ux-font` as their
776
798
  // `font-family`, falling back to the system stack when unset.
777
799
  ...(uxFont ? ({ '--squisq-ux-font': uxFont } as CSSProperties) : {}),
778
- // Exposed so the toolbar's view-tabs section can match the outline
779
- // pane's width, lining up its right-edge separator with the
780
- // outline's right edge. The variable is set unconditionally so the
781
- // outline pane itself can also read it if needed; the
782
- // `data-outline-visible` gate above keeps the toolbar override
783
- // scoped to the case where alignment matters.
784
- ...({ '--squisq-outline-width': `${outlineWidth}px` } as CSSProperties),
800
+ // Exposed so the toolbar's view-tabs section AND the outline pane read
801
+ // one shared width, lining up the view-tabs' right-edge separator with
802
+ // the outline's right edge. A fixed `outlineWidth` pins it to px;
803
+ // otherwise it's the responsive clamp so the pane stretches when
804
+ // there's room. The `data-outline-visible` gate above keeps the
805
+ // toolbar override scoped to the case where alignment matters.
806
+ ...({
807
+ '--squisq-outline-width':
808
+ outlineWidth != null ? `${outlineWidth}px` : OUTLINE_RESPONSIVE_WIDTH,
809
+ } as CSSProperties),
785
810
  }}
786
811
  {...containerProps}
787
812
  >
@@ -804,6 +829,16 @@ function EditorShellInner({
804
829
  showFiles={showFiles}
805
830
  onToggleFiles={!isCodeMode && filesToggleEnabled ? handleToggleFiles : undefined}
806
831
  slotLeft={toolbarSlotLeft}
832
+ slotAfterTabs={
833
+ !isCodeMode &&
834
+ isPreview && (
835
+ <div className="squisq-preview-left-controls">
836
+ <PreviewModeSwitch />
837
+ <span className="squisq-preview-seg-divider" aria-hidden="true" />
838
+ <PreviewFormatSwitch />
839
+ </div>
840
+ )
841
+ }
807
842
  slotAfterActions={
808
843
  <>
809
844
  {toolbarSlotAfterActions}
@@ -847,7 +882,7 @@ function EditorShellInner({
847
882
  onExport={onImageExport}
848
883
  />
849
884
  ) : (
850
- <ImageViewer src={imageSrc} alt={imageAlt} theme={theme} />
885
+ <ImageViewer src={imageSrc} alt={imageAlt} theme={colorScheme} />
851
886
  ))}
852
887
  {/* Raw (Monaco) view. Always wrapped in `.squisq-editor-with-gutter`
853
888
  so toggling a pane on/off doesn't change the editor's tree
@@ -870,7 +905,7 @@ function EditorShellInner({
870
905
  >
871
906
  <div key="raw-editor" className="squisq-raw-editor-container">
872
907
  <RawEditor
873
- theme={theme === 'dark' ? 'vs-dark' : 'vs'}
908
+ monacoTheme={colorScheme === 'dark' ? 'vs-dark' : 'vs'}
874
909
  submitOnEnter={submitOnEnter}
875
910
  readOnly={readOnly}
876
911
  />
@@ -879,12 +914,18 @@ function EditorShellInner({
879
914
  ) : (
880
915
  <div key="raw-editor" className="squisq-raw-editor-container">
881
916
  <RawEditor
882
- theme={theme === 'dark' ? 'vs-dark' : 'vs'}
917
+ monacoTheme={colorScheme === 'dark' ? 'vs-dark' : 'vs'}
883
918
  submitOnEnter={submitOnEnter}
884
919
  readOnly={readOnly}
885
920
  />
886
921
  </div>
887
922
  )}
923
+ {/* Renders nothing in normal flow — portals context
924
+ sections into Monaco view zones via the context's
925
+ monacoEditor. Code mode only. */}
926
+ {isCodeMode && codeContext && (
927
+ <CodeContextZones key="code-context" options={codeContext} />
928
+ )}
888
929
  {isMarkdownMode && isCardMode && inlinePreviewVisible && (
889
930
  <BlockPreviewPanel key="block-preview" basePath={basePath} />
890
931
  )}
@@ -1006,7 +1047,7 @@ function EditorShellInner({
1006
1047
  }}
1007
1048
  allowVersioning={allowVersioning}
1008
1049
  versioningAutoSaveIdleMs={versioningAutoSaveIdleMs}
1009
- shellTheme={theme}
1050
+ shellTheme={colorScheme}
1010
1051
  />
1011
1052
  )}
1012
1053
  </div>
@@ -1031,8 +1072,8 @@ interface ImageEditModalProps {
1031
1072
  onSaved: () => void;
1032
1073
  allowVersioning: boolean;
1033
1074
  versioningAutoSaveIdleMs?: number;
1034
- /** EditorShell's `theme` prop — 'light' or 'dark'. Threaded through so
1035
- * the image editor chrome matches the host shell. */
1075
+ /** EditorShell's `colorScheme` prop — 'light' or 'dark'. Threaded through
1076
+ * so the image editor chrome matches the host shell. */
1036
1077
  shellTheme?: 'light' | 'dark';
1037
1078
  }
1038
1079
 
@@ -17,14 +17,30 @@ import { templateLabel } from './TemplatePicker';
17
17
  import { useHeadingLayout } from './useHeadingLayout';
18
18
  import { usePreviewSettingsOptional } from './PreviewControls';
19
19
 
20
+ /**
21
+ * Responsive default width for the outline pane, used when no fixed `width`
22
+ * (or `--squisq-outline-width`) is supplied: never narrower than 260px, grows
23
+ * with the viewport so it stretches out to fill horizontal space when there's
24
+ * room, and caps at 460px so it never dominates the editor. A viewport unit
25
+ * (rather than
26
+ * a container unit) keeps the pane and the toolbar's view-tabs — which read the
27
+ * same value — resolving to an identical width, so their right edges stay
28
+ * aligned.
29
+ */
30
+ export const OUTLINE_RESPONSIVE_WIDTH = 'clamp(260px, 30vw, 460px)';
31
+
20
32
  export interface OutlinePanelProps {
21
- /** Width of the pane in pixels (default: 240). */
33
+ /**
34
+ * Fixed width of the pane in pixels. When omitted, the pane sizes
35
+ * responsively from `--squisq-outline-width` (falling back to
36
+ * {@link OUTLINE_RESPONSIVE_WIDTH}) so it stretches on wider screens.
37
+ */
22
38
  width?: number;
23
39
  /** Optional CSS class for the outer container. */
24
40
  className?: string;
25
41
  }
26
42
 
27
- export function OutlinePanel({ width = 240, className }: OutlinePanelProps) {
43
+ export function OutlinePanel({ width, className }: OutlinePanelProps) {
28
44
  const {
29
45
  doc,
30
46
  markdownSource,
@@ -88,9 +104,15 @@ export function OutlinePanel({ width = 240, className }: OutlinePanelProps) {
88
104
  const accentColor = previewSettings?.activeTheme?.colors?.primary;
89
105
 
90
106
  const isEmpty = !doc || doc.blocks.length === 0 || !hasAnyHeading(doc.blocks);
107
+ // Fixed px when a width is supplied; otherwise size from the shared
108
+ // `--squisq-outline-width` variable (the shell sets it, and the toolbar's
109
+ // view-tabs read the same value so their right edges stay aligned). The
110
+ // clamp fallback lets a standalone OutlinePanel stretch on wide screens too.
111
+ const basis =
112
+ width != null ? `${width}px` : `var(--squisq-outline-width, ${OUTLINE_RESPONSIVE_WIDTH})`;
91
113
  const paneStyle: CSSProperties = {
92
- width: `${width}px`,
93
- flex: `0 0 ${width}px`,
114
+ width: basis,
115
+ flex: `0 0 ${basis}`,
94
116
  overflow: 'auto',
95
117
  ...(accentColor
96
118
  ? ({ ['--squisq-outline-accent' as string]: accentColor } as CSSProperties)