@bendyline/squisq-editor-react 2.11.4 → 2.11.6

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.
@@ -9,10 +9,11 @@ import { FenceRendererMap } from '@bendyline/squisq/fence';
9
9
  import { CodeBlockCopyHandler } from '@bendyline/squisq-react';
10
10
  import { MarkdownDocument } from '@bendyline/squisq/markdown';
11
11
  import { ContentContainer } from '@bendyline/squisq/storage';
12
- import { P as ProofingCapability, b as ProofingIgnoreStore } from './types-B_aHRuSc.js';
12
+ import { a as ProofingCapability, c as ProofingIgnoreStore } from './types-De4z-gGb.js';
13
13
  import { DocumentVersionManager, SaveVersionOptions, SaveVersionResult, PrunePolicy } from '@bendyline/squisq/versions';
14
14
  import { Editor } from '@tiptap/core';
15
15
  import { editor } from 'monaco-editor';
16
+ import { a as ReviewCapability } from './types-C0puuw-M.js';
16
17
 
17
18
  /**
18
19
  * Sidecar bytes → the grid's neutral `IngestTable`, via lazy formats
@@ -149,6 +150,56 @@ interface XlsxFormulaSession {
149
150
  */
150
151
  declare function createXlsxFormulaSession(meta: XlsxSourceMeta, options?: FormulaSessionOptions): Promise<XlsxFormulaSession | null>;
151
152
 
153
+ /**
154
+ * blockRange
155
+ *
156
+ * Source-text-range slicing for the block-at-a-time editing view. Splits a
157
+ * full markdown document into ordered, contiguous slices — one per
158
+ * heading-defined block plus an optional leading preamble — so a single
159
+ * block can be shown in isolation and edits spliced back into the parent.
160
+ *
161
+ * A block runs from its heading line through the character just before the
162
+ * next heading at ANY depth (or EOF). Sub-headings therefore start their own
163
+ * slices — they are NOT folded into their parent — which matches the
164
+ * "don't see child blocks" requirement and the `slicePastHeading` boundary
165
+ * in `blockSlice.ts`. Ranges are half-open `[startOffset, endOffset)` and
166
+ * line-aligned, so trailing blank lines stay with the current block and
167
+ * `spliceBlock(src, range, getBlockSlices(src)[i].text) === src` for every i.
168
+ *
169
+ * These are pure functions over a markdown string — no React, no editor
170
+ * coupling — so any host can reuse them.
171
+ */
172
+ /** Half-open character range into the full source: `[startOffset, endOffset)`. */
173
+ interface BlockRange {
174
+ startOffset: number;
175
+ endOffset: number;
176
+ }
177
+ /** One block's source text plus the range it occupies in the full document. */
178
+ interface BlockSlice {
179
+ text: string;
180
+ range: BlockRange;
181
+ }
182
+ /**
183
+ * Split `fullSource` into ordered block slices.
184
+ *
185
+ * - With no headings, the entire post-frontmatter body is a single slice
186
+ * (so an empty or heading-less document still shows one editable card).
187
+ * - With headings, a leading preamble slice is included only when the text
188
+ * before the first heading has non-whitespace content. Each heading then
189
+ * yields one slice spanning up to the next heading (any depth) or EOF.
190
+ *
191
+ * Frontmatter is never part of any slice — slices start at the body offset.
192
+ */
193
+ declare function getBlockSlices(fullSource: string): BlockSlice[];
194
+ /** Replace the text in `range` with `newText`, returning the new full source. */
195
+ declare function spliceBlock(fullSource: string, range: BlockRange, newText: string): string;
196
+ /** Character offset of the start of 1-based `line` (clamps past EOF). */
197
+ declare function lineToOffset(source: string, line: number): number;
198
+ /** 1-based line number containing `offset`. */
199
+ declare function offsetToLine(source: string, offset: number): number;
200
+ /** Index of the slice whose range contains `offset`, or -1. */
201
+ declare function sliceIndexAtOffset(slices: BlockSlice[], offset: number): number;
202
+
152
203
  /**
153
204
  * Configuration a Scene host (diagram / drawing / layout widget) supplies
154
205
  * so the shared inline text editor knows how to read and write the text of
@@ -230,6 +281,15 @@ interface DocumentLinkCandidate {
230
281
  */
