osameditor 0.1.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.
- package/LICENSE +21 -0
- package/README.md +452 -0
- package/dist/index.cjs +2665 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +598 -0
- package/dist/index.d.ts +598 -0
- package/dist/index.js +2615 -0
- package/dist/index.js.map +1 -0
- package/dist/styles.css +710 -0
- package/package.json +85 -0
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,598 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { CSSProperties } from 'react';
|
|
3
|
+
import * as _tiptap_core from '@tiptap/core';
|
|
4
|
+
import { AnyExtension, JSONContent, Editor, Node, Extension } from '@tiptap/core';
|
|
5
|
+
import { UseEditorOptions } from '@tiptap/react';
|
|
6
|
+
import { CodeBlockLowlightOptions } from '@tiptap/extension-code-block-lowlight';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Storage layer types.
|
|
10
|
+
*
|
|
11
|
+
* `osameditor` never talks to a storage backend directly. It only ever calls an
|
|
12
|
+
* `UploadHandler`. Two ready-made handlers ship in the box:
|
|
13
|
+
*
|
|
14
|
+
* - `createOsamStorageUploader()` — uploads through the `osamstorage` SDK
|
|
15
|
+
* - `createVpsUploader()` — POSTs the file to your own server / VPS
|
|
16
|
+
*
|
|
17
|
+
* You can also pass any function that matches `UploadHandler` to use a completely
|
|
18
|
+
* custom backend (S3 presigned URL, Cloudinary, UploadThing, etc.).
|
|
19
|
+
*/
|
|
20
|
+
/** The kind of asset. `osamstorage` only accepts these three. */
|
|
21
|
+
type UploadKind = "image" | "video" | "pdf";
|
|
22
|
+
interface UploadContext {
|
|
23
|
+
/** 0 - 100. Called repeatedly while the file transfers. */
|
|
24
|
+
onProgress?: (percent: number) => void;
|
|
25
|
+
/** Abort the upload (wired to the editor / dialog lifecycle). */
|
|
26
|
+
signal?: AbortSignal;
|
|
27
|
+
/** Detected kind, based on the file's MIME type. */
|
|
28
|
+
kind: UploadKind;
|
|
29
|
+
}
|
|
30
|
+
interface UploadResult {
|
|
31
|
+
/** Public URL of the stored asset. Required. */
|
|
32
|
+
url: string;
|
|
33
|
+
/** Defaults to the detected kind. */
|
|
34
|
+
kind?: UploadKind;
|
|
35
|
+
/** Poster / thumbnail image URL (useful for video). */
|
|
36
|
+
poster?: string;
|
|
37
|
+
/** Natural width in px, if known. */
|
|
38
|
+
width?: number;
|
|
39
|
+
/** Natural height in px, if known. */
|
|
40
|
+
height?: number;
|
|
41
|
+
/** Original file name. */
|
|
42
|
+
name?: string;
|
|
43
|
+
/** Anything else your backend returned — kept on the node as `data-*` is not, but available to callbacks. */
|
|
44
|
+
meta?: Record<string, unknown>;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* The single function the editor uses to persist a file.
|
|
48
|
+
* Throw (or reject) to signal failure — the editor surfaces it to the user.
|
|
49
|
+
*/
|
|
50
|
+
type UploadHandler = (file: File, ctx: UploadContext) => Promise<UploadResult>;
|
|
51
|
+
declare function detectKind(file: File): UploadKind;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Every switch you can flip in the toolbar.
|
|
55
|
+
*
|
|
56
|
+
* Pass an array of these to `<OsamEditor toolbar={[...]} />` to build exactly the
|
|
57
|
+
* toolbar you want. `"|"` renders a divider; `"spacer"` pushes the rest to the
|
|
58
|
+
* right. Unknown / disabled features are silently skipped.
|
|
59
|
+
*/
|
|
60
|
+
type ToolbarItem = "bold" | "italic" | "underline" | "strike" | "code" | "color" | "highlight" | "clearFormatting" | "paragraph" | "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "headings" | "alignLeft" | "alignCenter" | "alignRight" | "alignJustify" | "hardBreak" | "bulletList" | "orderedList" | "taskList" | "indent" | "outdent" | "link" | "unlink" | "blockquote" | "codeBlock" | "image" | "imageUrl" | "mediaLibrary" | "youtube" | "instagram" | "video" | "embed" | "pdf" | "horizontalRule" | "undo" | "redo" | "fullscreen" | "source" | "customHtml" | "|" | "spacer";
|
|
61
|
+
type ToolbarPreset = "full" | "blog" | "basic" | "minimal";
|
|
62
|
+
interface HeadingConfig {
|
|
63
|
+
levels?: Array<1 | 2 | 3 | 4 | 5 | 6>;
|
|
64
|
+
}
|
|
65
|
+
interface LinkConfig {
|
|
66
|
+
/** Add `target="_blank"` control in the link dialog. Default `true`. */
|
|
67
|
+
allowTargetBlank?: boolean;
|
|
68
|
+
/** Show the `rel="nofollow"` / `rel="sponsored"` checkboxes. Default `true`. */
|
|
69
|
+
allowRelAttributes?: boolean;
|
|
70
|
+
/** `rel` applied to every link unless overridden in the dialog. */
|
|
71
|
+
defaultRel?: string | null;
|
|
72
|
+
/** Only allow these protocols. Default `["http", "https", "mailto", "tel"]`. */
|
|
73
|
+
protocols?: string[];
|
|
74
|
+
/** Auto-linkify URLs as you type / paste. Default `true`. */
|
|
75
|
+
autolink?: boolean;
|
|
76
|
+
}
|
|
77
|
+
interface ColorConfig {
|
|
78
|
+
/** Swatches shown in the color picker. */
|
|
79
|
+
colors?: string[];
|
|
80
|
+
/** Swatches shown in the highlight picker. */
|
|
81
|
+
highlights?: string[];
|
|
82
|
+
}
|
|
83
|
+
/** Alignment / text-wrap mode for an image. */
|
|
84
|
+
type ImageAlign = "left" | "center" | "right";
|
|
85
|
+
interface ImageConfig {
|
|
86
|
+
/** Allow the end user to drag-resize images. Default `true`. */
|
|
87
|
+
resizable?: boolean;
|
|
88
|
+
/** Allow a caption (`<figcaption>`) under images. Default `true`. */
|
|
89
|
+
caption?: boolean;
|
|
90
|
+
/**
|
|
91
|
+
* Allow alignment / text-wrap. `left` and `right` float the image so body text
|
|
92
|
+
* fills the rest of the row; `center` is a plain block. Default `true`.
|
|
93
|
+
*/
|
|
94
|
+
align?: boolean;
|
|
95
|
+
/** Allow wrapping the image in a link (`<a><img></a>`). Default `true`. */
|
|
96
|
+
link?: boolean;
|
|
97
|
+
/** Accept attribute for the file picker. Default `"image/*"`. */
|
|
98
|
+
accept?: string;
|
|
99
|
+
/** Max file size in bytes. Images above this are rejected before upload. */
|
|
100
|
+
maxSize?: number;
|
|
101
|
+
}
|
|
102
|
+
interface UploadConfig {
|
|
103
|
+
/**
|
|
104
|
+
* Where files go. The only two supported models:
|
|
105
|
+
* - `createOsamStorageUploader(...)` — upload through OsamStorage
|
|
106
|
+
* - `createVpsUploader(...)` — POST to your own backend API
|
|
107
|
+
* (Any function matching `UploadHandler` also works.)
|
|
108
|
+
*
|
|
109
|
+
* If omitted, upload buttons are hidden but "by URL" buttons still work.
|
|
110
|
+
*/
|
|
111
|
+
handler?: UploadHandler;
|
|
112
|
+
/** Accept video uploads (not just embeds). Default `true` when a handler is set. */
|
|
113
|
+
video?: boolean;
|
|
114
|
+
/** Accept PDF uploads. Default `true` when a handler is set. */
|
|
115
|
+
pdf?: boolean;
|
|
116
|
+
/** Global max upload size in bytes. */
|
|
117
|
+
maxSize?: number;
|
|
118
|
+
}
|
|
119
|
+
interface EmbedConfig {
|
|
120
|
+
/**
|
|
121
|
+
* Hostnames allowed for generic `embed` / iframe insertion. `"*"` allows any.
|
|
122
|
+
* Default is a curated list (YouTube, Vimeo, Instagram, Twitter/X, CodePen,
|
|
123
|
+
* CodeSandbox, Spotify, SoundCloud, Google Maps, Loom, Figma, GitHub Gist).
|
|
124
|
+
*/
|
|
125
|
+
allowedHosts?: string[] | "*";
|
|
126
|
+
/** Default aspect ratio (`"16/9"`, `"4/3"`, `"1/1"`, …). Default `"16/9"`. */
|
|
127
|
+
defaultRatio?: string;
|
|
128
|
+
}
|
|
129
|
+
interface MediaLibraryItem {
|
|
130
|
+
url: string;
|
|
131
|
+
name?: string;
|
|
132
|
+
thumbnail?: string;
|
|
133
|
+
}
|
|
134
|
+
interface MediaLibraryConfig {
|
|
135
|
+
/**
|
|
136
|
+
* Your **GET** endpoint (string) or a function returning the list. The editor
|
|
137
|
+
* shows these images in a "Library" tab so the user can re-pick past uploads.
|
|
138
|
+
*/
|
|
139
|
+
list: string | (() => Promise<Array<string | MediaLibraryItem>> | Array<string | MediaLibraryItem>);
|
|
140
|
+
/**
|
|
141
|
+
* Your **POST** endpoint (string) or a function. Called with the image URL
|
|
142
|
+
* after every successful upload so it lands in the user's library. POST body
|
|
143
|
+
* is `{ url }`.
|
|
144
|
+
*/
|
|
145
|
+
save?: string | ((url: string, file?: File) => void | Promise<void>);
|
|
146
|
+
/** Extra headers for the GET / POST requests. */
|
|
147
|
+
headers?: Record<string, string> | (() => Record<string, string> | Promise<Record<string, string>>);
|
|
148
|
+
/** `fetch` credentials mode. Default `"same-origin"`. */
|
|
149
|
+
credentials?: RequestCredentials;
|
|
150
|
+
/** Map an arbitrary GET response shape to the list. */
|
|
151
|
+
resolveList?: (response: any) => Array<string | MediaLibraryItem>;
|
|
152
|
+
}
|
|
153
|
+
interface HtmlConfig {
|
|
154
|
+
/** Show the whole-document "view source" toggle in the toolbar. Default `true`. */
|
|
155
|
+
sourceView?: boolean;
|
|
156
|
+
/** Allow inserting raw "Custom HTML/CSS" blocks. Default `true`. */
|
|
157
|
+
customBlock?: boolean;
|
|
158
|
+
/** Keep `style=""` attributes on paragraphs / headings / quotes. Default `true`. */
|
|
159
|
+
styleAttributes?: boolean;
|
|
160
|
+
/** Keep `class` / `id` attributes on paragraphs / headings / quotes. Default `true`. */
|
|
161
|
+
classAttributes?: boolean;
|
|
162
|
+
/**
|
|
163
|
+
* Also allow `<style>` blocks through the sanitizer. `<script>` and inline
|
|
164
|
+
* event handlers are **always** stripped. Default `false`.
|
|
165
|
+
*/
|
|
166
|
+
allowStyleTags?: boolean;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Per-instance colors. Each maps to a CSS custom property on the editor root, so
|
|
170
|
+
* you can also set these in CSS if you prefer. All optional.
|
|
171
|
+
*/
|
|
172
|
+
interface OsamEditorTheme {
|
|
173
|
+
background?: string;
|
|
174
|
+
foreground?: string;
|
|
175
|
+
muted?: string;
|
|
176
|
+
border?: string;
|
|
177
|
+
accent?: string;
|
|
178
|
+
accentText?: string;
|
|
179
|
+
radius?: string | number;
|
|
180
|
+
fontFamily?: string;
|
|
181
|
+
monoFontFamily?: string;
|
|
182
|
+
/** Toolbar strip background. */
|
|
183
|
+
toolbarBackground?: string;
|
|
184
|
+
/** Toolbar button text/icon color. */
|
|
185
|
+
buttonColor?: string;
|
|
186
|
+
/** Toolbar button hover background. */
|
|
187
|
+
buttonHoverBackground?: string;
|
|
188
|
+
/** Active/"on" toolbar button background. */
|
|
189
|
+
buttonActiveBackground?: string;
|
|
190
|
+
/** Active/"on" toolbar button text color. */
|
|
191
|
+
buttonActiveColor?: string;
|
|
192
|
+
/** Editing surface background. */
|
|
193
|
+
contentBackground?: string;
|
|
194
|
+
/** Code / pre background. */
|
|
195
|
+
codeBackground?: string;
|
|
196
|
+
}
|
|
197
|
+
type OsamEditorLabels = Partial<Record<string, string>>;
|
|
198
|
+
interface OsamEditorConfig {
|
|
199
|
+
/** Which buttons to show. An array of `ToolbarItem`, a preset name, or `false`. */
|
|
200
|
+
toolbar?: ToolbarItem[] | ToolbarPreset | false;
|
|
201
|
+
/** Placeholder shown when the document is empty. */
|
|
202
|
+
placeholder?: string;
|
|
203
|
+
/** Heading levels the schema allows. Also inferred from `h1`–`h6` toolbar items. */
|
|
204
|
+
heading?: HeadingConfig | false;
|
|
205
|
+
link?: LinkConfig | false;
|
|
206
|
+
color?: ColorConfig | false;
|
|
207
|
+
highlight?: ColorConfig | false;
|
|
208
|
+
image?: ImageConfig | false;
|
|
209
|
+
upload?: UploadConfig;
|
|
210
|
+
embed?: EmbedConfig | false;
|
|
211
|
+
/** The user's own image library, backed by their own GET/POST API. */
|
|
212
|
+
mediaLibrary?: MediaLibraryConfig;
|
|
213
|
+
/** Raw HTML / inline-CSS editing (source view + custom HTML block). */
|
|
214
|
+
html?: boolean | HtmlConfig;
|
|
215
|
+
/** Per-instance colors (editor surface, toolbar, buttons). */
|
|
216
|
+
theme?: OsamEditorTheme;
|
|
217
|
+
/** Show the small "powered by osamtech.com" credit. Default `true`. */
|
|
218
|
+
branding?: boolean;
|
|
219
|
+
/** Enable the task/checklist list. Inferred from the `taskList` toolbar item. */
|
|
220
|
+
taskList?: boolean;
|
|
221
|
+
/** Enable code blocks with syntax highlighting + a copy button. Inferred from `codeBlock`. */
|
|
222
|
+
codeBlock?: boolean | {
|
|
223
|
+
defaultLanguage?: string;
|
|
224
|
+
copyButton?: boolean;
|
|
225
|
+
};
|
|
226
|
+
/** Enable blockquotes. Inferred from `blockquote`. */
|
|
227
|
+
blockquote?: boolean;
|
|
228
|
+
/** Enable `<hr>`. Inferred from `horizontalRule`. */
|
|
229
|
+
horizontalRule?: boolean;
|
|
230
|
+
/** Enable text alignment. Inferred from any `align*` toolbar item. */
|
|
231
|
+
textAlign?: boolean | {
|
|
232
|
+
types?: string[];
|
|
233
|
+
alignments?: string[];
|
|
234
|
+
defaultAlignment?: string;
|
|
235
|
+
};
|
|
236
|
+
/** Extra raw TipTap extensions appended to the schema. */
|
|
237
|
+
extensions?: AnyExtension[] | ((base: AnyExtension[]) => AnyExtension[]);
|
|
238
|
+
/** Options forwarded to `StarterKit.configure(...)`. */
|
|
239
|
+
starterKit?: Record<string, unknown>;
|
|
240
|
+
/** Override any UI string. */
|
|
241
|
+
labels?: OsamEditorLabels;
|
|
242
|
+
/** `"ltr"` | `"rtl"`. Default `"ltr"`. */
|
|
243
|
+
dir?: "ltr" | "rtl";
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
interface ResolvedConfig {
|
|
247
|
+
toolbar: ToolbarItem[] | false;
|
|
248
|
+
placeholder: string;
|
|
249
|
+
dir: "ltr" | "rtl";
|
|
250
|
+
labels: Record<string, string>;
|
|
251
|
+
headingLevels: Array<1 | 2 | 3 | 4 | 5 | 6> | false;
|
|
252
|
+
link: false | {
|
|
253
|
+
allowTargetBlank: boolean;
|
|
254
|
+
allowRelAttributes: boolean;
|
|
255
|
+
defaultRel: string | null;
|
|
256
|
+
protocols: string[];
|
|
257
|
+
autolink: boolean;
|
|
258
|
+
};
|
|
259
|
+
color: false | {
|
|
260
|
+
colors: string[];
|
|
261
|
+
};
|
|
262
|
+
highlight: false | {
|
|
263
|
+
colors: string[];
|
|
264
|
+
multicolor: boolean;
|
|
265
|
+
};
|
|
266
|
+
image: false | {
|
|
267
|
+
resizable: boolean;
|
|
268
|
+
caption: boolean;
|
|
269
|
+
align: boolean;
|
|
270
|
+
link: boolean;
|
|
271
|
+
accept: string;
|
|
272
|
+
maxSize?: number;
|
|
273
|
+
allowUrl: boolean;
|
|
274
|
+
allowUpload: boolean;
|
|
275
|
+
};
|
|
276
|
+
upload: {
|
|
277
|
+
hasHandler: boolean;
|
|
278
|
+
video: boolean;
|
|
279
|
+
pdf: boolean;
|
|
280
|
+
maxSize?: number;
|
|
281
|
+
};
|
|
282
|
+
embed: false | {
|
|
283
|
+
allowedHosts: string[] | "*";
|
|
284
|
+
defaultRatio: string;
|
|
285
|
+
};
|
|
286
|
+
mediaLibrary: MediaLibraryConfig | false;
|
|
287
|
+
html: false | {
|
|
288
|
+
sourceView: boolean;
|
|
289
|
+
customBlock: boolean;
|
|
290
|
+
styleAttributes: boolean;
|
|
291
|
+
classAttributes: boolean;
|
|
292
|
+
allowStyleTags: boolean;
|
|
293
|
+
};
|
|
294
|
+
theme: OsamEditorTheme | undefined;
|
|
295
|
+
branding: boolean;
|
|
296
|
+
taskList: boolean;
|
|
297
|
+
codeBlock: false | {
|
|
298
|
+
defaultLanguage?: string;
|
|
299
|
+
copyButton: boolean;
|
|
300
|
+
};
|
|
301
|
+
blockquote: boolean;
|
|
302
|
+
horizontalRule: boolean;
|
|
303
|
+
textAlign: false | {
|
|
304
|
+
types: string[];
|
|
305
|
+
alignments: string[];
|
|
306
|
+
defaultAlignment: string;
|
|
307
|
+
};
|
|
308
|
+
starterKit: Record<string, unknown>;
|
|
309
|
+
}
|
|
310
|
+
declare function resolveConfig(input?: OsamEditorConfig): ResolvedConfig;
|
|
311
|
+
|
|
312
|
+
interface UseOsamEditorOptions extends OsamEditorConfig {
|
|
313
|
+
/** Initial content: an HTML string or TipTap JSON. */
|
|
314
|
+
content?: string | JSONContent | null;
|
|
315
|
+
editable?: boolean;
|
|
316
|
+
autofocus?: UseEditorOptions["autofocus"];
|
|
317
|
+
/** Fired on every change with the current HTML + JSON. */
|
|
318
|
+
onUpdate?: (payload: {
|
|
319
|
+
html: string;
|
|
320
|
+
json: JSONContent;
|
|
321
|
+
editor: Editor;
|
|
322
|
+
}) => void;
|
|
323
|
+
onCreate?: (editor: Editor) => void;
|
|
324
|
+
}
|
|
325
|
+
interface OsamEditorController {
|
|
326
|
+
editor: Editor | null;
|
|
327
|
+
config: ResolvedConfig;
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Lower-level hook: build a TipTap editor from an `OsamEditorConfig`. Use this
|
|
331
|
+
* when you want to render the toolbar and content area yourself. Most people
|
|
332
|
+
* should use `<OsamEditor />`.
|
|
333
|
+
*/
|
|
334
|
+
declare function useOsamEditor(options?: UseOsamEditorOptions): OsamEditorController;
|
|
335
|
+
|
|
336
|
+
interface OsamEditorProps extends OsamEditorConfig {
|
|
337
|
+
/** Uncontrolled initial content (HTML string or TipTap JSON). */
|
|
338
|
+
defaultValue?: string | JSONContent;
|
|
339
|
+
/** Controlled content (HTML string or TipTap JSON). Pair with `onChange`. */
|
|
340
|
+
value?: string | JSONContent;
|
|
341
|
+
/** Called on every edit. `html` is always provided; `json` is the TipTap doc. */
|
|
342
|
+
onChange?: (html: string, extra: {
|
|
343
|
+
json: JSONContent;
|
|
344
|
+
editor: Editor;
|
|
345
|
+
}) => void;
|
|
346
|
+
/** Get the editor instance once it's ready. */
|
|
347
|
+
onReady?: (editor: Editor) => void;
|
|
348
|
+
editable?: boolean;
|
|
349
|
+
autofocus?: UseOsamEditorOptions["autofocus"];
|
|
350
|
+
className?: string;
|
|
351
|
+
style?: React.CSSProperties;
|
|
352
|
+
/** Min height of the editable area. Default `260`. */
|
|
353
|
+
minHeight?: number | string;
|
|
354
|
+
/** Where the toolbar sits. Default `"top"`. */
|
|
355
|
+
toolbarPosition?: "top" | "bottom" | "none";
|
|
356
|
+
/** Stick the toolbar to the top while scrolling. Default `false`. */
|
|
357
|
+
stickyToolbar?: boolean;
|
|
358
|
+
/** Start in fullscreen (fills the viewport). Default `false`. */
|
|
359
|
+
defaultFullscreen?: boolean;
|
|
360
|
+
/** Controlled fullscreen state. */
|
|
361
|
+
fullscreen?: boolean;
|
|
362
|
+
onFullscreenChange?: (fullscreen: boolean) => void;
|
|
363
|
+
}
|
|
364
|
+
declare function OsamEditor(props: OsamEditorProps): React.JSX.Element;
|
|
365
|
+
|
|
366
|
+
interface OsamContentProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
367
|
+
/** HTML produced by the editor (`onChange` html / `editor.getHTML()`). */
|
|
368
|
+
html: string;
|
|
369
|
+
/** Load `hls.js` to play `.m3u8` videos outside Safari. Default `true`. */
|
|
370
|
+
hls?: boolean;
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Render saved editor HTML on your site (blog post page, preview, …) with the
|
|
374
|
+
* same look as the editor. Pair with `import "osameditor/styles.css"`.
|
|
375
|
+
*/
|
|
376
|
+
declare function OsamContent({ html, hls, className, ...rest }: OsamContentProps): React.JSX.Element;
|
|
377
|
+
|
|
378
|
+
interface ToolbarView {
|
|
379
|
+
fullscreen?: boolean;
|
|
380
|
+
onToggleFullscreen?: () => void;
|
|
381
|
+
sourceMode?: boolean;
|
|
382
|
+
onToggleSource?: () => void;
|
|
383
|
+
}
|
|
384
|
+
interface ToolbarProps {
|
|
385
|
+
editor: Editor;
|
|
386
|
+
config: ResolvedConfig;
|
|
387
|
+
uploadHandler?: UploadHandler;
|
|
388
|
+
view?: ToolbarView;
|
|
389
|
+
}
|
|
390
|
+
declare function Toolbar({ editor, config, uploadHandler, view }: ToolbarProps): React.JSX.Element | null;
|
|
391
|
+
|
|
392
|
+
declare function buildExtensions(config: ResolvedConfig): AnyExtension[];
|
|
393
|
+
declare function applyUserExtensions(base: AnyExtension[], extra: AnyExtension[] | ((base: AnyExtension[]) => AnyExtension[]) | undefined): AnyExtension[];
|
|
394
|
+
|
|
395
|
+
declare const TOOLBAR_PRESETS: Record<ToolbarPreset, ToolbarItem[]>;
|
|
396
|
+
declare function resolveToolbar(toolbar: ToolbarItem[] | ToolbarPreset | false | undefined): ToolbarItem[] | false;
|
|
397
|
+
|
|
398
|
+
/** Turn a `theme` prop into inline CSS custom properties for the editor root. */
|
|
399
|
+
declare function themeToStyle(theme: OsamEditorTheme | undefined): CSSProperties;
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Small, dependency-free HTML sanitizer for user-authored markup coming from the
|
|
403
|
+
* source view / custom-HTML block / paste.
|
|
404
|
+
*
|
|
405
|
+
* Policy: `<script>` and inline event handlers (`on*`) are **always** removed;
|
|
406
|
+
* `javascript:` URLs are neutralised. `<style>` blocks are removed unless
|
|
407
|
+
* `allowStyleTags` is set. Everything else is kept — this is deliberately
|
|
408
|
+
* permissive so authors can write real HTML/CSS, and matches the
|
|
409
|
+
* "inline style + class" trust level.
|
|
410
|
+
*/
|
|
411
|
+
interface SanitizeOptions {
|
|
412
|
+
allowStyleTags?: boolean;
|
|
413
|
+
}
|
|
414
|
+
declare function sanitizeHtml(input: string, opts?: SanitizeOptions): string;
|
|
415
|
+
|
|
416
|
+
/** GET the user's image library through their own API. */
|
|
417
|
+
declare function loadMediaLibrary(cfg: MediaLibraryConfig): Promise<MediaLibraryItem[]>;
|
|
418
|
+
/** POST a freshly-uploaded image URL into the user's library. Best-effort. */
|
|
419
|
+
declare function saveToMediaLibrary(cfg: MediaLibraryConfig, url: string, file?: File): Promise<void>;
|
|
420
|
+
|
|
421
|
+
interface OsamStorageUploaderOptions {
|
|
422
|
+
/**
|
|
423
|
+
* Returns a short-lived OsamStorage token. Same contract as the `osamstorage`
|
|
424
|
+
* SDK: point it at your backend route that exchanges your secret MAIN_TOKEN
|
|
425
|
+
* for a temporary token.
|
|
426
|
+
*
|
|
427
|
+
* @example
|
|
428
|
+
* getToken: async () => {
|
|
429
|
+
* const r = await fetch("/api/osam-token");
|
|
430
|
+
* return (await r.json()).token;
|
|
431
|
+
* }
|
|
432
|
+
*/
|
|
433
|
+
getToken: () => Promise<string> | string;
|
|
434
|
+
/** Passed straight through to `new OsamStorage(...)`. */
|
|
435
|
+
baseUrl?: string;
|
|
436
|
+
chunkSize?: number;
|
|
437
|
+
concurrency?: number;
|
|
438
|
+
maxAttempts?: number;
|
|
439
|
+
/**
|
|
440
|
+
* OsamStorage processes files asynchronously and the merge response shape can
|
|
441
|
+
* evolve, so you map it to a public URL here. The default tries the most
|
|
442
|
+
* common fields.
|
|
443
|
+
*/
|
|
444
|
+
resolveUrl?: (data: any, file: File) => string | undefined | null;
|
|
445
|
+
/** Optional: map the merge response to a poster/thumbnail URL. */
|
|
446
|
+
resolvePoster?: (data: any, file: File) => string | undefined | null;
|
|
447
|
+
/**
|
|
448
|
+
* Provide the SDK instance yourself instead of letting this helper import
|
|
449
|
+
* `osamstorage`. Useful if you already configured one, or to control which
|
|
450
|
+
* adapter build is used.
|
|
451
|
+
*/
|
|
452
|
+
client?: {
|
|
453
|
+
upload: (file: any, opts: any) => Promise<any>;
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* Upload handler backed by the `osamstorage` SDK (chunked upload, automatic
|
|
458
|
+
* image → WebP, video → HLS, PDF passthrough).
|
|
459
|
+
*
|
|
460
|
+
* `osamstorage` is an **optional** dependency — it's only required if you
|
|
461
|
+
* actually use this handler.
|
|
462
|
+
*/
|
|
463
|
+
declare function createOsamStorageUploader(options: OsamStorageUploaderOptions): UploadHandler;
|
|
464
|
+
|
|
465
|
+
interface VpsUploaderOptions {
|
|
466
|
+
/**
|
|
467
|
+
* Your upload endpoint. Receives a `multipart/form-data` POST with the file
|
|
468
|
+
* under `fieldName` (default `"file"`).
|
|
469
|
+
*
|
|
470
|
+
* Can be a string or a function of the file (e.g. to route images and video
|
|
471
|
+
* to different endpoints).
|
|
472
|
+
*/
|
|
473
|
+
endpoint: string | ((file: File, kind: ReturnType<typeof detectKind>) => string);
|
|
474
|
+
/** Form field name for the file. Default `"file"`. */
|
|
475
|
+
fieldName?: string;
|
|
476
|
+
/** Extra static form fields sent with every upload. */
|
|
477
|
+
fields?: Record<string, string> | ((file: File) => Record<string, string>);
|
|
478
|
+
/** Extra headers (auth tokens, etc.). Don't set `Content-Type` — the browser does. */
|
|
479
|
+
headers?: Record<string, string> | (() => Record<string, string> | Promise<Record<string, string>>);
|
|
480
|
+
/** `fetch` credentials mode. Default `"same-origin"`. */
|
|
481
|
+
credentials?: RequestCredentials;
|
|
482
|
+
/**
|
|
483
|
+
* Map your JSON response to a public URL. Default tries `url`, `fileUrl`,
|
|
484
|
+
* `location`, `src`, `data.url`.
|
|
485
|
+
*/
|
|
486
|
+
resolveUrl?: (data: any, file: File) => string | undefined | null;
|
|
487
|
+
/** Map your JSON response to a poster/thumbnail URL. */
|
|
488
|
+
resolvePoster?: (data: any, file: File) => string | undefined | null;
|
|
489
|
+
}
|
|
490
|
+
/**
|
|
491
|
+
* Upload handler that POSTs the file to your own server / VPS storage.
|
|
492
|
+
*
|
|
493
|
+
* Reports real progress via `XMLHttpRequest` (so the editor's progress bar
|
|
494
|
+
* works) and supports cancellation through `AbortSignal`.
|
|
495
|
+
*/
|
|
496
|
+
declare function createVpsUploader(options: VpsUploaderOptions): UploadHandler;
|
|
497
|
+
|
|
498
|
+
interface ResizableImageOptions {
|
|
499
|
+
resizable: boolean;
|
|
500
|
+
caption: boolean;
|
|
501
|
+
align: boolean;
|
|
502
|
+
link: boolean;
|
|
503
|
+
HTMLAttributes: Record<string, unknown>;
|
|
504
|
+
}
|
|
505
|
+
/**
|
|
506
|
+
* Block image with alt / title / caption / alignment / drag-resize, and an
|
|
507
|
+
* optional wrapping link. `left` / `right` alignment **float** the image so body
|
|
508
|
+
* text fills the rest of the row; `center` is a plain block.
|
|
509
|
+
* Serializes to `<figure><a?><img></a?><figcaption?></figure>`.
|
|
510
|
+
*/
|
|
511
|
+
declare const ResizableImage: _tiptap_core.Node<ResizableImageOptions, any>;
|
|
512
|
+
|
|
513
|
+
type EmbedProvider = "youtube" | "vimeo" | "instagram" | "twitter" | "video" | "pdf" | "iframe";
|
|
514
|
+
interface ResolvedEmbed {
|
|
515
|
+
provider: EmbedProvider;
|
|
516
|
+
/** URL to load in the iframe / <video> / <object>. */
|
|
517
|
+
src: string;
|
|
518
|
+
/** The URL the user pasted. */
|
|
519
|
+
originalUrl: string;
|
|
520
|
+
}
|
|
521
|
+
/**
|
|
522
|
+
* Turn a pasted URL into something embeddable. Returns `null` if we can't.
|
|
523
|
+
*/
|
|
524
|
+
declare function resolveEmbed(raw: string, opts: {
|
|
525
|
+
allowedHosts: string[] | "*";
|
|
526
|
+
treatAsPdf?: boolean;
|
|
527
|
+
treatAsVideo?: boolean;
|
|
528
|
+
}): ResolvedEmbed | null;
|
|
529
|
+
|
|
530
|
+
interface EmbedAttributes {
|
|
531
|
+
src: string | null;
|
|
532
|
+
provider: EmbedProvider;
|
|
533
|
+
ratio: string;
|
|
534
|
+
title: string | null;
|
|
535
|
+
originalUrl: string | null;
|
|
536
|
+
align: "left" | "center" | "right" | null;
|
|
537
|
+
width: string | null;
|
|
538
|
+
}
|
|
539
|
+
interface EmbedOptions {
|
|
540
|
+
defaultRatio: string;
|
|
541
|
+
HTMLAttributes: Record<string, unknown>;
|
|
542
|
+
}
|
|
543
|
+
declare module "@tiptap/core" {
|
|
544
|
+
interface Commands<ReturnType> {
|
|
545
|
+
embed: {
|
|
546
|
+
setEmbed: (attrs: Partial<EmbedAttributes> & {
|
|
547
|
+
src: string;
|
|
548
|
+
}) => ReturnType;
|
|
549
|
+
updateEmbed: (attrs: Partial<EmbedAttributes>) => ReturnType;
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
/**
|
|
554
|
+
* One atom block node for every kind of rich media that isn't an image:
|
|
555
|
+
* YouTube / Vimeo / Instagram / generic iframe, self-hosted `<video>`
|
|
556
|
+
* (with lazy HLS support) and PDF documents.
|
|
557
|
+
*/
|
|
558
|
+
declare const Embed: Node<EmbedOptions, any>;
|
|
559
|
+
|
|
560
|
+
type CodeBlockOptions = Partial<CodeBlockLowlightOptions> & {
|
|
561
|
+
copyButton: boolean;
|
|
562
|
+
languages: string[];
|
|
563
|
+
};
|
|
564
|
+
/**
|
|
565
|
+
* `CodeBlockLowlight` + a header with a language picker and a "Copy" button so
|
|
566
|
+
* readers can copy the snippet.
|
|
567
|
+
*/
|
|
568
|
+
declare const CodeBlock: _tiptap_core.Node<CodeBlockOptions, any>;
|
|
569
|
+
|
|
570
|
+
interface CustomHtmlOptions {
|
|
571
|
+
allowStyleTags: boolean;
|
|
572
|
+
}
|
|
573
|
+
declare module "@tiptap/core" {
|
|
574
|
+
interface Commands<ReturnType> {
|
|
575
|
+
customHtml: {
|
|
576
|
+
setCustomHtml: (html?: string) => ReturnType;
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
/**
|
|
581
|
+
* A raw HTML/CSS block. Renders sanitized markup; double-click (or the Edit
|
|
582
|
+
* button) to edit the source in a textarea.
|
|
583
|
+
*/
|
|
584
|
+
declare const CustomHtml: Node<CustomHtmlOptions, any>;
|
|
585
|
+
|
|
586
|
+
interface PreserveAttributesOptions {
|
|
587
|
+
types: string[];
|
|
588
|
+
style: boolean;
|
|
589
|
+
classAttr: boolean;
|
|
590
|
+
}
|
|
591
|
+
/**
|
|
592
|
+
* Keeps `style` / `class` / `id` attributes on block nodes so inline CSS written
|
|
593
|
+
* in the source view (or pasted) survives a round-trip through the schema.
|
|
594
|
+
* Scoped to text blocks by default — not images/embeds, which manage their own.
|
|
595
|
+
*/
|
|
596
|
+
declare const PreserveAttributes: Extension<PreserveAttributesOptions, any>;
|
|
597
|
+
|
|
598
|
+
export { CodeBlock, type ColorConfig, CustomHtml, Embed, type EmbedConfig, type HeadingConfig, type HtmlConfig, type ImageAlign, type ImageConfig, type LinkConfig, type MediaLibraryConfig, type MediaLibraryItem, OsamContent, type OsamContentProps, OsamEditor, type OsamEditorConfig, type OsamEditorController, type OsamEditorProps, type OsamEditorTheme, type OsamStorageUploaderOptions, PreserveAttributes, ResizableImage, type ResolvedConfig, type SanitizeOptions, TOOLBAR_PRESETS, Toolbar, type ToolbarItem, type ToolbarPreset, type UploadConfig, type UploadContext, type UploadHandler, type UploadKind, type UploadResult, type UseOsamEditorOptions, type VpsUploaderOptions, applyUserExtensions, buildExtensions, createOsamStorageUploader, createVpsUploader, detectKind, loadMediaLibrary, resolveConfig, resolveEmbed, resolveToolbar, sanitizeHtml, saveToMediaLibrary, themeToStyle, useOsamEditor };
|