@slash-editor/core 0.0.5 → 0.0.7

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.d.mts CHANGED
@@ -22,6 +22,12 @@ interface SlashItem {
22
22
  aliases?: string[];
23
23
  /** Lower-weight search terms that are never displayed. */
24
24
  keywords?: string[];
25
+ /**
26
+ * Markdown input-rule shorthand shown as a hint in the menu (`#`, `---`).
27
+ * Display only — ranking never reads it, and it must mirror a rule the
28
+ * editor actually registers.
29
+ */
30
+ shortcut?: string;
25
31
  /** Icon key resolved by the UI layer; the core ships no components. */
26
32
  icon?: string;
27
33
  /** Hides the item when the current editor cannot run it. */
@@ -913,6 +919,54 @@ declare const Mention: Node<MentionOptions, MentionStorage>;
913
919
  /** Configures the mention node. `items` is required — there is no default provider. */
914
920
  declare function mention(options: Partial<MentionOptions> & Pick<MentionOptions, "items">): Node<MentionOptions, MentionStorage>;
915
921
  //#endregion
922
+ //#region src/placeholder.d.ts
923
+ /**
924
+ * Slot a placeholder string is looked up under. Slots are resolved from the
925
+ * empty node *and its parent*: the empty node inside a list item, a task item,
926
+ * a quote or a callout is a plain `paragraph`, so node type alone cannot tell
927
+ * those cases apart.
928
+ */
929
+ type PlaceholderKey = "paragraph" | "heading1" | "heading2" | "heading3" | "listItem" | "taskItem" | "blockquote" | "callout" | "details";
930
+ interface PlaceholderContext {
931
+ editor: Editor;
932
+ /** The empty block holding the caret. */
933
+ node: Node$1;
934
+ /** Its parent, which distinguishes a bare paragraph from a list/quote child. */
935
+ parent: Node$1 | null;
936
+ /** Position directly before `node`. */
937
+ pos: number;
938
+ }
939
+ interface PlaceholderOptions {
940
+ /** Per-slot overrides; unset slots keep the default string. */
941
+ text?: Partial<Record<PlaceholderKey, string>>;
942
+ /**
943
+ * Full override. Wins over `text`; returning `null` suppresses the
944
+ * placeholder for that block.
945
+ */
946
+ resolve?: (context: PlaceholderContext) => string | null;
947
+ }
948
+ declare const defaultPlaceholderText: Record<PlaceholderKey, string>;
949
+ /**
950
+ * Maps an empty block to its placeholder slot, or `null` when it should stay
951
+ * blank. Code blocks are excluded: their content is literal, and a hint would
952
+ * read as source.
953
+ */
954
+ declare function placeholderKeyFor(node: Node$1, parent: Node$1 | null): PlaceholderKey | null;
955
+ declare const placeholderPluginKey: PluginKey<any>;
956
+ /**
957
+ * Empty-block hints, Notion style: only the block holding the caret shows one.
958
+ *
959
+ * Renders `data-placeholder` and no class names — the UI layer styles
960
+ * `[data-placeholder]::before`, matching how every other core node is styled.
961
+ *
962
+ * The decoration is derived from the state it is drawn against rather than
963
+ * from `editor.state`: during a transaction those are different documents, and
964
+ * resolving a parent in the stale one mislabels every nested block.
965
+ */
966
+ declare const Placeholder: Extension<PlaceholderOptions, any>;
967
+ /** Configures empty-block placeholders. */
968
+ declare function placeholder(options?: PlaceholderOptions): Extension<PlaceholderOptions, any>;
969
+ //#endregion
916
970
  //#region src/slash-command.d.ts
917
971
  interface SlashMenuState {
918
972
  open: boolean;
@@ -947,6 +1001,12 @@ interface SlashCommandOptions {
947
1001
  char: string;
948
1002
  /** Item registry, or a resolver called with the live editor. */
949
1003
  items: SlashItem[] | ((editor: Editor) => SlashItem[]);
1004
+ /**
1005
+ * Inline hint rendered next to the trigger character while the query is
1006
+ * still empty, the way Notion prompts after `/`. Set to `""` to drop it.
1007
+ * The UI layer renders it from `data-decoration-content`.
1008
+ */
1009
+ hint: string;
950
1010
  /**
951
1011
  * Called when an item's `run` throws. The failing transaction is never
952
1012
  * applied, so the document keeps the state it had before the item ran.
@@ -1045,6 +1105,13 @@ interface BlockKitOptions {
1045
1105
  * @default { char: "/", items: defaultSlashItems }
1046
1106
  */
1047
1107
  slash?: Partial<SlashCommandOptions> | false;
1108
+ /**
1109
+ * Empty-block placeholder hints, or `false` to render none. Only the block
1110
+ * holding the caret shows one.
1111
+ *
1112
+ * @default {}
1113
+ */
1114
+ placeholder?: PlaceholderOptions | false;
1048
1115
  /**
1049
1116
  * Stable per-block id configuration, or `false` to opt out. Drag
1050
1117
  * targeting and the future comment/collaboration surfaces depend on it.
@@ -1188,4 +1255,4 @@ declare const Callout: Node<CalloutOptions, any>;
1188
1255
  /** Configures the callout node. */
1189
1256
  declare function callout(options?: Partial<CalloutOptions>): Node<CalloutOptions, any>;
1190
1257
  //#endregion
1191
- export { type AiActionStatus, AiBlock, type AiBlockOptions, type AiBlockStorage, type AiKitOptions, type AiRequest, type AiSlashAction, BLOCK_ID_REMOTE_META, BlockDrag, type BlockDragOptions, type BlockDragState, type BlockDragStorage, BlockId, type BlockIdOptions, type BlockKitOptions, type BlockLocation, type BlockRect, type BlockTarget, BubbleToolbar, type BubbleToolbarItem, type BubbleToolbarOptions, type BubbleToolbarState, type BubbleToolbarStorage, Callout, type CalloutOptions, type CollaborationOptions, type CollaborationProvider, type CollaborationUser, Column, type ColumnOptions, Columns, type ColumnsOptions, Comment, type CommentMessage, type CommentOptions, type CommentState, type CommentStorage, type CommentThread, type CommentThreadStore, type DropMode, type DropTarget, Embed, type EmbedMode, type EmbedOptions, File$1 as File, type FileOptions, type FileStorage, type HeadingLevel, Image, type ImageOptions, type ImageStorage, LinkEditor, type LinkEditorOptions, type LinkEditorState, type LinkEditorStorage, Mention, type MentionItem, type MentionOptions, type MentionState, type MentionStorage, PendingAiRegistry, PendingUploadRegistry, type RetryUploadOptions, type RunAiActionOptions, type RunUploadOptions, type SetEmbedOptions, type SetFileFromFile, type SetFileFromSrc, type SetFileOptions, type SetImageFromFile, type SetImageFromSrc, type SetImageOptions, type SetVideoFromFile, type SetVideoFromSrc, type SetVideoOptions, SlashCommand, type SlashCommandOptions, type SlashCommandStorage, type SlashContext, type SlashItem, type SlashMenuState, type StreamAdapter, type StreamContext, TableKit, type TableKitOptions, type UploadAdapter, type UploadContext, type UploadResult, type UploadStatus, Video, type VideoOptions, type VideoStorage, activeThreadIds, aiBlock, blockDrag, blockDragPluginKey, blockId, blockIdPluginKey, bubbleToolbar, callout, canAppendChild, canOpenLinkEditor, collaboration, column, columns, comment, createAiSlashItems, createBlockKit, defaultAiSlashActions, defaultBubbleToolbarItems, defaultSlashItems, embed, file, filterBubbleToolbarItems, filterSlashItems, findNodeById, image, linkEditor, mention, mentionPluginKey, resolveDropTarget, retryUpload, runUpload, slashCommand, slashCommandPluginKey, table, video };
1258
+ export { type AiActionStatus, AiBlock, type AiBlockOptions, type AiBlockStorage, type AiKitOptions, type AiRequest, type AiSlashAction, BLOCK_ID_REMOTE_META, BlockDrag, type BlockDragOptions, type BlockDragState, type BlockDragStorage, BlockId, type BlockIdOptions, type BlockKitOptions, type BlockLocation, type BlockRect, type BlockTarget, BubbleToolbar, type BubbleToolbarItem, type BubbleToolbarOptions, type BubbleToolbarState, type BubbleToolbarStorage, Callout, type CalloutOptions, type CollaborationOptions, type CollaborationProvider, type CollaborationUser, Column, type ColumnOptions, Columns, type ColumnsOptions, Comment, type CommentMessage, type CommentOptions, type CommentState, type CommentStorage, type CommentThread, type CommentThreadStore, type DropMode, type DropTarget, Embed, type EmbedMode, type EmbedOptions, File$1 as File, type FileOptions, type FileStorage, type HeadingLevel, Image, type ImageOptions, type ImageStorage, LinkEditor, type LinkEditorOptions, type LinkEditorState, type LinkEditorStorage, Mention, type MentionItem, type MentionOptions, type MentionState, type MentionStorage, PendingAiRegistry, PendingUploadRegistry, Placeholder, type PlaceholderContext, type PlaceholderKey, type PlaceholderOptions, type RetryUploadOptions, type RunAiActionOptions, type RunUploadOptions, type SetEmbedOptions, type SetFileFromFile, type SetFileFromSrc, type SetFileOptions, type SetImageFromFile, type SetImageFromSrc, type SetImageOptions, type SetVideoFromFile, type SetVideoFromSrc, type SetVideoOptions, SlashCommand, type SlashCommandOptions, type SlashCommandStorage, type SlashContext, type SlashItem, type SlashMenuState, type StreamAdapter, type StreamContext, TableKit, type TableKitOptions, type UploadAdapter, type UploadContext, type UploadResult, type UploadStatus, Video, type VideoOptions, type VideoStorage, activeThreadIds, aiBlock, blockDrag, blockDragPluginKey, blockId, blockIdPluginKey, bubbleToolbar, callout, canAppendChild, canOpenLinkEditor, collaboration, column, columns, comment, createAiSlashItems, createBlockKit, defaultAiSlashActions, defaultBubbleToolbarItems, defaultPlaceholderText, defaultSlashItems, embed, file, filterBubbleToolbarItems, filterSlashItems, findNodeById, image, linkEditor, mention, mentionPluginKey, placeholder, placeholderKeyFor, placeholderPluginKey, resolveDropTarget, retryUpload, runUpload, slashCommand, slashCommandPluginKey, table, video };
package/dist/index.mjs CHANGED
@@ -293,28 +293,28 @@ const defaultAiSlashActions = [
293
293
  id: "continue-writing",
294
294
  title: "Continue writing",
295
295
  description: "AI extends the text above the cursor",
296
- icon: "sparkles",
296
+ icon: "pencil-sparkles",
297
297
  prompt: "Continue writing the document naturally, matching its tone and style. Write only the continuation, with no preamble."
298
298
  },
299
299
  {
300
300
  id: "summarize",
301
301
  title: "Summarize",
302
302
  description: "AI summarizes the text above the cursor",
303
- icon: "sparkles",
303
+ icon: "broom-sparkles",
304
304
  prompt: "Summarize the following text in a few concise sentences."
305
305
  },
306
306
  {
307
307
  id: "brainstorm-ideas",
308
308
  title: "Brainstorm ideas",
309
309
  description: "AI lists ideas related to the text above the cursor",
310
- icon: "sparkles",
310
+ icon: "brain",
311
311
  prompt: "Brainstorm a short bullet list of ideas related to the following text."
312
312
  },
313
313
  {
314
314
  id: "fix-spelling-grammar",
315
315
  title: "Fix spelling & grammar",
316
316
  description: "AI rewrites the text above the cursor, correcting mistakes",
317
- icon: "sparkles",
317
+ icon: "spell-check",
318
318
  prompt: "Rewrite the following text, correcting spelling and grammar mistakes only, and preserve its meaning and tone."
319
319
  }
320
320
  ];
@@ -1904,6 +1904,92 @@ function mention(options) {
1904
1904
  return Mention.configure(options);
1905
1905
  }
1906
1906
  //#endregion
1907
+ //#region src/placeholder.ts
1908
+ const defaultPlaceholderText = {
1909
+ paragraph: "Press '/' for commands…",
1910
+ heading1: "Heading 1",
1911
+ heading2: "Heading 2",
1912
+ heading3: "Heading 3",
1913
+ listItem: "List",
1914
+ taskItem: "To-do",
1915
+ blockquote: "Quote",
1916
+ callout: "Callout",
1917
+ details: "Toggle"
1918
+ };
1919
+ const PARENT_KEYS = {
1920
+ listItem: "listItem",
1921
+ taskItem: "taskItem",
1922
+ blockquote: "blockquote",
1923
+ callout: "callout",
1924
+ detailsContent: "details"
1925
+ };
1926
+ /**
1927
+ * Maps an empty block to its placeholder slot, or `null` when it should stay
1928
+ * blank. Code blocks are excluded: their content is literal, and a hint would
1929
+ * read as source.
1930
+ */
1931
+ function placeholderKeyFor(node, parent) {
1932
+ const name = node.type.name;
1933
+ if (name === "heading") {
1934
+ const { level } = node.attrs;
1935
+ return level === 1 || level === 2 || level === 3 ? `heading${level}` : null;
1936
+ }
1937
+ if (name === "detailsSummary") return "details";
1938
+ if (name !== "paragraph") return null;
1939
+ return parent ? PARENT_KEYS[parent.type.name] ?? "paragraph" : "paragraph";
1940
+ }
1941
+ const placeholderPluginKey = new PluginKey("placeholder");
1942
+ /**
1943
+ * Empty-block hints, Notion style: only the block holding the caret shows one.
1944
+ *
1945
+ * Renders `data-placeholder` and no class names — the UI layer styles
1946
+ * `[data-placeholder]::before`, matching how every other core node is styled.
1947
+ *
1948
+ * The decoration is derived from the state it is drawn against rather than
1949
+ * from `editor.state`: during a transaction those are different documents, and
1950
+ * resolving a parent in the stale one mislabels every nested block.
1951
+ */
1952
+ const Placeholder = Extension.create({
1953
+ name: "placeholder",
1954
+ addOptions() {
1955
+ return {};
1956
+ },
1957
+ addProseMirrorPlugins() {
1958
+ const { editor } = this;
1959
+ const text = {
1960
+ ...defaultPlaceholderText,
1961
+ ...this.options.text
1962
+ };
1963
+ const { resolve } = this.options;
1964
+ return [new Plugin({
1965
+ key: placeholderPluginKey,
1966
+ props: { decorations: (state) => {
1967
+ if (!editor.isEditable) return null;
1968
+ const { doc, selection } = state;
1969
+ const $anchor = doc.resolve(selection.anchor);
1970
+ const node = $anchor.parent;
1971
+ if (!node.type.isTextblock || node.content.size > 0) return null;
1972
+ const parent = $anchor.depth > 0 ? $anchor.node($anchor.depth - 1) : null;
1973
+ const pos = $anchor.before($anchor.depth);
1974
+ const context = {
1975
+ editor,
1976
+ node,
1977
+ parent,
1978
+ pos
1979
+ };
1980
+ const key = placeholderKeyFor(node, parent);
1981
+ const value = resolve ? resolve(context) : key && text[key];
1982
+ if (!value) return null;
1983
+ return DecorationSet.create(doc, [Decoration.node(pos, pos + node.nodeSize, { "data-placeholder": value })]);
1984
+ } }
1985
+ })];
1986
+ }
1987
+ });
1988
+ /** Configures empty-block placeholders. */
1989
+ function placeholder(options = {}) {
1990
+ return Placeholder.configure(options);
1991
+ }
1992
+ //#endregion
1907
1993
  //#region src/slash-items.ts
1908
1994
  const TITLE_PREFIX = 100;
1909
1995
  const TITLE_WORD_PREFIX = 80;
@@ -1959,6 +2045,7 @@ const defaultSlashItems = [
1959
2045
  description: "Large section title",
1960
2046
  aliases: ["h1", "title"],
1961
2047
  keywords: ["#"],
2048
+ shortcut: "#",
1962
2049
  icon: "heading-1",
1963
2050
  when: (editor) => hasNode(editor, "heading"),
1964
2051
  run: ({ editor, range }) => {
@@ -1972,6 +2059,7 @@ const defaultSlashItems = [
1972
2059
  description: "Medium section title",
1973
2060
  aliases: ["h2", "subtitle"],
1974
2061
  keywords: ["##"],
2062
+ shortcut: "##",
1975
2063
  icon: "heading-2",
1976
2064
  when: (editor) => hasNode(editor, "heading"),
1977
2065
  run: ({ editor, range }) => {
@@ -1985,6 +2073,7 @@ const defaultSlashItems = [
1985
2073
  description: "Small section title",
1986
2074
  aliases: ["h3"],
1987
2075
  keywords: ["###"],
2076
+ shortcut: "###",
1988
2077
  icon: "heading-3",
1989
2078
  when: (editor) => hasNode(editor, "heading"),
1990
2079
  run: ({ editor, range }) => {
@@ -1998,6 +2087,7 @@ const defaultSlashItems = [
1998
2087
  description: "Unordered list",
1999
2088
  aliases: ["ul", "bullet"],
2000
2089
  keywords: ["-", "*"],
2090
+ shortcut: "-",
2001
2091
  icon: "list",
2002
2092
  when: (editor) => hasNode(editor, "bulletList"),
2003
2093
  run: ({ editor, range }) => {
@@ -2011,6 +2101,7 @@ const defaultSlashItems = [
2011
2101
  description: "Ordered list",
2012
2102
  aliases: ["ol", "numbered"],
2013
2103
  keywords: ["1."],
2104
+ shortcut: "1.",
2014
2105
  icon: "list-ordered",
2015
2106
  when: (editor) => hasNode(editor, "orderedList"),
2016
2107
  run: ({ editor, range }) => {
@@ -2028,6 +2119,7 @@ const defaultSlashItems = [
2028
2119
  "checkbox"
2029
2120
  ],
2030
2121
  keywords: ["[]", "[ ]"],
2122
+ shortcut: "[]",
2031
2123
  icon: "list-checks",
2032
2124
  when: (editor) => hasNode(editor, "taskList"),
2033
2125
  run: ({ editor, range }) => {
@@ -2041,6 +2133,7 @@ const defaultSlashItems = [
2041
2133
  description: "Capture a quotation",
2042
2134
  aliases: ["quote", "citation"],
2043
2135
  keywords: [">"],
2136
+ shortcut: ">",
2044
2137
  icon: "quote",
2045
2138
  when: (editor) => hasNode(editor, "blockquote"),
2046
2139
  run: ({ editor, range }) => {
@@ -2088,6 +2181,7 @@ const defaultSlashItems = [
2088
2181
  description: "Monospaced code",
2089
2182
  aliases: ["code", "snippet"],
2090
2183
  keywords: ["```"],
2184
+ shortcut: "```",
2091
2185
  icon: "code",
2092
2186
  when: (editor) => hasNode(editor, "codeBlock"),
2093
2187
  run: ({ editor, range }) => {
@@ -2105,6 +2199,7 @@ const defaultSlashItems = [
2105
2199
  "rule"
2106
2200
  ],
2107
2201
  keywords: ["---"],
2202
+ shortcut: "---",
2108
2203
  icon: "minus",
2109
2204
  when: (editor) => hasNode(editor, "horizontalRule"),
2110
2205
  run: ({ editor, range }) => {
@@ -2222,6 +2317,7 @@ const SlashCommand = Extension.create({
2222
2317
  addOptions() {
2223
2318
  return {
2224
2319
  char: "/",
2320
+ hint: "Type to search",
2225
2321
  items: defaultSlashItems
2226
2322
  };
2227
2323
  },
@@ -2283,6 +2379,7 @@ const SlashCommand = Extension.create({
2283
2379
  return [Suggestion({
2284
2380
  editor,
2285
2381
  char: this.options.char,
2382
+ decorationContent: this.options.hint,
2286
2383
  pluginKey: slashCommandPluginKey,
2287
2384
  allowSpaces: false,
2288
2385
  allow: ({ state, range }) => !state.doc.resolve(range.from).parent.type.spec.code,
@@ -2467,7 +2564,7 @@ const DEFAULT_HEADING_LEVELS = [
2467
2564
  * the `data-*` attributes rendered by slash-editor nodes.
2468
2565
  */
2469
2566
  function createBlockKit(options = {}) {
2470
- const { headingLevels = DEFAULT_HEADING_LEVELS, history = true, slash, blockId: blockIdOptions, drag, bubbleToolbar: bubbleToolbarOptions, image: imageOptions, file: fileOptions, video: videoOptions, embed: embedOptions, table: tableOptions, columns: columnsOptions, linkEditor: linkEditorOptions, mention: mentionOptions, ai: aiOptions, collaboration: collaborationOptions, comment: commentOptions, extend = [] } = options;
2567
+ const { headingLevels = DEFAULT_HEADING_LEVELS, history = true, slash, placeholder: placeholderOptions, blockId: blockIdOptions, drag, bubbleToolbar: bubbleToolbarOptions, image: imageOptions, file: fileOptions, video: videoOptions, embed: embedOptions, table: tableOptions, columns: columnsOptions, linkEditor: linkEditorOptions, mention: mentionOptions, ai: aiOptions, collaboration: collaborationOptions, comment: commentOptions, extend = [] } = options;
2471
2568
  const resolvedAi = aiOptions ? {
2472
2569
  actions: defaultAiSlashActions,
2473
2570
  node: true,
@@ -2501,6 +2598,7 @@ function createBlockKit(options = {}) {
2501
2598
  ...slash,
2502
2599
  items: slash?.items ?? (() => [...defaultSlashItems, ...resolvedAi ? createAiSlashItems(resolvedAi) : []])()
2503
2600
  })],
2601
+ ...placeholderOptions === false ? [] : [placeholder(placeholderOptions)],
2504
2602
  ...blockIdOptions === false ? [] : [blockId(blockIdOptions)],
2505
2603
  ...drag === false ? [] : [blockDrag(drag)],
2506
2604
  ...bubbleToolbarOptions === false ? [] : [bubbleToolbar(bubbleToolbarOptions)],
@@ -2511,4 +2609,4 @@ function createBlockKit(options = {}) {
2511
2609
  ];
2512
2610
  }
2513
2611
  //#endregion
2514
- export { AiBlock, BLOCK_ID_REMOTE_META, BlockDrag, BlockId, BubbleToolbar, Callout, Column, Columns, Comment, Embed, File, Image, LinkEditor, Mention, PendingAiRegistry, PendingUploadRegistry, SlashCommand, TableKit, Video, activeThreadIds, aiBlock, blockDrag, blockDragPluginKey, blockId, blockIdPluginKey, bubbleToolbar, callout, canAppendChild, canOpenLinkEditor, collaboration, column, columns, comment, createAiSlashItems, createBlockKit, defaultAiSlashActions, defaultBubbleToolbarItems, defaultSlashItems, embed, file, filterBubbleToolbarItems, filterSlashItems, findNodeById, image, linkEditor, mention, mentionPluginKey, resolveDropTarget, retryUpload, runUpload, slashCommand, slashCommandPluginKey, table, video };
2612
+ export { AiBlock, BLOCK_ID_REMOTE_META, BlockDrag, BlockId, BubbleToolbar, Callout, Column, Columns, Comment, Embed, File, Image, LinkEditor, Mention, PendingAiRegistry, PendingUploadRegistry, Placeholder, SlashCommand, TableKit, Video, activeThreadIds, aiBlock, blockDrag, blockDragPluginKey, blockId, blockIdPluginKey, bubbleToolbar, callout, canAppendChild, canOpenLinkEditor, collaboration, column, columns, comment, createAiSlashItems, createBlockKit, defaultAiSlashActions, defaultBubbleToolbarItems, defaultPlaceholderText, defaultSlashItems, embed, file, filterBubbleToolbarItems, filterSlashItems, findNodeById, image, linkEditor, mention, mentionPluginKey, placeholder, placeholderKeyFor, placeholderPluginKey, resolveDropTarget, retryUpload, runUpload, slashCommand, slashCommandPluginKey, table, video };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@slash-editor/core",
3
- "version": "0.0.5",
3
+ "version": "0.0.7",
4
4
  "description": "Headless block-editor core: ProseMirror/Tiptap extensions, schema, commands.",
5
5
  "license": "MIT",
6
6
  "repository": {