kopia lustrzana https://github.com/elk-zone/elk
				
				
				
			feat: basic parsing of post content
							rodzic
							
								
									38f5d7155f
								
							
						
					
					
						commit
						bc3197ba22
					
				|  | @ -0,0 +1,11 @@ | |||
| export default defineComponent({ | ||||
|   props: { | ||||
|     content: { | ||||
|       type: String, | ||||
|       required: true, | ||||
|     }, | ||||
|   }, | ||||
|   setup(props) { | ||||
|     return () => contentToVNode(props.content) | ||||
|   }, | ||||
| }) | ||||
|  | @ -18,7 +18,12 @@ async function publish() { | |||
| 
 | ||||
| <template> | ||||
|   <div flex flex-col gap-4 :class="isSending ? ' pointer-events-none' : ''"> | ||||
|     <textarea v-model="draftPost" p2 border-rounded w-full h-40 color-black placeholder="What's on your mind?" /> | ||||
|     <textarea | ||||
|       v-model="draftPost" | ||||
|       placeholder="What's on your mind?" | ||||
|       p2 border-rounded w-full h-40 | ||||
|       bg-gray:10 outline-none border="~ border" | ||||
|     /> | ||||
|     <div flex justify-end> | ||||
|       <button h-9 w-22 bg-primary border-rounded :disabled="draftPost === ''" @click="publish"> | ||||
|         Publish! | ||||
|  |  | |||
|  | @ -1,15 +1,15 @@ | |||
| <script setup lang="ts"> | ||||
| import type { Status } from 'masto' | ||||
| 
 | ||||
| defineProps<{ | ||||
| const { status } = defineProps<{ | ||||
|   status: Status | ||||
| }>() | ||||
| 
 | ||||
| // TODO: parse and interop content (link, emojis) | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|   <div class="status-body" v-html="sanitize(status.content)" /> | ||||
|   <div class="status-body"> | ||||
|     <CommonRichContent :content="status.content" /> | ||||
|   </div> | ||||
| </template> | ||||
| 
 | ||||
| <style lang="postcss"> | ||||
|  |  | |||
|  | @ -0,0 +1,72 @@ | |||
| import type { DefaultTreeAdapterMap } from 'parse5' | ||||
| import { parseFragment, serialize } from 'parse5' | ||||
| import type { VNode } from 'vue' | ||||
| import { Fragment, h } from 'vue' | ||||
| import { RouterLink } from 'vue-router' | ||||
| 
 | ||||
| type Node = DefaultTreeAdapterMap['childNode'] | ||||
| type Element = DefaultTreeAdapterMap['element'] | ||||
| 
 | ||||
| const UserLinkRE = /^https?:\/\/([^/]+)\/@([^/]+)$/ | ||||
| const TagLinkRE = /^https?:\/\/([^/]+)\/tags\/([^/]+)$/ | ||||
| 
 | ||||
| export function defaultHandle(el: Element) { | ||||
|   // Redirect mentions to the user page
 | ||||
|   if (el.tagName === 'a' && el.attrs.find(i => i.name === 'class' && i.value.includes('mention'))) { | ||||
|     const href = el.attrs.find(i => i.name === 'href') | ||||
|     if (href) { | ||||
|       const matchUser = href.value.match(UserLinkRE) | ||||
|       if (matchUser) { | ||||
|         const [, server, username] = matchUser | ||||
|         href.value = `/@${username}@${server}` | ||||
|       } | ||||
|       const matchTag = href.value.match(TagLinkRE) | ||||
|       if (matchTag) { | ||||
|         const [, , name] = matchTag | ||||
|         href.value = `/tags/${name}` | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   return el | ||||
| } | ||||
| 
 | ||||
| export function contentToVNode( | ||||
|   content: string, | ||||
|   handle: (node: Element) => Element | undefined | null | void = defaultHandle, | ||||
| ): VNode { | ||||
|   const tree = parseFragment(content) | ||||
|   return h(Fragment, tree.childNodes.map(n => treeToVNode(n, handle))) | ||||
| } | ||||
| 
 | ||||
| export function treeToVNode( | ||||
|   input: Node, | ||||
|   handle: (node: Element) => Element | undefined | null | void = defaultHandle, | ||||
| ): VNode | string | null { | ||||
|   if (input.nodeName === '#text') | ||||
|     // @ts-expect-error casing
 | ||||
|     return input.value | ||||
| 
 | ||||
|   if ('childNodes' in input) { | ||||
|     const node = handle(input) | ||||
|     if (node == null) | ||||
|       return null | ||||
| 
 | ||||
|     const attrs = Object.fromEntries(node.attrs.map(i => [i.name, i.value])) | ||||
|     if (node.nodeName === 'a' && (attrs.href?.startsWith('/') || attrs.href?.startsWith('.'))) { | ||||
|       attrs.to = attrs.href | ||||
|       delete attrs.href | ||||
|       delete attrs.target | ||||
|       return h( | ||||
|         RouterLink as any, | ||||
|         attrs, | ||||
|         node.childNodes.map(n => treeToVNode(n, handle)), | ||||
|       ) | ||||
|     } | ||||
|     return h( | ||||
|       node.nodeName, | ||||
|       attrs, | ||||
|       node.childNodes.map(n => treeToVNode(n, handle)), | ||||
|     ) | ||||
|   } | ||||
|   return null | ||||
| } | ||||
|  | @ -28,6 +28,7 @@ | |||
|     "fs-extra": "^10.1.0", | ||||
|     "masto": "^4.6.5", | ||||
|     "nuxt": "^3.0.0", | ||||
|     "parse5": "^7.1.1", | ||||
|     "pinia": "^2.0.23", | ||||
|     "postcss-nested": "^6.0.0", | ||||
|     "rollup-plugin-node-polyfills": "^0.2.1", | ||||
|  |  | |||
|  | @ -17,6 +17,7 @@ specifiers: | |||
|   fs-extra: ^10.1.0 | ||||
|   masto: ^4.6.5 | ||||
|   nuxt: ^3.0.0 | ||||
|   parse5: ^7.1.1 | ||||
|   pinia: ^2.0.23 | ||||
|   postcss-nested: ^6.0.0 | ||||
|   rollup-plugin-node-polyfills: ^0.2.1 | ||||
|  | @ -41,6 +42,7 @@ devDependencies: | |||
|   fs-extra: 10.1.0 | ||||
|   masto: 4.6.5 | ||||
|   nuxt: 3.0.0_e3uo4sehh4zr4i6m57mkkxxv7y | ||||
|   parse5: 7.1.1 | ||||
|   pinia: 2.0.23_typescript@4.9.3 | ||||
|   postcss-nested: 6.0.0 | ||||
|   rollup-plugin-node-polyfills: 0.2.1 | ||||
|  | @ -5396,6 +5398,12 @@ packages: | |||
|       parse-path: 7.0.0 | ||||
|     dev: true | ||||
| 
 | ||||
|   /parse5/7.1.1: | ||||
|     resolution: {integrity: sha512-kwpuwzB+px5WUg9pyK0IcK/shltJN5/OVhQagxhCQNtT9Y9QRZqNY2e1cmbu/paRh5LMnz/oVTVLBpjFmMZhSg==} | ||||
|     dependencies: | ||||
|       entities: 4.4.0 | ||||
|     dev: true | ||||
| 
 | ||||
|   /parseurl/1.3.3: | ||||
|     resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} | ||||
|     engines: {node: '>= 0.8'} | ||||
|  |  | |||
		Ładowanie…
	
		Reference in New Issue
	
	 Anthony Fu
						Anthony Fu