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.
@@ -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 };