Bluesky app fork with some witchin' additions 💫

markup highlighting in editor!

authored by

12Me21 and committed by tangled.org d172ad98 ebec4a9a

+75 -5
+1 -1
src/view/com/composer/text-input/TextInput.web.tsx
··· 63 63 const extensions = useMemo( 64 64 () => [ 65 65 Document, 66 - Decorator, 67 66 Mention.configure({ 68 67 HTMLAttributes: { 69 68 class: 'mention', ··· 77 76 TiptapText, 78 77 History, 79 78 Hardbreak, 79 + Decorator, 80 80 ], 81 81 [autocomplete, placeholder], 82 82 )
+74 -4
src/view/com/composer/text-input/web/Decorator.ts
··· 20 20 import {Decoration, DecorationSet} from '@tiptap/pm/view' 21 21 import {markup_main} from '#/lib/twelve/markup.js' 22 22 23 + let preview_styles = { 24 + "app.bsky.richtext.facet#link": "color: var(--mention-color, #ed5345)", 25 + "app.bsky.richtext.facet#mention": "color: var(--mention-color, #ed5345)", 26 + "app.bsky.richtext.facet#tag": "color: var(--mention-color, #ed5345)", 27 + "com.example.richtext.facet#markup": { 28 + italic: "font-style: italic", 29 + bold: "font-weight: bold", 30 + strikethrough: "text-decoration: line-through", 31 + underline: "text-decoration: underline", 32 + } 33 + } 34 + 35 + // ok so. we need plain text to parse markup from, but 36 + // this is a "rich text" editor with paragraphs and other unimaginable things 37 + // so, we have to convert the contents to plain text, but 38 + // also need to convert indexes into the plaintext string, BACK 39 + // into indexes into the rich text, for applying the decorations 40 + function get_editor_text(node) { 41 + let text = "" 42 + let decor_index = 0 43 + // indexes converts an index in our raw string -> index for inline decor 44 + let indexes = [[0,0]] 45 + function recurse(node, islast=false) { 46 + if (node.type.name=='doc') { 47 + let content = node.content.content 48 + for (let i=0; i<content.length; i++) 49 + recurse(content[i], i==content.length-1) 50 + } else if (node.type.name=='paragraph') { 51 + let content = node.content.content 52 + for (let i=0; i<content.length; i++) 53 + recurse(content[i]) 54 + if (!islast) { 55 + text += '\n' 56 + decor_index += 2 // why 2? no idea. 57 + indexes.push([text.length,decor_index]) 58 + } 59 + } else if (node.type.name=='hardBreak') { 60 + text += "\n" 61 + decor_index += 1 // i assume ? 62 + indexes.push([text.length,decor_index]) 63 + } else if (node.type.name=='text') { 64 + text += node.text 65 + decor_index += node.text.length 66 + } else if (node.type.name=='mention') { 67 + text += `@${node.attrs?.id || ''}` 68 + decor_index += 1 // ? 69 + indexes.push([text.length,decor_index]) 70 + } 71 + } 72 + recurse(node) 73 + return {text, indexes} 74 + } 75 + 76 + function convert_index(indexes, index) { 77 + let i 78 + for (i=0; i<indexes.length; i++) { 79 + if (indexes[i][0]>index) 80 + break 81 + } 82 + let [real_index, decor_index] = indexes[i-1] 83 + let offset = index - real_index 84 + return decor_index + offset 85 + } 86 + 23 87 function getDecorations(doc: ProsemirrorNode) { 88 + console.log(doc) 89 + 24 90 const decorations: Decoration[] = [] 25 91 26 - let text = doc.textContent 27 - for (let span of markup_main(text)) { 92 + let content = get_editor_text(doc) 93 + console.log(content) 94 + for (let span of markup_main(content.text)) { 95 + let style = preview_styles[span.feature.$type] 96 + if (span.feature.$type=="com.example.richtext.facet#markup") 97 + style = style[span.feature.style] 28 98 decorations.push( 29 - Decoration.inline(span.start, span.end+1, { 30 - class: 'autolink', 99 + Decoration.inline(convert_index(content.indexes, span.start)+1, convert_index(content.indexes, span.end)+1, { 100 + style: style || "color: red", 31 101 }), 32 102 ) 33 103 }