@railway/inkwell 1.1.1 → 1.2.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/dist/index.cjs +34 -16
- package/dist/index.d.cts +6 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +34 -16
- package/package.json +1 -1
- package/src/styles.css +19 -17
package/dist/index.cjs
CHANGED
|
@@ -1377,6 +1377,7 @@ var InkwellEditorClient = react.forwardRef(
|
|
|
1377
1377
|
);
|
|
1378
1378
|
const overLimit = characterLimit !== void 0 && characterCount > characterLimit;
|
|
1379
1379
|
const hasCharacterLimit = characterLimit !== void 0;
|
|
1380
|
+
const showCharacterCount = characterLimit !== void 0 && characterCount >= characterLimit * 0.8;
|
|
1380
1381
|
const getEditorState = react.useCallback(() => {
|
|
1381
1382
|
const content2 = serializeContent();
|
|
1382
1383
|
return {
|
|
@@ -2026,7 +2027,7 @@ var InkwellEditorClient = react.forwardRef(
|
|
|
2026
2027
|
className: `inkwell-editor-wrapper${hasCharacterLimit ? " inkwell-editor-has-character-limit" : ""}${overLimit ? " inkwell-editor-over-limit" : ""}${className ? ` ${className}` : ""}${classNames?.root ? ` ${classNames.root}` : ""}`,
|
|
2027
2028
|
style: styles?.root,
|
|
2028
2029
|
children: [
|
|
2029
|
-
|
|
2030
|
+
showCharacterCount && /* @__PURE__ */ jsxRuntime.jsx(
|
|
2030
2031
|
CharacterCount,
|
|
2031
2032
|
{
|
|
2032
2033
|
count: characterCount,
|
|
@@ -2185,16 +2186,41 @@ var insertUploadedAttachment = (file, options) => {
|
|
|
2185
2186
|
options.onError?.(err, file);
|
|
2186
2187
|
});
|
|
2187
2188
|
};
|
|
2188
|
-
function
|
|
2189
|
+
function routeFiles(editor, files, options) {
|
|
2189
2190
|
const { accept } = options;
|
|
2191
|
+
const matching = accept ? files.filter((f) => mimeMatches(f.type, accept)) : files;
|
|
2192
|
+
const handled = matching.filter(
|
|
2193
|
+
(f) => isImageFile(f) || options.onAttachmentAdd !== void 0
|
|
2194
|
+
);
|
|
2195
|
+
for (const file of handled) {
|
|
2196
|
+
if (isImageFile(file)) {
|
|
2197
|
+
insertUploadedImage(editor, file, options);
|
|
2198
|
+
} else {
|
|
2199
|
+
insertUploadedAttachment(file, options);
|
|
2200
|
+
}
|
|
2201
|
+
}
|
|
2202
|
+
const skipped = files.filter((f) => !handled.includes(f));
|
|
2203
|
+
return { handled, skipped };
|
|
2204
|
+
}
|
|
2205
|
+
function createAttachmentsPlugin(options) {
|
|
2190
2206
|
return {
|
|
2191
2207
|
name: "attachments",
|
|
2208
|
+
setup(editor) {
|
|
2209
|
+
if (!options.ref) return;
|
|
2210
|
+
const writableRef = options.ref;
|
|
2211
|
+
writableRef.current = {
|
|
2212
|
+
upload: (files) => {
|
|
2213
|
+
if (files.length === 0) return;
|
|
2214
|
+
routeFiles(editor, files, options);
|
|
2215
|
+
}
|
|
2216
|
+
};
|
|
2217
|
+
return () => {
|
|
2218
|
+
writableRef.current = null;
|
|
2219
|
+
};
|
|
2220
|
+
},
|
|
2192
2221
|
onInsertData(data, { editor, insertData }) {
|
|
2193
2222
|
const files = extractFiles(data);
|
|
2194
|
-
const
|
|
2195
|
-
const handled = matching.filter(
|
|
2196
|
-
(f) => isImageFile(f) || options.onAttachmentAdd !== void 0
|
|
2197
|
-
);
|
|
2223
|
+
const { handled, skipped } = routeFiles(editor, files, options);
|
|
2198
2224
|
if (handled.length === 0) {
|
|
2199
2225
|
const htmlImages = extractHtmlImages(data);
|
|
2200
2226
|
if (htmlImages.length === 0) return false;
|
|
@@ -2203,16 +2229,8 @@ function createAttachmentsPlugin(options) {
|
|
|
2203
2229
|
}
|
|
2204
2230
|
return true;
|
|
2205
2231
|
}
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
insertData(filesOnlyDataTransfer(unhandled));
|
|
2209
|
-
}
|
|
2210
|
-
for (const file of handled) {
|
|
2211
|
-
if (isImageFile(file)) {
|
|
2212
|
-
insertUploadedImage(editor, file, options);
|
|
2213
|
-
} else {
|
|
2214
|
-
insertUploadedAttachment(file, options);
|
|
2215
|
-
}
|
|
2232
|
+
if (skipped.length > 0) {
|
|
2233
|
+
insertData(filesOnlyDataTransfer(skipped));
|
|
2216
2234
|
}
|
|
2217
2235
|
return true;
|
|
2218
2236
|
}
|
package/dist/index.d.cts
CHANGED
|
@@ -280,6 +280,9 @@ interface Attachment {
|
|
|
280
280
|
*/
|
|
281
281
|
[key: string]: unknown;
|
|
282
282
|
}
|
|
283
|
+
interface AttachmentsHandle {
|
|
284
|
+
upload: (files: File[]) => void;
|
|
285
|
+
}
|
|
283
286
|
interface AttachmentsPluginOptions {
|
|
284
287
|
/**
|
|
285
288
|
* Upload a single file and resolve to the public URL, or an object
|
|
@@ -292,6 +295,8 @@ interface AttachmentsPluginOptions {
|
|
|
292
295
|
* (`image/*`). Files that don't match pass through untouched.
|
|
293
296
|
*/
|
|
294
297
|
accept?: string;
|
|
298
|
+
/** Populated on editor mount, nulled on unmount. */
|
|
299
|
+
ref?: RefObject<AttachmentsHandle | null>;
|
|
295
300
|
/**
|
|
296
301
|
* Placeholder alt text shown on the inserted image element while an
|
|
297
302
|
* image upload is in flight. Defaults to `"Uploading…"`.
|
|
@@ -484,4 +489,4 @@ declare function InkwellRenderer({ content, className, components, rehypePlugins
|
|
|
484
489
|
*/
|
|
485
490
|
declare function parseMarkdown(content: string, options?: ParseMarkdownOptions): ReactNode;
|
|
486
491
|
|
|
487
|
-
export { type Attachment, type AttachmentUploadResult, type AttachmentsPluginOptions, type BubbleMenuItem, type BubbleMenuItemProps, type BubbleMenuOptions, type CompletionsPluginOptions, type EmojiItem, type EmojiPluginOptions, type InkwellComponents, InkwellEditor, type InkwellEditorClassNames, type InkwellEditorFocusOptions, type InkwellEditorHandle, type InkwellEditorProps, type InkwellEditorState, type InkwellEditorStyles, type InkwellFeatures, type InkwellPlugin, type InkwellPluginActivation, type InkwellPluginEditor, type InkwellPluginPlaceholder, InkwellRenderer, type InkwellRendererProps, type MentionItem, type MentionRenderer, type MentionsPluginOptions, type ParseMarkdownOptions, type PluginInsertDataContext, type PluginKeyDownContext, type PluginRenderProps, type RehypePluginConfig, type SlashCommandArg, type SlashCommandChoice, type SlashCommandExecution, type SlashCommandItem, type SlashCommandsPluginOptions, type Snippet, type SnippetsPluginOptions, type SubscribeForwardedKey, createAttachmentsPlugin, createBubbleMenuPlugin, createCompletionsPlugin, createEmojiPlugin, createMentionsPlugin, createSlashCommandsPlugin, createSnippetsPlugin, defaultBubbleMenuItems, defaultEmojis, htmlToMarkdown, parseMarkdown };
|
|
492
|
+
export { type Attachment, type AttachmentUploadResult, type AttachmentsHandle, type AttachmentsPluginOptions, type BubbleMenuItem, type BubbleMenuItemProps, type BubbleMenuOptions, type CompletionsPluginOptions, type EmojiItem, type EmojiPluginOptions, type InkwellComponents, InkwellEditor, type InkwellEditorClassNames, type InkwellEditorFocusOptions, type InkwellEditorHandle, type InkwellEditorProps, type InkwellEditorState, type InkwellEditorStyles, type InkwellFeatures, type InkwellPlugin, type InkwellPluginActivation, type InkwellPluginEditor, type InkwellPluginPlaceholder, InkwellRenderer, type InkwellRendererProps, type MentionItem, type MentionRenderer, type MentionsPluginOptions, type ParseMarkdownOptions, type PluginInsertDataContext, type PluginKeyDownContext, type PluginRenderProps, type RehypePluginConfig, type SlashCommandArg, type SlashCommandChoice, type SlashCommandExecution, type SlashCommandItem, type SlashCommandsPluginOptions, type Snippet, type SnippetsPluginOptions, type SubscribeForwardedKey, createAttachmentsPlugin, createBubbleMenuPlugin, createCompletionsPlugin, createEmojiPlugin, createMentionsPlugin, createSlashCommandsPlugin, createSnippetsPlugin, defaultBubbleMenuItems, defaultEmojis, htmlToMarkdown, parseMarkdown };
|
package/dist/index.d.ts
CHANGED
|
@@ -280,6 +280,9 @@ interface Attachment {
|
|
|
280
280
|
*/
|
|
281
281
|
[key: string]: unknown;
|
|
282
282
|
}
|
|
283
|
+
interface AttachmentsHandle {
|
|
284
|
+
upload: (files: File[]) => void;
|
|
285
|
+
}
|
|
283
286
|
interface AttachmentsPluginOptions {
|
|
284
287
|
/**
|
|
285
288
|
* Upload a single file and resolve to the public URL, or an object
|
|
@@ -292,6 +295,8 @@ interface AttachmentsPluginOptions {
|
|
|
292
295
|
* (`image/*`). Files that don't match pass through untouched.
|
|
293
296
|
*/
|
|
294
297
|
accept?: string;
|
|
298
|
+
/** Populated on editor mount, nulled on unmount. */
|
|
299
|
+
ref?: RefObject<AttachmentsHandle | null>;
|
|
295
300
|
/**
|
|
296
301
|
* Placeholder alt text shown on the inserted image element while an
|
|
297
302
|
* image upload is in flight. Defaults to `"Uploading…"`.
|
|
@@ -484,4 +489,4 @@ declare function InkwellRenderer({ content, className, components, rehypePlugins
|
|
|
484
489
|
*/
|
|
485
490
|
declare function parseMarkdown(content: string, options?: ParseMarkdownOptions): ReactNode;
|
|
486
491
|
|
|
487
|
-
export { type Attachment, type AttachmentUploadResult, type AttachmentsPluginOptions, type BubbleMenuItem, type BubbleMenuItemProps, type BubbleMenuOptions, type CompletionsPluginOptions, type EmojiItem, type EmojiPluginOptions, type InkwellComponents, InkwellEditor, type InkwellEditorClassNames, type InkwellEditorFocusOptions, type InkwellEditorHandle, type InkwellEditorProps, type InkwellEditorState, type InkwellEditorStyles, type InkwellFeatures, type InkwellPlugin, type InkwellPluginActivation, type InkwellPluginEditor, type InkwellPluginPlaceholder, InkwellRenderer, type InkwellRendererProps, type MentionItem, type MentionRenderer, type MentionsPluginOptions, type ParseMarkdownOptions, type PluginInsertDataContext, type PluginKeyDownContext, type PluginRenderProps, type RehypePluginConfig, type SlashCommandArg, type SlashCommandChoice, type SlashCommandExecution, type SlashCommandItem, type SlashCommandsPluginOptions, type Snippet, type SnippetsPluginOptions, type SubscribeForwardedKey, createAttachmentsPlugin, createBubbleMenuPlugin, createCompletionsPlugin, createEmojiPlugin, createMentionsPlugin, createSlashCommandsPlugin, createSnippetsPlugin, defaultBubbleMenuItems, defaultEmojis, htmlToMarkdown, parseMarkdown };
|
|
492
|
+
export { type Attachment, type AttachmentUploadResult, type AttachmentsHandle, type AttachmentsPluginOptions, type BubbleMenuItem, type BubbleMenuItemProps, type BubbleMenuOptions, type CompletionsPluginOptions, type EmojiItem, type EmojiPluginOptions, type InkwellComponents, InkwellEditor, type InkwellEditorClassNames, type InkwellEditorFocusOptions, type InkwellEditorHandle, type InkwellEditorProps, type InkwellEditorState, type InkwellEditorStyles, type InkwellFeatures, type InkwellPlugin, type InkwellPluginActivation, type InkwellPluginEditor, type InkwellPluginPlaceholder, InkwellRenderer, type InkwellRendererProps, type MentionItem, type MentionRenderer, type MentionsPluginOptions, type ParseMarkdownOptions, type PluginInsertDataContext, type PluginKeyDownContext, type PluginRenderProps, type RehypePluginConfig, type SlashCommandArg, type SlashCommandChoice, type SlashCommandExecution, type SlashCommandItem, type SlashCommandsPluginOptions, type Snippet, type SnippetsPluginOptions, type SubscribeForwardedKey, createAttachmentsPlugin, createBubbleMenuPlugin, createCompletionsPlugin, createEmojiPlugin, createMentionsPlugin, createSlashCommandsPlugin, createSnippetsPlugin, defaultBubbleMenuItems, defaultEmojis, htmlToMarkdown, parseMarkdown };
|
package/dist/index.js
CHANGED
|
@@ -1362,6 +1362,7 @@ var InkwellEditorClient = forwardRef(
|
|
|
1362
1362
|
);
|
|
1363
1363
|
const overLimit = characterLimit !== void 0 && characterCount > characterLimit;
|
|
1364
1364
|
const hasCharacterLimit = characterLimit !== void 0;
|
|
1365
|
+
const showCharacterCount = characterLimit !== void 0 && characterCount >= characterLimit * 0.8;
|
|
1365
1366
|
const getEditorState = useCallback(() => {
|
|
1366
1367
|
const content2 = serializeContent();
|
|
1367
1368
|
return {
|
|
@@ -2011,7 +2012,7 @@ var InkwellEditorClient = forwardRef(
|
|
|
2011
2012
|
className: `inkwell-editor-wrapper${hasCharacterLimit ? " inkwell-editor-has-character-limit" : ""}${overLimit ? " inkwell-editor-over-limit" : ""}${className ? ` ${className}` : ""}${classNames?.root ? ` ${classNames.root}` : ""}`,
|
|
2012
2013
|
style: styles?.root,
|
|
2013
2014
|
children: [
|
|
2014
|
-
|
|
2015
|
+
showCharacterCount && /* @__PURE__ */ jsx(
|
|
2015
2016
|
CharacterCount,
|
|
2016
2017
|
{
|
|
2017
2018
|
count: characterCount,
|
|
@@ -2170,16 +2171,41 @@ var insertUploadedAttachment = (file, options) => {
|
|
|
2170
2171
|
options.onError?.(err, file);
|
|
2171
2172
|
});
|
|
2172
2173
|
};
|
|
2173
|
-
function
|
|
2174
|
+
function routeFiles(editor, files, options) {
|
|
2174
2175
|
const { accept } = options;
|
|
2176
|
+
const matching = accept ? files.filter((f) => mimeMatches(f.type, accept)) : files;
|
|
2177
|
+
const handled = matching.filter(
|
|
2178
|
+
(f) => isImageFile(f) || options.onAttachmentAdd !== void 0
|
|
2179
|
+
);
|
|
2180
|
+
for (const file of handled) {
|
|
2181
|
+
if (isImageFile(file)) {
|
|
2182
|
+
insertUploadedImage(editor, file, options);
|
|
2183
|
+
} else {
|
|
2184
|
+
insertUploadedAttachment(file, options);
|
|
2185
|
+
}
|
|
2186
|
+
}
|
|
2187
|
+
const skipped = files.filter((f) => !handled.includes(f));
|
|
2188
|
+
return { handled, skipped };
|
|
2189
|
+
}
|
|
2190
|
+
function createAttachmentsPlugin(options) {
|
|
2175
2191
|
return {
|
|
2176
2192
|
name: "attachments",
|
|
2193
|
+
setup(editor) {
|
|
2194
|
+
if (!options.ref) return;
|
|
2195
|
+
const writableRef = options.ref;
|
|
2196
|
+
writableRef.current = {
|
|
2197
|
+
upload: (files) => {
|
|
2198
|
+
if (files.length === 0) return;
|
|
2199
|
+
routeFiles(editor, files, options);
|
|
2200
|
+
}
|
|
2201
|
+
};
|
|
2202
|
+
return () => {
|
|
2203
|
+
writableRef.current = null;
|
|
2204
|
+
};
|
|
2205
|
+
},
|
|
2177
2206
|
onInsertData(data, { editor, insertData }) {
|
|
2178
2207
|
const files = extractFiles(data);
|
|
2179
|
-
const
|
|
2180
|
-
const handled = matching.filter(
|
|
2181
|
-
(f) => isImageFile(f) || options.onAttachmentAdd !== void 0
|
|
2182
|
-
);
|
|
2208
|
+
const { handled, skipped } = routeFiles(editor, files, options);
|
|
2183
2209
|
if (handled.length === 0) {
|
|
2184
2210
|
const htmlImages = extractHtmlImages(data);
|
|
2185
2211
|
if (htmlImages.length === 0) return false;
|
|
@@ -2188,16 +2214,8 @@ function createAttachmentsPlugin(options) {
|
|
|
2188
2214
|
}
|
|
2189
2215
|
return true;
|
|
2190
2216
|
}
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
insertData(filesOnlyDataTransfer(unhandled));
|
|
2194
|
-
}
|
|
2195
|
-
for (const file of handled) {
|
|
2196
|
-
if (isImageFile(file)) {
|
|
2197
|
-
insertUploadedImage(editor, file, options);
|
|
2198
|
-
} else {
|
|
2199
|
-
insertUploadedAttachment(file, options);
|
|
2200
|
-
}
|
|
2217
|
+
if (skipped.length > 0) {
|
|
2218
|
+
insertData(filesOnlyDataTransfer(skipped));
|
|
2201
2219
|
}
|
|
2202
2220
|
return true;
|
|
2203
2221
|
}
|
package/package.json
CHANGED
package/src/styles.css
CHANGED
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
--inkwell-accent: hsl(217, 91%, 50%);
|
|
32
32
|
--inkwell-accent-soft: hsla(217, 91%, 50%, 0.12);
|
|
33
33
|
--inkwell-danger: hsl(0, 72%, 51%);
|
|
34
|
+
--inkwell-danger-soft: hsla(0, 72%, 51%, 0.14);
|
|
34
35
|
|
|
35
36
|
/* Inline code */
|
|
36
37
|
--inkwell-code-bg: hsl(220, 14%, 94%);
|
|
@@ -61,6 +62,8 @@
|
|
|
61
62
|
|
|
62
63
|
--inkwell-accent: hsl(217, 91%, 65%);
|
|
63
64
|
--inkwell-accent-soft: hsla(217, 91%, 65%, 0.16);
|
|
65
|
+
--inkwell-danger: hsl(0, 70%, 65%);
|
|
66
|
+
--inkwell-danger-soft: hsla(0, 70%, 65%, 0.18);
|
|
64
67
|
|
|
65
68
|
--inkwell-code-bg: hsl(220, 13%, 18%);
|
|
66
69
|
--inkwell-code-fg: hsl(340, 70%, 75%);
|
|
@@ -175,36 +178,35 @@
|
|
|
175
178
|
height: auto;
|
|
176
179
|
}
|
|
177
180
|
|
|
178
|
-
/* Built-in character count.
|
|
179
|
-
wrapper
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
181
|
+
/* Built-in character count. Overlays the top-right corner of the editor
|
|
182
|
+
wrapper so it never shifts content, and sits on a solid surface tint
|
|
183
|
+
so it visually layers above any text that wraps underneath. Only
|
|
184
|
+
rendered once typing reaches 80% of `characterLimit`, since the
|
|
185
|
+
limit is a soft hint — typing past it is allowed; the count then
|
|
186
|
+
turns red and the wrapper picks up `.inkwell-editor-over-limit`,
|
|
187
|
+
which paints a soft red halo on the editor surface so it's visually
|
|
188
|
+
obvious the document is over budget. */
|
|
183
189
|
.inkwell-editor-character-count {
|
|
184
190
|
position: absolute;
|
|
185
|
-
|
|
186
|
-
|
|
191
|
+
top: 0.5rem;
|
|
192
|
+
right: 0.5rem;
|
|
187
193
|
z-index: 10;
|
|
188
|
-
padding: 0.
|
|
194
|
+
padding: 0.1rem 0.4rem;
|
|
189
195
|
font-size: 0.72rem;
|
|
190
196
|
font-variant-numeric: tabular-nums;
|
|
191
197
|
color: var(--inkwell-text-dim);
|
|
198
|
+
background: var(--inkwell-bg);
|
|
199
|
+
border-radius: calc(var(--inkwell-radius) - 2px);
|
|
192
200
|
pointer-events: none;
|
|
193
201
|
user-select: none;
|
|
194
202
|
}
|
|
195
203
|
.inkwell-editor-character-count-over {
|
|
196
|
-
color:
|
|
204
|
+
color: var(--inkwell-danger);
|
|
197
205
|
font-weight: 500;
|
|
198
206
|
}
|
|
199
|
-
:where(
|
|
200
|
-
.inkwell-editor-wrapper.inkwell-editor-has-character-limit .inkwell-editor
|
|
201
|
-
) {
|
|
202
|
-
padding-right: 5.25rem;
|
|
203
|
-
padding-bottom: 2.25rem;
|
|
204
|
-
}
|
|
205
207
|
.inkwell-editor-wrapper.inkwell-editor-over-limit .inkwell-editor {
|
|
206
|
-
border-color:
|
|
207
|
-
box-shadow: 0 0 0
|
|
208
|
+
border-color: var(--inkwell-danger-soft);
|
|
209
|
+
box-shadow: 0 0 0 3px var(--inkwell-danger-soft);
|
|
208
210
|
}
|
|
209
211
|
|
|
210
212
|
.inkwell-editor-backtick,
|