231
282
  type DocumentLinkProvider = (query: string) => Promise<DocumentLinkCandidate[]>;
232
283
  type EditorView = 'raw' | 'wysiwyg' | 'preview';
284
+ /** What is selected in the active editing surface. */
285
+ interface EditorSelectionInfo {
286
+ /** Which surface reported it. */
287
+ view: EditorView;
288
+ /** The selected text, empty when the selection is a bare cursor. */
289
+ text: string;
290
+ /** True when the selection is a bare cursor rather than a range. */
291
+ empty: boolean;
292
+ }
233
293
  /**
234
294
  * Light/dark chrome mode for the editor shell (toolbar, tabs, status bar,
235
295
  * side panes). This is the editor's *UI color scheme* — distinct from a
@@ -375,6 +435,14 @@ interface EditorState {
375
435
  activeBlockKey: number;
376
436
  /** 1-based source line where the active block begins, or null. */
377
437
  activeBlockStartLine: number | null;
438
+ /**
439
+ * Bumped on every selection change in either surface.
440
+ *
441
+ * Exists so a host can gate on "is there a selection" without subscribing to
442
+ * Tiptap or Monaco internals. The counter, rather than the selection itself,
443
+ * is what keeps a toolbar from re-rendering on every keystroke inside one.
444
+ */
445
+ selectionVersion: number;
378
446
  }
