snapshot of https://github.com/ueberdosis/tiptap.git (main, 2026-09-15; blobs > 131072B omitted)
Some checks failed
Verify / Install node dependencies (push) Failing after 2s
Verify / Build packages (push) Has been skipped
Verify / Check linting & formatting (push) Has been skipped
Verify / Run unit tests (push) Has been skipped
Verify / Run e2e tests (shard 1/4) (push) Has been skipped
Verify / Run e2e tests (shard 2/4) (push) Has been skipped
Verify / Run e2e tests (shard 3/4) (push) Has been skipped
Verify / Run e2e tests (shard 4/4) (push) Has been skipped
Publish / Resolve publish config (push) Failing after 2s
Publish / Send notification to Slack (push) Has been skipped
Publish / Send failure notification to Slack (push) Failing after 8s
Verify / Merge e2e reports (push) Has been skipped
Publish / Build (push) Has been skipped
Publish / Bump Versions OR Release to Registry (push) Has been skipped
Some checks failed
Verify / Install node dependencies (push) Failing after 2s
Verify / Build packages (push) Has been skipped
Verify / Check linting & formatting (push) Has been skipped
Verify / Run unit tests (push) Has been skipped
Verify / Run e2e tests (shard 1/4) (push) Has been skipped
Verify / Run e2e tests (shard 2/4) (push) Has been skipped
Verify / Run e2e tests (shard 3/4) (push) Has been skipped
Verify / Run e2e tests (shard 4/4) (push) Has been skipped
Publish / Resolve publish config (push) Failing after 2s
Publish / Send notification to Slack (push) Has been skipped
Publish / Send failure notification to Slack (push) Failing after 8s
Verify / Merge e2e reports (push) Has been skipped
Publish / Build (push) Has been skipped
Publish / Bump Versions OR Release to Registry (push) Has been skipped
This commit is contained in:
1670
packages/extension-code/CHANGELOG.md
Normal file
1670
packages/extension-code/CHANGELOG.md
Normal file
File diff suppressed because it is too large
Load Diff
18
packages/extension-code/README.md
Normal file
18
packages/extension-code/README.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# @tiptap/extension-code
|
||||
|
||||
[](https://www.npmjs.com/package/@tiptap/extension-code)
|
||||
[](https://npmcharts.com/compare/tiptap?minimal=true)
|
||||
[](https://www.npmjs.com/package/@tiptap/extension-code)
|
||||
[](https://github.com/sponsors/ueberdosis)
|
||||
|
||||
## Introduction
|
||||
|
||||
Tiptap is a headless wrapper around [ProseMirror](https://ProseMirror.net) – a toolkit for building rich text WYSIWYG editors, which is already in use at many well-known companies such as _New York Times_, _The Guardian_ or _Atlassian_.
|
||||
|
||||
## Official Documentation
|
||||
|
||||
Documentation can be found on the [Tiptap website](https://tiptap.dev).
|
||||
|
||||
## License
|
||||
|
||||
Tiptap is open sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap/blob/main/LICENSE.md).
|
||||
50
packages/extension-code/package.json
Normal file
50
packages/extension-code/package.json
Normal file
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"name": "@tiptap/extension-code",
|
||||
"version": "3.30.3",
|
||||
"description": "code extension for tiptap",
|
||||
"keywords": [
|
||||
"tiptap",
|
||||
"tiptap extension"
|
||||
],
|
||||
"homepage": "https://tiptap.dev",
|
||||
"bugs": {
|
||||
"url": "https://github.com/ueberdosis/tiptap/issues"
|
||||
},
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ueberdosis/tiptap",
|
||||
"directory": "packages/extension-code"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"files": [
|
||||
"src",
|
||||
"dist"
|
||||
],
|
||||
"type": "module",
|
||||
"main": "dist/index.cjs",
|
||||
"module": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": {
|
||||
"import": "./dist/index.d.ts",
|
||||
"require": "./dist/index.d.cts"
|
||||
},
|
||||
"import": "./dist/index.js",
|
||||
"require": "./dist/index.cjs"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"build": "vp pack"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tiptap/core": "workspace:^"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "workspace:*"
|
||||
}
|
||||
}
|
||||
122
packages/extension-code/src/code.test.ts
Normal file
122
packages/extension-code/src/code.test.ts
Normal file
@@ -0,0 +1,122 @@
|
||||
import { inputRegex, inputRegexMatch, pasteRegex, pasteRegexMatch } from './code.js'
|
||||
import { describe, expect, it } from 'vite-plus/test'
|
||||
|
||||
describe('code inputRegexMatch', () => {
|
||||
it('matches basic backtick code', () => {
|
||||
const result = inputRegexMatch('`code`')
|
||||
expect(result).not.toBeNull()
|
||||
expect(result!.replaceWith).toBe('code')
|
||||
expect(result!.text).toBe('`code`')
|
||||
expect(result!.index).toBe(0)
|
||||
})
|
||||
|
||||
it('matches backtick code preceded by a non-backtick character', () => {
|
||||
const result = inputRegexMatch('a`code`')
|
||||
expect(result).not.toBeNull()
|
||||
expect(result!.replaceWith).toBe('code')
|
||||
expect(result!.text).toBe('`code`')
|
||||
// match starts at the opening backtick, not at 'a'
|
||||
expect(result!.index).toBe(1)
|
||||
})
|
||||
|
||||
it('matches backtick code preceded by whitespace', () => {
|
||||
const result = inputRegexMatch(' `code`')
|
||||
expect(result).not.toBeNull()
|
||||
expect(result!.replaceWith).toBe('code')
|
||||
expect(result!.text).toBe('`code`')
|
||||
expect(result!.index).toBe(1)
|
||||
})
|
||||
|
||||
it('matches backtick code preceded by multiple characters', () => {
|
||||
const result = inputRegexMatch('hello`code`')
|
||||
expect(result).not.toBeNull()
|
||||
expect(result!.replaceWith).toBe('code')
|
||||
expect(result!.index).toBe(5)
|
||||
})
|
||||
|
||||
it('does not match when preceded by a backtick', () => {
|
||||
expect(inputRegexMatch('``code`')).toBeNull()
|
||||
})
|
||||
|
||||
it('does not match with empty content', () => {
|
||||
expect(inputRegexMatch('``')).toBeNull()
|
||||
})
|
||||
|
||||
it('does not match with no closing backtick', () => {
|
||||
expect(inputRegexMatch('`code')).toBeNull()
|
||||
})
|
||||
|
||||
it('does not match plain text', () => {
|
||||
expect(inputRegexMatch('code')).toBeNull()
|
||||
})
|
||||
|
||||
it('matches only the last pair when there are multiple pairs', () => {
|
||||
const result = inputRegexMatch('`first` middle `second`')
|
||||
expect(result).not.toBeNull()
|
||||
expect(result!.replaceWith).toBe('second')
|
||||
expect(result!.text).toBe('`second`')
|
||||
})
|
||||
})
|
||||
|
||||
describe('code pasteRegexMatch', () => {
|
||||
it('matches basic backtick code', () => {
|
||||
const results = pasteRegexMatch('`code`')
|
||||
expect(results).toHaveLength(1)
|
||||
expect(results[0].replaceWith).toBe('code')
|
||||
expect(results[0].text).toBe('`code`')
|
||||
})
|
||||
|
||||
it('matches backtick code preceded by non-backtick character', () => {
|
||||
const results = pasteRegexMatch('a`code`')
|
||||
expect(results).toHaveLength(1)
|
||||
expect(results[0].replaceWith).toBe('code')
|
||||
expect(results[0].text).toBe('`code`')
|
||||
expect(results[0].index).toBe(1)
|
||||
})
|
||||
|
||||
it('matches backtick code preceded by whitespace', () => {
|
||||
const results = pasteRegexMatch(' `code`')
|
||||
expect(results).toHaveLength(1)
|
||||
expect(results[0].replaceWith).toBe('code')
|
||||
})
|
||||
|
||||
it('does not match when preceded by a backtick', () => {
|
||||
const results = pasteRegexMatch('``code`')
|
||||
expect(results).toHaveLength(0)
|
||||
})
|
||||
|
||||
it('does not match empty content', () => {
|
||||
expect(pasteRegexMatch('``')).toHaveLength(0)
|
||||
})
|
||||
|
||||
it('does not match plain text', () => {
|
||||
expect(pasteRegexMatch('code')).toHaveLength(0)
|
||||
})
|
||||
|
||||
it('matches multiple code spans in pasted text', () => {
|
||||
const results = pasteRegexMatch('`a` and `b`')
|
||||
expect(results).toHaveLength(2)
|
||||
expect(results[0].replaceWith).toBe('a')
|
||||
expect(results[1].replaceWith).toBe('b')
|
||||
})
|
||||
|
||||
it('does not match code spans preceded by backtick within pasted text', () => {
|
||||
// ``a` should NOT match because it's preceded by a backtick
|
||||
// but `b` should match
|
||||
const results = pasteRegexMatch('``a` and `b`')
|
||||
expect(results).toHaveLength(1)
|
||||
expect(results[0].replaceWith).toBe('b')
|
||||
})
|
||||
})
|
||||
|
||||
describe('code inputRegex (deprecated regex export)', () => {
|
||||
it('matches basic backtick code (backward compat)', () => {
|
||||
expect('`code`').toMatch(inputRegex)
|
||||
})
|
||||
})
|
||||
|
||||
describe('code pasteRegex (deprecated regex export)', () => {
|
||||
it('matches basic backtick code (backward compat)', () => {
|
||||
expect('`code`').toMatch(pasteRegex)
|
||||
})
|
||||
})
|
||||
182
packages/extension-code/src/code.ts
Normal file
182
packages/extension-code/src/code.ts
Normal file
@@ -0,0 +1,182 @@
|
||||
import type { InputRuleMatch, PasteRuleMatch } from '@tiptap/core'
|
||||
import { Mark, markInputRule, markPasteRule, mergeAttributes } from '@tiptap/core'
|
||||
|
||||
export interface CodeOptions {
|
||||
/**
|
||||
* The HTML attributes applied to the code element.
|
||||
* @default {}
|
||||
* @example { class: 'foo' }
|
||||
*/
|
||||
HTMLAttributes: Record<string, any>
|
||||
}
|
||||
|
||||
declare module '@tiptap/core' {
|
||||
interface Commands<ReturnType> {
|
||||
code: {
|
||||
/**
|
||||
* Set a code mark
|
||||
*/
|
||||
setCode: () => ReturnType
|
||||
/**
|
||||
* Toggle inline code
|
||||
*/
|
||||
toggleCode: () => ReturnType
|
||||
/**
|
||||
* Unset a code mark
|
||||
*/
|
||||
unsetCode: () => ReturnType
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The regular expression used for the inline code input rule.
|
||||
* @deprecated The extension now uses a function-based finder internally.
|
||||
* This regex is kept for backward compatibility.
|
||||
*/
|
||||
export const inputRegex = /(^|[^`])`([^`]+)`(?!`)$/
|
||||
|
||||
/**
|
||||
* The regular expression used for the inline code paste rule.
|
||||
* @deprecated The extension now uses a function-based finder internally.
|
||||
* This regex is kept for backward compatibility.
|
||||
*/
|
||||
export const pasteRegex = /(^|[^`])`([^`]+)`(?!`)/g
|
||||
|
||||
/**
|
||||
* A function-based finder for the inline code input rule.
|
||||
* Used internally by the extension to ensure the preceding character
|
||||
* is not consumed as part of the match, preventing it from being deleted.
|
||||
*/
|
||||
export const inputRegexMatch = (text: string): InputRuleMatch | null => {
|
||||
const match = /`([^`]+)`(?!`)$/.exec(text)
|
||||
|
||||
if (!match) {
|
||||
return null
|
||||
}
|
||||
|
||||
// Ensure the opening backtick isn't preceded by another backtick
|
||||
if (match.index > 0 && text[match.index - 1] === '`') {
|
||||
return null
|
||||
}
|
||||
|
||||
return {
|
||||
index: match.index,
|
||||
text: match[0],
|
||||
replaceWith: match[1],
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A function-based finder for the inline code paste rule.
|
||||
* Used internally by the extension to avoid consuming the preceding
|
||||
* character as part of the match.
|
||||
*/
|
||||
export const pasteRegexMatch = (text: string): PasteRuleMatch[] => {
|
||||
const regex = /`([^`]+)`(?!`)/g
|
||||
const matches: PasteRuleMatch[] = []
|
||||
let match: RegExpExecArray | null
|
||||
|
||||
while ((match = regex.exec(text)) !== null) {
|
||||
// Ensure the opening backtick isn't preceded by another backtick
|
||||
if (match.index > 0 && text[match.index - 1] === '`') {
|
||||
continue
|
||||
}
|
||||
|
||||
matches.push({
|
||||
index: match.index,
|
||||
text: match[0],
|
||||
replaceWith: match[1],
|
||||
})
|
||||
}
|
||||
|
||||
return matches
|
||||
}
|
||||
|
||||
/**
|
||||
* This extension allows you to mark text as inline code.
|
||||
* @see https://tiptap.dev/api/marks/code
|
||||
*/
|
||||
export const Code = Mark.create<CodeOptions>({
|
||||
name: 'code',
|
||||
|
||||
addOptions() {
|
||||
return {
|
||||
HTMLAttributes: {},
|
||||
}
|
||||
},
|
||||
|
||||
excludes: '_',
|
||||
|
||||
code: true,
|
||||
|
||||
exitable: true,
|
||||
|
||||
parseHTML() {
|
||||
return [{ tag: 'code' }]
|
||||
},
|
||||
|
||||
renderHTML({ HTMLAttributes }) {
|
||||
return ['code', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0]
|
||||
},
|
||||
|
||||
markdownTokenName: 'codespan',
|
||||
|
||||
parseMarkdown: (token, helpers) => {
|
||||
// Convert 'codespan' token to code mark
|
||||
// For codespan tokens, we use the raw text content, not tokens
|
||||
return helpers.applyMark('code', [{ type: 'text', text: token.text || '' }])
|
||||
},
|
||||
|
||||
renderMarkdown: (node, h) => {
|
||||
if (!node.content) {
|
||||
return ''
|
||||
}
|
||||
|
||||
return `\`${h.renderChildren(node.content)}\``
|
||||
},
|
||||
|
||||
addCommands() {
|
||||
return {
|
||||
setCode:
|
||||
() =>
|
||||
({ commands }) => {
|
||||
return commands.setMark(this.name)
|
||||
},
|
||||
toggleCode:
|
||||
() =>
|
||||
({ commands }) => {
|
||||
return commands.toggleMark(this.name)
|
||||
},
|
||||
unsetCode:
|
||||
() =>
|
||||
({ commands }) => {
|
||||
return commands.unsetMark(this.name)
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
addKeyboardShortcuts() {
|
||||
return {
|
||||
'Mod-e': () => this.editor.commands.toggleCode(),
|
||||
}
|
||||
},
|
||||
|
||||
addInputRules() {
|
||||
return [
|
||||
markInputRule({
|
||||
find: inputRegexMatch,
|
||||
type: this.type,
|
||||
}),
|
||||
]
|
||||
},
|
||||
|
||||
addPasteRules() {
|
||||
return [
|
||||
markPasteRule({
|
||||
find: pasteRegexMatch,
|
||||
type: this.type,
|
||||
}),
|
||||
]
|
||||
},
|
||||
})
|
||||
5
packages/extension-code/src/index.ts
Normal file
5
packages/extension-code/src/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { Code } from './code.js'
|
||||
|
||||
export * from './code.js'
|
||||
|
||||
export default Code
|
||||
9
packages/extension-code/vite.config.ts
Normal file
9
packages/extension-code/vite.config.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { defineConfig } from 'vite-plus'
|
||||
import { basePackConfig } from '../../pack.config.mjs'
|
||||
|
||||
export default defineConfig({
|
||||
pack: {
|
||||
entry: ['src/index.ts'],
|
||||
...basePackConfig(),
|
||||
},
|
||||
})
|
||||
Reference in New Issue
Block a user