@bendyline/squisq-editor-react 2.8.0 → 2.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,156 @@
1
+ import { ProofFinding, ProofDialect } from '@bendyline/squisq/proof';
2
+
3
+ /**
4
+ * The proofing capability contract between an editor host and the shell.
5
+ *
6
+ * A host that wants grammar/spellcheck passes a {@link ProofingProvider}
7
+ * (or a factory) to `EditorShell`'s `proofing` prop — the same
8
+ * capability-injection semantics as `ffmpegWasm`: absent means the
9
+ * feature is off, and there is no CDN fallback. The provider surface is
10
+ * plain data end to end, deliberately free of harper.js types so the
11
+ * engine stays an optional peer dependency that never appears in this
12
+ * package's published declarations.
13
+ */
14
+
15
+ /** Markup mode for one lint call. */
16
+ type ProofingLanguage = 'plaintext' | 'markdown';
17
+ interface ProofingLintOptions {
18
+ /** Defaults to `plaintext`. */
19
+ language?: ProofingLanguage;
20
+ }
21
+ /**
22
+ * An engine adapter the editor drives. Implementations must serialize
23
+ * their own engine access (calls may arrive while a previous one is in
24
+ * flight) and keep `lint` results resolvable by finding id until the
25
+ * next `lint` call.
26
+ */
27
+ interface ProofingProvider {
28
+ /**
29
+ * Load the engine (module + WASM). Idempotent and safe to call
30
+ * repeatedly; a rejection is retryable — a later call attempts a
31
+ * fresh load. All expensive work belongs here, never in construction.
32
+ */
33
+ setup(): Promise<void>;
34
+ /** Lint `text`; finding offsets index into `text` (UTF-16 units). */
35
+ lint(text: string, options?: ProofingLintOptions): Promise<ProofFinding[]>;
36
+ /**
37
+ * Persistently ignore the finding's underlying lint — including any
38
+ * stacked same-span lints other rules reveal once the first is
39
+ * suppressed. `findingId` must come from the most recent `lint` call.
40
+ */
41
+ ignoreFinding(findingId: string): Promise<void>;
42
+ /**
43
+ * Accept one word into the APP-level dictionary — fires the host's
44
+ * `onDictionaryWord` so it can persist wherever it keeps user
45
+ * settings. Backs the menu's "Add to dictionary".
46
+ */
47
+ addWord(word: string): Promise<void>;
48
+ /**
49
+ * Accept words for this session without notifying the host. Used both
50
+ * to load a document's own word list at setup and to back the menu's
51
+ * "Add to document word list" (where the DOC's frontmatter, not the
52
+ * app, is the store of record).
53
+ */
54
+ addWords(words: readonly string[]): Promise<void>;
55
+ /**
56
+ * Whether app-level additions actually persist — i.e. the host wired
57
+ * `onDictionaryWord`. When `false`, the editor hides "Add to
58
+ * dictionary" so a word can never appear to be saved app-wide and
59
+ * then silently come back. `undefined` (a provider that doesn't
60
+ * report) is treated as capable.
61
+ */
62
+ readonly hasAppDictionary?: boolean;
63
+ /**
64
+ * The engine's ignored-findings state as an OPAQUE string — it holds
65
+ * context hashes as integers above 2^53, so it must never be
66
+ * JSON-parsed or rebuilt, only stored and handed back verbatim.
67
+ */
68
+ exportIgnored(): Promise<string>;
69
+ /** Merge previously exported ignored-findings state into the engine. */
70
+ importIgnored(json: string): Promise<void>;
71
+ /**
72
+ * Drop all ignored findings. The editor calls this when the engine's
73
+ * ignore set belongs to a different document than the one about to be
74
+ * linted — ignores are per-document, but an engine instance may be
75
+ * shared across documents and shells.
76
+ */
77
+ clearIgnored(): Promise<void>;
78
+ /** Switch English dialect. The caller re-lints afterwards. */
79
+ setDialect(dialect: ProofDialect): Promise<void>;
80
+ /** Release engine resources. Further calls reject. */
81
+ dispose(): void;
82
+ }
83
+ /**
84
+ * Deferred construction. When the shell receives a factory it creates
85
+ * the provider on first activation and disposes it on unmount; a host
86
+ * that passes an instance owns its lifetime (letting one warm engine
87
+ * survive shell remounts).
88
+ */
89
+ type ProofingProviderFactory = () => ProofingProvider;
90
+ /** What the `proofing` shell prop accepts. */
91
+ type ProofingCapability = ProofingProvider | ProofingProviderFactory;
92
+ /**
93
+ * Identifies the document a set of ignored findings belongs to. Hosts
94
+ * key their storage off whichever field suits them — usually the file
95
+ * path, falling back to the article id for unsaved buffers.
96
+ */
97
+ interface ProofingDocumentRef {
98
+ /** The document's `articleId` (always present; `'untitled'` default). */
99
+ articleId: string;
100
+ /** The document's file name / path, when the host supplied one. */
101
+ fileName?: string;
102
+ }
103
+ /**
104
+ * Host-owned persistence for dismissed ("Ignore") findings.
105
+ *
106
+ * Ignores are deliberately never written into the document — they are
107
+ * one person's editing preference, and a doc travelling through git
108
+ * shouldn't carry them. Instead the editor hands the state to the host,
109
+ * scoped per document, and the host stores it wherever its user data
110
+ * lives (a settings file, IndexedDB, a per-workspace record keyed by
111
+ * path…). The payload is the engine's OPAQUE export: store it as a
112
+ * string, never parse it.
113
+ *
114
+ * Omit the store and Ignore still works — it just lasts for the
115
+ * session, which is often the right behavior for a scratch buffer.
116
+ */
117
+ interface ProofingIgnoreStore {
118
+ /** Previously saved state for this document, if any. */
119
+ load(doc: ProofingDocumentRef): string | undefined | Promise<string | undefined>;
120
+ /** Persist this document's state. Called after every Ignore. */
121
+ save(doc: ProofingDocumentRef, ignoredJson: string): void | Promise<void>;
122
+ }
123
+ /** Configuration for {@link createHarperProofingProvider}. */
124
+ interface HarperProofingConfig {
125
+ /**
126
+ * Same-origin URL of harper's WASM binary (the host serves
127
+ * `harper.js/dist/harper_wasm_bg.wasm` itself — see docs/proofing.md).
128
+ * When omitted, the `harper.js/binary` module default is used, which
129
+ * relies on the host bundler resolving the WASM as an asset.
130
+ */
131
+ wasmUrl?: string;
132
+ /** Initial dialect (default American). */
133
+ dialect?: ProofDialect;
134
+ /** App-level dictionary loaded during setup. */
135
+ initialWords?: readonly string[];
136
+ /**
137
+ * Fired when the user adds a word app-wide — persist it host-side.
138
+ * Its absence reports `hasAppDictionary: false`, which hides the
139
+ * "Add to dictionary" menu item.
140
+ *
141
+ * Note the asymmetry with ignores: a dictionary word is app-global
142
+ * (a proper noun is spelled the same everywhere), so it belongs on
143
+ * the engine's config. Ignores are per-document and go through
144
+ * `EditorShell`'s `proofingIgnoreStore` instead.
145
+ */
146
+ onDictionaryWord?: (word: string) => void;
147
+ /**
148
+ * Escape hatch: a preconstructed harper `BinaryModule`. Typed
149
+ * `unknown` so harper types stay out of published declarations
150
+ * (Node tests pass `binaryInlined` here — harper's `file://` loader
151
+ * is broken on Windows).
152
+ */
153
+ binary?: unknown;
154
+ }
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 };
@@ -188,6 +188,16 @@ interface UseMediaRecorderResult {
188
188
  directory: 'audio' | 'video' | null;
189
189
  /** Milliseconds elapsed since `start()` was called. Updates ~10× per second while recording. */
190
190
  durationMs: number;
191
+ /**
192
+ * Milliseconds since `start()`, read at the moment of the call — or null
193
+ * when no take is rolling.
194
+ *
195
+ * The companion of `durationMs`, for callers that need to STAMP an event
196
+ * rather than display a clock. `durationMs` is a 10 Hz state value and so
197
+ * lags by up to one tick, which is fine for a readout but not for recording
198
+ * when the presenter advanced a slide.
199
+ */
200
+ getElapsedMs: () => number | null;
191
201
  /** Most recent error, if any. */
192
202
  error: Error | null;
193
203
  /**
@@ -557,4 +567,4 @@ interface NarrationStageHandle {
557
567
  }
558
568
  declare function useNarrationStage(opts: UseNarrationStageOptions): NarrationStageHandle;
559
569
 
560
- export { useMediaRecorder as A, useMicAnalysis as B, type CanvasSink as C, DEFAULT_TELEPROMPTER_PREFS as D, useNarrationRecorder as E, type FloatOpenOptions as F, useNarrationStage as G, useTeleprompter as H, vadConfigForSensitivity as I, TELEPROMPTER_PREF_LIMITS as J, normalizeTeleprompterPrefs as K, type MicAnalysisHandle as M, type NarrationRecorderController as N, type PrompterTransport as P, type RecorderAudioBitrateMode as R, type TeleprompterController as T, type UseMediaRecorderOptions as U, type CaptureKind as a, type FloatTier as b, type FloatingWindowHandle as c, type FloatingWindowManager as d, type MicAnalysisStatus as e, type NarrationRecorderState as f, type NarrationStageHandle as g, type NarrationTake as h, type RecorderCameraLane as i, type RecorderExtendedMediaOptions as j, type RecorderSource as k, type RecorderState as l, type ResolvedFormat as m, type TeleprompterPrefs as n, type TeleprompterRecordingDeps as o, type UseMediaRecorderResult as p, type UseNarrationStageOptions as q, buildFilename as r, createFloatingWindowManager as s, detectFloatTiers as t, getCaptureKind as u, resolveFormat as v, supportsDisplayMedia as w, supportsMediaRecorder as x, supportsUserMedia as y, useFloatingWindow as z };
570
+ export { useMediaRecorder as A, useMicAnalysis as B, type CanvasSink as C, DEFAULT_TELEPROMPTER_PREFS as D, useNarrationRecorder as E, type FloatOpenOptions as F, useNarrationStage as G, useTeleprompter as H, vadConfigForSensitivity as I, TELEPROMPTER_PREF_LIMITS as J, normalizeTeleprompterPrefs as K, type MicAnalysisHandle as M, type NarrationRecorderController as N, type PrompterTransport as P, type RecorderState as R, type TeleprompterController as T, type UseMediaRecorderOptions as U, type CaptureKind as a, type FloatTier as b, type FloatingWindowHandle as c, type FloatingWindowManager as d, type MicAnalysisStatus as e, type NarrationRecorderState as f, type NarrationStageHandle as g, type NarrationTake as h, type RecorderAudioBitrateMode as i, type RecorderCameraLane as j, type RecorderExtendedMediaOptions as k, type RecorderSource as l, type ResolvedFormat as m, type TeleprompterPrefs as n, type TeleprompterRecordingDeps as o, type UseMediaRecorderResult as p, type UseNarrationStageOptions as q, buildFilename as r, createFloatingWindowManager as s, detectFloatTiers as t, getCaptureKind as u, resolveFormat as v, supportsDisplayMedia as w, supportsMediaRecorder as x, supportsUserMedia as y, useFloatingWindow as z };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bendyline/squisq-editor-react",
3
- "version": "2.8.0",
3
+ "version": "2.10.0",
4
4
  "description": "React editor shell with raw/WYSIWYG/preview modes for Squisq documents",
5
5
  "license": "MIT",
6
6
  "author": "Bendyline",
@@ -68,6 +68,10 @@
68
68
  "types": "./dist/teleprompter/index.d.ts",
69
69
  "import": "./dist/teleprompter/index.js"
70
70
  },
71
+ "./proofing": {
72
+ "types": "./dist/proofing/index.d.ts",
73
+ "import": "./dist/proofing/index.js"
74
+ },
71
75
  "./styles": {
72
76
  "types": "./dist/styles/index.d.ts",
73
77
  "default": "./dist/styles/index.css"
@@ -79,15 +83,21 @@
79
83
  "typecheck": "tsc --noEmit"
80
84
  },
81
85
  "peerDependencies": {
86
+ "harper.js": "^2.7.0",
82
87
  "monaco-editor": "~0.50.0",
83
88
  "react": "^18.0.0 || ^19.0.0",
84
89
  "react-dom": "^18.0.0 || ^19.0.0"
85
90
  },
91
+ "peerDependenciesMeta": {
92
+ "harper.js": {
93
+ "optional": true
94
+ }
95
+ },
86
96
  "dependencies": {
87
- "@bendyline/squisq": "2.8.0",
88
- "@bendyline/squisq-formats": "2.4.5",
89
- "@bendyline/squisq-react": "2.8.0",
90
- "@bendyline/squisq-video-react": "2.4.0",
97
+ "@bendyline/squisq": "2.10.0",
98
+ "@bendyline/squisq-formats": "2.5.0",
99
+ "@bendyline/squisq-react": "2.10.0",
100
+ "@bendyline/squisq-video-react": "2.4.2",
91
101
  "@fortawesome/fontawesome-free": "7.2.0",
92
102
  "@tiptap/core": "2.27.2",
93
103
  "@tiptap/extension-heading": "2.27.2",
@@ -112,6 +122,7 @@
112
122
  "@monaco-editor/react": "4.7.0",
113
123
  "@types/react": "18.3.28",
114
124
  "@types/react-dom": "18.3.7",
125
+ "harper.js": "2.7.0",
115
126
  "monaco-editor": "0.50.0",
116
127
  "react": "18.3.1",
117
128
  "react-dom": "18.3.1",