379
447
  interface EditorActions {
380
448
  /** Set markdown source and trigger re-parse */
@@ -421,6 +489,28 @@ interface EditorActions {
421
489
  setBlockTagVisibility: (visibility: BlockTagVisibility) => void;
422
490
  /** Change how much of the active Squisq theme the WYSIWYG surface mirrors. */
423
491
  setThemeInheritance: (mode: ThemeInheritance) => void;
492
+ /**
493
+ * The current selection in the active surface, or null when the surface has
494
+ * no selection to report (Preview, or an editor that has not mounted).
495
+ */
496
+ getSelection: () => EditorSelectionInfo | null;
497
+ /**
498
+ * Replace the current selection with markdown, as ONE undoable edit.
499
+ *
500
+ * Returns false when the active surface cannot be edited, so a caller can
501
+ * offer to switch views rather than silently dropping the text. With an
502
+ * empty selection this inserts at the cursor.
503
+ */
504
+ replaceSelection: (markdown: string) => boolean;
505
+ /**
506
+ * The block containing the cursor.
507
+ *
508
+ * Exact in Source view. Null in Write view, where there is no public mapping
509
+ * from a ProseMirror position back to a markdown source offset — returning a
510
+ * plausible-but-wrong block would be worse than returning nothing, since a
511
+ * caller would edit the wrong paragraph.
512
+ */
513
+ getBlockAtCursor: () => BlockSlice | null;
424
514
  /** Insert text at the current cursor position in the active editor */
425
515
  insertAtCursor: (text: string) => void;
426
516
  /** Replace all editor content with the given text */
@@ -1029,6 +1119,29 @@ interface EditorShellProps {
1029
1119
  toolbarSlotRight?: ReactNode;
1030
1120
  /** Host-supplied content rendered at the right edge of the bottom status bar. */
1031
1121
  statusBarSlotRight?: ReactNode;
1122
+ /**
1123
+ * Host-supplied panel docked beside the editor, as a flex sibling of the
1124
+ * preview — so opening it reflows the editor narrower rather than covering
1125
+ * it. Renders nothing when absent.
1126
+ *
1127
+ * The built-in panels (Files, proofing, the theme designer) are fixed
1128
+ * siblings here. This is the seam for a host's own: a review drawer, an
1129
+ * assistant, an inspector. Without it a host has to portal into a DOM node
1130
+ * it owns and give up the editor context in the process.
1131
+ *
1132
+ * Markdown modes only: the image and JSON surfaces have no panel row.
1133
+ */
1134
+ sidePanelSlot?: ReactNode;
1135
+ /**
1136
+ * Review providers, each scheduled independently.
1137
+ *
1138
+ * A list rather than one capability because the providers are independent: a
1139
+ * spellchecker and a document reviewer disagree about latency, about what a
1140
+ * suggestion is, and about which hue they draw in, and a host should be able
1141
+ * to run either without the other. Absent or empty means no review at all —
1142
+ * nothing is scheduled and no provider is constructed.
1143
+ */
1144
+ reviewProviders?: readonly ReviewCapability[];
1032
1145
  /**
1033
1146
  * Whether to show the "Play" (preview) tab in the toolbar. When false, the
1034
1147
  * tab and its preview panel are hidden, and ⌘3 becomes a no-op. Use this
@@ -1377,7 +1490,7 @@ interface EditorShellProps {
1377
1490
  * Complete markdown editor shell with toolbar, view switcher, and three
1378
1491
  * editing modes: Raw (Monaco), WYSIWYG (Tiptap), and Preview.
1379
1492
  */
1380
- 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, showFilesWhenNotEmpty, showFormattingControls, showInsertControls, allowBinaryDownloads, toolbarSlotLeft, toolbarSlotAfterActions, toolbarSlotRight, statusBarSlotRight, showPlayTab, allowPresentationWindow, allowPresentationFullscreen, allowPrint, submitOnEnter, calcEngineFactory, codeContext, fullWidth, uxFont, thinMargins, writeCanvasSettings, showStatusBar, imageDisplayMode, fileName, saveCoverImageOutput, saveDashboardImageOutput, language, findMode, onFindModeChange, mentionProvider, proofing, proofingDefaultEnabled, proofingSpellingEnabled, proofingGrammarEnabled, proofingIgnoreStore, 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;
1493
+ 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, showFilesWhenNotEmpty, showFormattingControls, showInsertControls, allowBinaryDownloads, toolbarSlotLeft, toolbarSlotAfterActions, toolbarSlotRight, statusBarSlotRight, sidePanelSlot, reviewProviders, showPlayTab, allowPresentationWindow, allowPresentationFullscreen, allowPrint, submitOnEnter, calcEngineFactory, codeContext, fullWidth, uxFont, thinMargins, writeCanvasSettings, showStatusBar, imageDisplayMode, fileName, saveCoverImageOutput, saveDashboardImageOutput, language, findMode, onFindModeChange, mentionProvider, proofing, proofingDefaultEnabled, proofingSpellingEnabled, proofingGrammarEnabled, proofingIgnoreStore, 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;
1381
1494
 
1382
1495
  /**
1383
1496
  * Shared editor context menu.
@@ -1527,4 +1640,4 @@ interface PreviewPanelProps {
1527
1640
  */
1528
1641
  declare function PreviewPanel({ basePath, className, workspaceContainer, onLinkClick, showCodeCopyButton, onCopyCode, }: PreviewPanelProps): react_jsx_runtime.JSX.Element;
1529
1642
 
1530
- export { WysiwygEditor as A, type BlockTagVisibility as B, type CodeContext as C, type DashboardImageSaveOutput as D, type EditorHostMode as E, type FormulaEditRecord as F, type WysiwygEditorProps as G, XLSX_LOCKED_REASON as H, type ImageDisplayMode as I, type XlsxFormulaSession as J, createXlsxFormulaSession as K, type LayoutMode as L, type MentionCandidate as M, ingestSidecarBytes as N, useEditorContext as O, PreviewPanel as P, useEditorContextMenuItems as Q, RawEditor as R, type SceneTextChannel as S, type ThemeInheritance as T, type ViewPreferences as V, type WriteCanvasSettings as W, type XlsxSourceMeta as X, type CalcEngineFactory as a, type CsvSourceMeta as b, type CodeContextSection as c, type CoverImageSaveOutput as d, type DocumentLinkCandidate as e, type DocumentLinkProvider as f, type EditorActions as g, type EditorColorScheme as h, type EditorContextMenuContext as i, type EditorContextMenuItem as j, type EditorContextMenuItemVisibility as k, EditorContextMenuProvider as l, type EditorContextMenuProviderProps as m, type EditorContextValue as n, type EditorMode as o, EditorProvider as p, type EditorProviderProps as q, EditorShell as r, type EditorShellProps as s, type EditorState as t, type EditorView as u, type FormulaSessionOptions as v, type IngestedSidecar as w, type MentionProvider as x, type PreviewPanelProps as y, type RawEditorProps as z };
1643
+ export { sliceIndexAtOffset as $, type MentionProvider as A, type BlockRange as B, type CodeContext as C, type DashboardImageSaveOutput as D, type EditorHostMode as E, type FormulaEditRecord as F, type PreviewPanelProps as G, type RawEditorProps as H, type ImageDisplayMode as I, WysiwygEditor as J, type WysiwygEditorProps as K, type LayoutMode as L, type MentionCandidate as M, XLSX_LOCKED_REASON as N, type XlsxFormulaSession as O, PreviewPanel as P, createXlsxFormulaSession as Q, RawEditor as R, type SceneTextChannel as S, type ThemeInheritance as T, getBlockSlices as U, type ViewPreferences as V, type WriteCanvasSettings as W, type XlsxSourceMeta as X, ingestSidecarBytes as Y, lineToOffset as Z, offsetToLine as _, type CalcEngineFactory as a, spliceBlock as a0, useEditorContext as a1, useEditorContextMenuItems as a2, type CsvSourceMeta as b, type BlockSlice as c, type BlockTagVisibility as d, type CodeContextSection as e, type CoverImageSaveOutput as f, type DocumentLinkCandidate as g, type DocumentLinkProvider as h, type EditorActions as i, type EditorColorScheme as j, type EditorContextMenuContext as k, type EditorContextMenuItem as l, type EditorContextMenuItemVisibility as m, EditorContextMenuProvider as n, type EditorContextMenuProviderProps as o, type EditorContextValue as p, type EditorMode as q, EditorProvider as r, type EditorProviderProps as s, type EditorSelectionInfo as t, EditorShell as u, type EditorShellProps as v, type EditorState as w, type EditorView as x, type FormulaSessionOptions as y, type IngestedSidecar as z };
@@ -7110,6 +7110,7 @@
7110
7110
  --squisq-proof-spelling: #d93025;
7111
7111
  --squisq-proof-grammar: #188038;
7112
7112
  --squisq-proof-style: #1a73e8;
7113
+ --squisq-review-assist: #6c4bd6;
7113
7114
  --squisq-find-match: #fde68a;
7114
7115
  --squisq-find-match-current: #f59e0b;
7115
7116
  --squisq-find-match-ring: #b45309;
@@ -7185,6 +7186,7 @@
7185
7186
  --squisq-proof-spelling: #f28b82;
7186
7187
  --squisq-proof-grammar: #81c995;
7187
7188
  --squisq-proof-style: #8ab4f8;
7189
+ --squisq-review-assist: #b39ddb;
7188
7190
  --squisq-tag-text: #ffe3ad;
7189
7191
  --squisq-tag-bg: #3a2818;
7190
7192
  --squisq-tag-border: #d79a45;
@@ -7376,6 +7378,7 @@
7376
7378
  align-self: stretch;
7377
7379
  }
7378
7380
  .squisq-toolbar-use-tab {
7381
+ position: relative;
7379
7382
  border-bottom: 2px solid transparent;
7380
7383
  }
7381
7384
  .squisq-toolbar-use-tab > .squisq-toolbar-view-tab {
@@ -7386,6 +7389,19 @@
7386
7389
  .squisq-toolbar-use-tab--active {
7387
7390
  border-bottom-color: var(--squisq-accent);
7388
7391
  }
7392
+ .squisq-toolbar-use-tab > .squisq-toolbar-view-tab:focus-visible,
7393
+ .squisq-toolbar-use-tab > .squisq-use-mode-trigger:focus-visible {
7394
+ outline: none;
7395
+ }
7396
+ .squisq-toolbar-use-tab:has(> .squisq-toolbar-view-tab:focus-visible, > .squisq-use-mode-trigger:focus-visible)::after {
7397
+ content: "";
7398
+ position: absolute;
7399
+ inset: 2px 0 4px;
7400
+ border: 2px solid var(--squisq-focus-ring);
7401
+ border-radius: 6px;
7402
+ box-sizing: border-box;
7403
+ pointer-events: none;
7404
+ }
7389
7405
  .squisq-use-mode-trigger {
7390
7406
  align-self: stretch;
7391
7407
  display: inline-flex;
@@ -17583,6 +17599,25 @@ button.squisq-ascii-timeline-track-name:focus-visible {
17583
17599
  text-decoration: underline dotted var(--squisq-proof-style) 1.5px;
17584
17600
  text-decoration-skip-ink: none;
17585
17601
  }
17602
+ .squisq-review-underline {
17603
+ text-decoration-skip-ink: none;
17604
+ }
17605
+ .squisq-review-underline--spelling {
17606
+ text-decoration: underline wavy var(--squisq-proof-spelling) 1px;
17607
+ text-decoration-skip-ink: none;
17608
+ }
17609
+ .squisq-review-underline--grammar {
17610
+ text-decoration: underline wavy var(--squisq-proof-grammar) 1px;
17611
+ text-decoration-skip-ink: none;
17612
+ }
17613
+ .squisq-review-underline--style {
17614
+ text-decoration: underline dotted var(--squisq-proof-style) 1.5px;
17615
+ text-decoration-skip-ink: none;
17616
+ }
17617
+ .squisq-review-underline--assist {
17618
+ text-decoration: underline dashed var(--squisq-review-assist) 1.5px;
17619
+ text-decoration-skip-ink: none;
17620
+ }
17586
17621
  .squisq-proof-dot {
17587
17622
  display: inline-block;
17588
17623
  width: 8px;
@@ -17955,6 +17990,137 @@ button.squisq-ascii-timeline-track-name:focus-visible {
17955
17990
  .squisq-proof-tooltip[data-theme=dark] .squisq-proof-tooltip-actions {
17956
17991
  border-top-color: var(--squisq-border-subtle);
17957
17992
  }
17993
+ .squisq-review-panel {
17994
+ width: 260px;
17995
+ flex: 0 0 260px;
17996
+ display: flex;
17997
+ flex-direction: column;
17998
+ overflow-y: auto;
17999
+ border-left: 1px solid var(--squisq-border-subtle);
18000
+ background: var(--squisq-surface-subtle);
18001
+ min-height: 0;
18002
+ font-size: 12px;
18003
+ }
18004
+ .squisq-editor-shell[data-theme=dark] .squisq-review-panel {
18005
+ background: var(--squisq-surface-sunken);
18006
+ border-left-color: var(--squisq-border-subtle);
18007
+ }
18008
+ .squisq-review-panel-header {
18009
+ display: flex;
18010
+ align-items: center;
18011
+ justify-content: space-between;
18012
+ gap: 8px;
18013
+ padding: 6px 10px;
18014
+ border-bottom: 1px solid var(--squisq-border-subtle);
18015
+ }
18016
+ .squisq-review-panel-title {
18017
+ display: flex;
18018
+ align-items: center;
18019
+ gap: 6px;
18020
+ font-weight: 600;
18021
+ color: var(--squisq-text);
18022
+ }
18023
+ .squisq-review-panel-count {
18024
+ color: var(--squisq-text-muted);
18025
+ font-weight: 400;
18026
+ }
18027
+ .squisq-review-panel-button {
18028
+ border: none;
18029
+ background: transparent;
18030
+ color: var(--squisq-text-soft);
18031
+ cursor: pointer;
18032
+ font: inherit;
18033
+ padding: 2px 6px;
18034
+ border-radius: 3px;
18035
+ }
18036
+ .squisq-review-panel-button:hover:not(:disabled) {
18037
+ background: var(--squisq-surface-hover);
18038
+ color: var(--squisq-text);
18039
+ }
18040
+ .squisq-review-panel-button:disabled {
18041
+ cursor: default;
18042
+ opacity: 0.6;
18043
+ }
18044
+ .squisq-review-panel-group-title {
18045
+ margin: 0;
18046
+ padding: 8px 10px 2px;
18047
+ font-size: 11px;
18048
+ font-weight: 600;
18049
+ text-transform: uppercase;
18050
+ letter-spacing: 0.04em;
18051
+ color: var(--squisq-text-muted);
18052
+ }
18053
+ .squisq-review-panel-empty {
18054
+ padding: 4px 10px 8px;
18055
+ color: var(--squisq-text-muted);
18056
+ }
18057
+ .squisq-review-panel-list {
18058
+ list-style: none;
18059
+ margin: 0;
18060
+ padding: 0;
18061
+ }
18062
+ .squisq-review-panel-row {
18063
+ display: flex;
18064
+ align-items: flex-start;
18065
+ gap: 4px;
18066
+ }
18067
+ .squisq-review-panel-row:hover {
18068
+ background: var(--squisq-row-hover);
18069
+ }
18070
+ .squisq-review-panel-row-main {
18071
+ display: flex;
18072
+ align-items: flex-start;
18073
+ gap: 8px;
18074
+ flex: 1 1 auto;
18075
+ min-width: 0;
18076
+ border: none;
18077
+ background: transparent;
18078
+ text-align: left;
18079
+ padding: 6px 4px 6px 10px;
18080
+ cursor: pointer;
18081
+ font: inherit;
18082
+ color: inherit;
18083
+ }
18084
+ .squisq-review-panel-row-body {
18085
+ display: flex;
18086
+ flex-direction: column;
18087
+ gap: 2px;
18088
+ min-width: 0;
18089
+ }
18090
+ .squisq-review-panel-excerpt {
18091
+ color: var(--squisq-text);
18092
+ font-weight: 600;
18093
+ overflow: hidden;
18094
+ text-overflow: ellipsis;
18095
+ white-space: nowrap;
18096
+ }
18097
+ .squisq-review-panel-message {
18098
+ color: var(--squisq-text-soft);
18099
+ }
18100
+ .squisq-review-panel-rationale {
18101
+ color: var(--squisq-text-muted);
18102
+ }
18103
+ .squisq-review-dot {
18104
+ display: inline-block;
18105
+ width: 8px;
18106
+ height: 8px;
18107
+ border-radius: 50%;
18108
+ margin-top: 4px;
18109
+ flex: 0 0 auto;
18110
+ background: var(--squisq-text-muted);
18111
+ }
18112
+ .squisq-review-dot--error {
18113
+ background: var(--squisq-proof-spelling);
18114
+ }
18115
+ .squisq-review-dot--warning {
18116
+ background: var(--squisq-warning);
18117
+ }
18118
+ .squisq-review-dot--suggestion {
18119
+ background: var(--squisq-review-assist);
18120
+ }
18121
+ .squisq-review-dot--info {
18122
+ background: var(--squisq-text-faint);
18123
+ }
17958
18124
 
17959
18125
  /* src/scene/scene.css */
17960
18126
  .squisq-scene-root {
@@ -0,0 +1,81 @@
1
+ import { ReviewRequest, ReviewEvent } from '@bendyline/squisq/review';
2
+
3
+ /**
4
+ * The review capability contract between an editor host and the shell.
5
+ *
6
+ * A host passes providers to `EditorShell`'s `reviewProviders` prop, the same
7
+ * capability-injection semantics as `proofing`: absent means the feature is
8
+ * off and nothing is loaded. Unlike `proofing` this is a list, because the
9
+ * providers are independent — a spellchecker and a document reviewer disagree
10
+ * about latency, about what a suggestion is, and about which hue they draw in,
11
+ * and a host should be able to run either without the other.
12
+ *
13
+ * The surface is plain data end to end, so a provider can be an engine in the
14
+ * page, a call to a local service, or a test fake.
15
+ */
16
+
17
+ /**
18
+ * Which hue a provider's findings draw in.
19
+ *
20
+ * Named rather than free-form so the palette stays a theme decision. The first
21
+ * three are proofing's existing colours; `assist` is the fourth, for a provider
22
+ * whose findings are suggestions rather than mistakes.
23
+ */
24
+ type ReviewHue = 'spelling' | 'grammar' | 'style' | 'assist';
25
+ interface ReviewProvider {
26
+ /**
27
+ * Stable namespace, e.g. `harper`. Prefixes decoration keys and groups the
28
+ * panel, so two providers reporting the same span never collide.
29
+ */
30
+ readonly id: string;
31
+ /** Shown as the panel group header and in the status segment. */
32
+ readonly label: string;
33
+ /** Defaults to `style`. */
34
+ readonly hue?: ReviewHue;
35
+ /**
36
+ * Idle delay before this provider runs, in milliseconds.
37
+ *
38
+ * Per provider because the right answer differs by an order of magnitude:
39
+ * a local spellchecker can re-run 450 ms after a keystroke, while a model
40
+ * that takes seconds and owns the device's accelerator should not. Defaults
41
+ * to a conservative value rather than to proofing's.
42
+ */
43
+ readonly debounceMs?: number;
44
+ /**
45
+ * Prepare the provider. Idempotent, retryable, and where all expensive work
46
+ * belongs. A rejection is not fatal: the editor reports the provider as
47
+ * unavailable and keeps the others running.
48
+ */
49
+ setup?(): Promise<void>;
50
+ /**
51
+ * Review a document.
52
+ *
53
+ * Streaming and cancellable, which is the whole reason this is not `lint`.
54
+ * The editor aborts the signal as soon as the document changes, and a
55
+ * provider must stop promptly rather than finish and return stale findings.
56
+ */
57
+ review(request: ReviewRequest, signal: AbortSignal): AsyncIterable<ReviewEvent>;
58
+ /**
59
+ * Persistently dismiss a finding. Optional: a provider with no notion of
60
+ * "don't tell me again" simply omits it and the editor hides the action.
61
+ */
62
+ dismissFinding?(findingId: string): Promise<void>;
63
+ /** Release resources. Further calls may reject. */
64
+ dispose?(): void;
65
+ }
66
+ /**
67
+ * Deferred construction. A factory's provider is created on first activation
68
+ * and disposed on unmount; a host that passes an instance owns its lifetime,
69
+ * which is what lets one warm engine survive shell remounts.
70
+ */
71
+ type ReviewProviderFactory = () => ReviewProvider;
72
+ /** What each entry of the `reviewProviders` shell prop accepts. */
73
+ type ReviewCapability = ReviewProvider | ReviewProviderFactory;
74
+ /** Resolve a capability to a provider instance. */
75
+ declare function resolveReviewProvider(capability: ReviewCapability): ReviewProvider;
76
+ /** The delay a provider gets when it does not ask for one. */
77
+ declare const DEFAULT_REVIEW_DEBOUNCE_MS = 2000;
78
+ /** The hue a provider draws in when it does not ask for one. */
79
+ declare const DEFAULT_REVIEW_HUE: ReviewHue;
80
+
81
+ export { DEFAULT_REVIEW_DEBOUNCE_MS as D, type ReviewProvider as R, type ReviewCapability as a, type ReviewHue as b, DEFAULT_REVIEW_HUE as c, type ReviewProviderFactory as d, resolveReviewProvider as r };
@@ -153,4 +153,4 @@ interface HarperProofingConfig {
153
153
  binary?: unknown;
154
154
  }
155
155
 
156
- export type { HarperProofingConfig as H, ProofingCapability as P, ProofingDocumentRef as a, ProofingIgnoreStore as b, ProofingLanguage as c, ProofingLintOptions as d, ProofingProvider as e, ProofingProviderFactory as f };
156
+ export type { HarperProofingConfig as H, ProofingProvider as P, ProofingCapability as a, ProofingDocumentRef as b, ProofingIgnoreStore as c, ProofingLanguage as d, ProofingLintOptions as e, ProofingProviderFactory as f };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bendyline/squisq-editor-react",
3
- "version": "2.11.4",
3
+ "version": "2.11.6",
4
4
  "description": "React editor shell with raw/WYSIWYG/preview modes for Squisq documents",
5
5
  "license": "MIT",
6
6
  "author": "Bendyline",
@@ -72,6 +72,10 @@
72
72
  "types": "./dist/proofing/index.d.ts",
73
73
  "import": "./dist/proofing/index.js"
74
74
  },
75
+ "./review": {
76
+ "types": "./dist/review/index.d.ts",
77
+ "import": "./dist/review/index.js"
78
+ },
75
79
  "./styles": {
76
80
  "types": "./dist/styles/index.d.ts",
77
81
  "default": "./dist/styles/index.css"
@@ -94,12 +98,12 @@
94
98
  }
95
99
  },
96
100
  "dependencies": {
97
- "@bendyline/squisq": "2.11.4",
101
+ "@bendyline/squisq": "2.11.6",
98
102
  "@bendyline/squisq-calc": "2.11.0",
99
- "@bendyline/squisq-formats": "2.6.4",
100
- "@bendyline/squisq-grid-react": "2.11.5",
101
- "@bendyline/squisq-react": "2.11.4",
102
- "@bendyline/squisq-video-react": "2.4.8",
103
+ "@bendyline/squisq-formats": "2.6.6",
104
+ "@bendyline/squisq-grid-react": "2.11.7",
105
+ "@bendyline/squisq-react": "2.11.6",
106
+ "@bendyline/squisq-video-react": "2.4.10",
103
107
  "@fortawesome/fontawesome-free": "7.2.0",
104
108
  "@tiptap/core": "2.27.2",
105
109
  "@tiptap/extension-heading": "2.27.2",