Skip to content

Commit 5462608

Browse files
committed
Merge branch 'feat/html-to-md-converter' into chore/all-my-stuffs
# Conflicts: # package.json # pnpm-lock.yaml # src/tools/index.ts
2 parents bcfe44e + befc1a1 commit 5462608

File tree

7 files changed

+116
-4
lines changed

7 files changed

+116
-4
lines changed

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
"@github/paste-markdown": "^1.5.3",
4040
"@chenfengyuan/vue-barcode": "^2.0.2",
4141
"@guolao/vue-monaco-editor": "^1.4.1",
42+
"@guyplusplus/turndown-plugin-gfm": "^1.0.7",
4243
"@it-tools/bip39": "^0.0.4",
4344
"@it-tools/oggen": "^1.3.0",
4445
"@regexper/render": "^1.0.0",
@@ -55,6 +56,7 @@
5556
"@types/lodash.defaultsdeep": "^4.6.9",
5657
"@types/lodash.flattendeep": "^4.4.9",
5758
"@types/lodash.last": "^3.0.9",
59+
"@types/turndown": "^5.0.4",
5860
"@vicons/material": "^0.12.0",
5961
"@vicons/tabler": "^0.12.0",
6062
"@vueuse/core": "^10.11.1",
@@ -136,6 +138,7 @@
136138
"rtf-stream-parser": "^3.8.0",
137139
"sql-formatter": "^13.0.0",
138140
"sshpk": "^1.18.0",
141+
"turndown": "^7.1.2",
139142
"ua-parser-js": "^1.0.35",
140143
"ulid": "^2.3.0",
141144
"unicode-emoji-json": "^0.4.0",

pnpm-lock.yaml

Lines changed: 31 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<script setup lang="ts">
2+
import TurndownService from 'turndown';
3+
import { gfm as addGFM } from '@guyplusplus/turndown-plugin-gfm';
4+
import TextareaCopyable from '@/components/TextareaCopyable.vue';
5+
6+
const turndownService = new TurndownService();
7+
addGFM(turndownService);
8+
9+
const inputHtml = ref('');
10+
const outputMarkdown = computed(() => {
11+
try {
12+
return turndownService.turndown(inputHtml.value ?? '');
13+
}
14+
catch (e: any) {
15+
return e.toString();
16+
}
17+
});
18+
</script>
19+
20+
<template>
21+
<div>
22+
<c-input-text
23+
v-model:value="inputHtml"
24+
multiline raw-text
25+
placeholder="Your Html content..."
26+
rows="8"
27+
autofocus
28+
label="Your Html to convert (can paste from clipboard):"
29+
paste-html
30+
/>
31+
32+
<n-divider />
33+
34+
<n-form-item label="Output markdown:">
35+
<TextareaCopyable :value="outputMarkdown" />
36+
</n-form-item>
37+
</div>
38+
</template>

src/tools/html-to-markdown/index.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { Markdown } from '@vicons/tabler';
2+
import { defineTool } from '../tool';
3+
4+
export const tool = defineTool({
5+
name: 'Html to markdown',
6+
path: '/html-to-markdown',
7+
description: 'Convert HTML (either from clipboard) to Markdown',
8+
keywords: ['html', 'markdown', 'converter'],
9+
component: () => import('./html-to-markdown.vue'),
10+
icon: Markdown,
11+
createdAt: new Date('2024-01-17'),
12+
});
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
declare module '@guyplusplus/turndown-plugin-gfm' {
2+
export function gfm(turndown: any);
3+
}

src/tools/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ import { tool as dockerComposeToDockerRunConverter } from './docker-compose-to-d
116116
import { tool as yamlViewer } from './yaml-viewer';
117117
import { tool as barcodeReader } from './barcode-reader';
118118
import { tool as barcodeGenerator } from './barcode-generator';
119+
import { tool as htmlToMarkdown } from './html-to-markdown';
119120

120121
export const toolsByCategory: ToolCategory[] = [
121122
{
@@ -158,6 +159,7 @@ export const toolsByCategory: ToolCategory[] = [
158159
listConverter,
159160
tomlToJson,
160161
tomlToYaml,
162+
htmlToMarkdown,
161163
xmlToJson,
162164
jsonToXml,
163165
markdownToHtml,

src/ui/c-input-text/c-input-text.vue

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ const props = withDefaults(
3131
autosize?: boolean
3232
autofocus?: boolean
3333
monospace?: boolean
34+
pasteHtml?: boolean
3435
}>(),
3536
{
3637
value: '',
@@ -58,13 +59,14 @@ const props = withDefaults(
5859
autosize: false,
5960
autofocus: false,
6061
monospace: false,
62+
pasteHtml: false,
6163
},
6264
);
6365
const emit = defineEmits(['update:value']);
6466
const value = useVModel(props, 'value', emit);
6567
const showPassword = ref(false);
6668
67-
const { id, placeholder, label, validationRules, labelPosition, labelWidth, labelAlign, autosize, readonly, disabled, clearable, type, multiline, rows, rawText, autofocus, monospace } = toRefs(props);
69+
const { id, placeholder, label, validationRules, labelPosition, labelWidth, labelAlign, autosize, readonly, disabled, clearable, type, multiline, rows, rawText, autofocus, monospace, pasteHtml } = toRefs(props);
6870
6971
const validation
7072
= props.validation
@@ -81,6 +83,28 @@ const textareaRef = ref<HTMLTextAreaElement>();
8183
const inputRef = ref<HTMLInputElement>();
8284
const inputWrapperRef = ref<HTMLElement>();
8385
86+
interface HTMLElementWithValue {
87+
value?: string
88+
}
89+
90+
function onPasteInputHtml(evt: ClipboardEvent) {
91+
if (!pasteHtml.value) {
92+
return false;
93+
}
94+
95+
const target = (evt.target as HTMLElementWithValue);
96+
if (!target) {
97+
return false;
98+
}
99+
evt.preventDefault();
100+
const textHtmlData = evt.clipboardData?.getData('text/html');
101+
if (textHtmlData && textHtmlData !== '') {
102+
value.value = textHtmlData;
103+
return true;
104+
}
105+
return false;
106+
}
107+
84108
watch(
85109
[value, autosize, multiline, inputWrapperRef, textareaRef],
86110
() => nextTick(() => {
@@ -173,6 +197,7 @@ defineExpose({
173197
:autocorrect="autocorrect ?? (rawText ? 'off' : undefined)"
174198
:spellcheck="spellcheck ?? (rawText ? false : undefined)"
175199
:rows="rows"
200+
@paste="onPasteInputHtml"
176201
/>
177202

178203
<input
@@ -194,6 +219,7 @@ defineExpose({
194219
:autocomplete="autocomplete ?? (rawText ? 'off' : undefined)"
195220
:autocorrect="autocorrect ?? (rawText ? 'off' : undefined)"
196221
:spellcheck="spellcheck ?? (rawText ? false : undefined)"
222+
@paste="onPasteInputHtml"
197223
>
198224

199225
<c-button v-if="clearable && value" variant="text" circle size="small" @click="value = ''">

0 commit comments

Comments
 (0)