Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ declare module '@vue/runtime-core' {
LoremIpsumGenerator: typeof import('./src/tools/lorem-ipsum-generator/lorem-ipsum-generator.vue')['default']
MacAddressGenerator: typeof import('./src/tools/mac-address-generator/mac-address-generator.vue')['default']
MacAddressLookup: typeof import('./src/tools/mac-address-lookup/mac-address-lookup.vue')['default']
MarkdownDiff: typeof import('./src/tools/markdown-diff/markdown-diff.vue')['default']
MarkdownToHtml: typeof import('./src/tools/markdown-to-html/markdown-to-html.vue')['default']
MathEvaluator: typeof import('./src/tools/math-evaluator/math-evaluator.vue')['default']
MenuBar: typeof import('./src/tools/html-wysiwyg-editor/editor/menu-bar.vue')['default']
Expand Down
3 changes: 3 additions & 0 deletions locales/de.yml
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,9 @@ tools:
json-diff:
title: JSON-Unterschied
description: Vergleiche zwei JSON-Objekte und erhalte die Unterschiede zwischen ihnen.
markdown-diff:
title: Markdown-Unterschied
description: Vergleiche zwei Markdown-Dokumente und sieh die Unterschiede zwischen ihnen.
jwt-parser:
title: JWT-Parser
description: >-
Expand Down
4 changes: 4 additions & 0 deletions locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,10 @@ tools:
title: JSON diff
description: Compare two JSON objects and get the differences between them.

markdown-diff:
title: Markdown diff
description: Compare two Markdown documents and see the differences between them.

jwt-parser:
title: JWT parser
description: Parse and decode your JSON Web Token (jwt) and display its content.
Expand Down
4 changes: 4 additions & 0 deletions locales/es.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,7 @@ tools:
measurement: Measurement
text: Text
data: Data

markdown-diff:
title: Markdown diff
description: Compare two Markdown documents and see the differences between them.
4 changes: 4 additions & 0 deletions locales/fr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ tools:
text: Texte
data: Données

markdown-diff:
title: Markdown diff
description: Compare two Markdown documents and see the differences between them.

token-generator:
title: Générateur de token
description: >-
Expand Down
4 changes: 4 additions & 0 deletions locales/no.yml
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,10 @@ tools:
title: JSON diff
description: Sammenlign to JSON objekter og finn forskjellene mellom dem.

markdown-diff:
title: Markdown diff
description: Sammenlign to Markdown-dokumenter og vis forskjellene mellom dem.

jwt-parser:
title: JWT parser
description: Parse og dekode et JSON Web Token (jwt) og vis innholdet.
Expand Down
4 changes: 4 additions & 0 deletions locales/pt.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,7 @@ tools:
measurement: 'Medidas'
text: 'Texto'
data: 'Dados'

markdown-diff:
title: Markdown diff
description: Compare two Markdown documents and see the differences between them.
4 changes: 4 additions & 0 deletions locales/uk.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,7 @@ tools:
measurement: Вимірювання
text: Текст
data: Дані

markdown-diff:
title: Markdown diff
description: Compare two Markdown documents and see the differences between them.
4 changes: 4 additions & 0 deletions locales/vi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,10 @@ tools:
title: So sánh JSON
description: So sánh hai đối tượng JSON và lấy ra sự khác biệt giữa chúng.

markdown-diff:
title: Markdown diff
description: Compare two Markdown documents and see the differences between them.

jwt-parser:
title: Giải mã JWT
description: Giải mã và hiển thị nội dung của JSON Web Token (jwt).
Expand Down
3 changes: 3 additions & 0 deletions locales/zh.yml
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,9 @@ tools:
json-diff:
title: JSON 差异比较
description: 比较两个JSON对象并获得它们之间的差异。
markdown-diff:
title: Markdown diff
description: Compare two Markdown documents and see the differences between them.
jwt-parser:
title: JWT 解析器
description: 解析和解码JSON Web Token(jwt)并显示其内容。
Expand Down
2 changes: 2 additions & 0 deletions src/tools/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { tool as base64FileConverter } from './base64-file-converter';
import { tool as base64StringConverter } from './base64-string-converter';
import { tool as basicAuthGenerator } from './basic-auth-generator';
import { tool as markdownDiff } from './markdown-diff';
import { tool as emailNormalizer } from './email-normalizer';

import { tool as asciiTextDrawer } from './ascii-text-drawer';
Expand Down Expand Up @@ -116,6 +117,7 @@ export const toolsByCategory: ToolCategory[] = [
xmlToJson,
jsonToXml,
markdownToHtml,
markdownDiff,
],
},
{
Expand Down
13 changes: 13 additions & 0 deletions src/tools/markdown-diff/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Markdown } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';

export const tool = defineTool({
name: translate('tools.markdown-diff.title'),
path: '/markdown-diff',
description: translate('tools.markdown-diff.description'),
keywords: ['markdown', 'diff', 'compare', 'difference', 'markdown diff', 'md', 'text'],
component: () => import('./markdown-diff.vue'),
icon: Markdown,
createdAt: new Date('2026-05-21'),
});
12 changes: 12 additions & 0 deletions src/tools/markdown-diff/markdown-diff.constants.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { describe, expect, it } from 'vitest';
import { modifiedMarkdown, originalMarkdown } from './markdown-diff.constants';

describe('markdown-diff constants', () => {
it('provides different default Markdown documents', () => {
expect(originalMarkdown).toContain('# Release notes');
expect(originalMarkdown).toContain('| user | host | plugin |');
expect(modifiedMarkdown).toContain('# Release notes');
expect(modifiedMarkdown).toContain('```sql');
expect(modifiedMarkdown).not.toBe(originalMarkdown);
});
});
48 changes: 48 additions & 0 deletions src/tools/markdown-diff/markdown-diff.constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
export const originalMarkdown = `# Release notes

## Added

- JSON export for reports
- Keyboard shortcuts for navigation
- Markdown table previews

## Fixed

- Preserve whitespace in code blocks

## Plugin support

| user | host | plugin |
| --- | --- | --- |
| mysql.infoschema | localhost | caching_sha2_password |
| mysql.session | localhost | caching_sha2_password |
| mysql.sys | localhost | caching_sha2_password |

\`inline code\` stays readable in preview mode.
`;

export const modifiedMarkdown = `# Release notes

## Added

- JSON and CSV export for reports
- Keyboard shortcuts for navigation
- Dark mode support for charts
- Markdown table previews

## Fixed

- Preserve whitespace in fenced code blocks

## Plugin support

| user | host | plugin |
| --- | --- | --- |
| mysql.session | localhost | caching_sha2_password |
| mysql.sys | localhost | caching_sha2_password |

\`\`\`sql
select user, host, plugin
from mysql.user;
\`\`\`
`;
33 changes: 33 additions & 0 deletions src/tools/markdown-diff/markdown-diff.e2e.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { expect, test } from '@playwright/test';

test.describe('Tool - Markdown diff', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/markdown-diff');
});

test('Has correct title', async ({ page }) => {
await expect(page).toHaveTitle('Markdown diff - IT Tools', { timeout: 15000 });
});

test('Diff editor is visible with two editable panes', async ({ page }) => {
await expect(page.getByTestId('markdown-diff-editor')).toBeVisible();

await expect(page.locator('.monaco-diff-editor')).toBeVisible();
await expect(page.locator('.monaco-editor textarea.inputarea')).toHaveCount(2);
});

test('Preview mode shows rendered source and modified Markdown', async ({ page }) => {
await page.getByTestId('preview').click();

await expect(page.getByTestId('markdown-preview')).toBeVisible();
await expect(page.getByTestId('source-markdown-preview')).toBeVisible();
await expect(page.getByTestId('modified-markdown-preview')).toBeVisible();
await expect(page.getByTestId('source-markdown-preview').locator('table')).toBeVisible();
await expect(page.getByTestId('modified-markdown-preview')).toContainText('caching_sha2_password');

await page.getByTestId('code').click();

await expect(page.getByTestId('markdown-diff-editor')).toBeVisible();
await expect(page.locator('.monaco-diff-editor')).toBeVisible();
});
});
142 changes: 142 additions & 0 deletions src/tools/markdown-diff/markdown-diff.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
<template>
<div class="markdown-diff-tool">
<div flex justify-center mb-4>

Check warning on line 3 in src/tools/markdown-diff/markdown-diff.vue

View workflow job for this annotation

GitHub Actions / ci

UnoCSS attributes are not ordered
<c-buttons-select
v-model:value="mode"
:options="modeOptions"
size="small"
/>
</div>

<c-card v-if="mode === 'code'" w-full important:flex-1 important:pa-0>
<c-diff-editor
v-model:original="sourceMarkdown"
v-model:modified="targetMarkdown"
test-id="markdown-diff-editor"
language="markdown"
height="clamp(620px, 72vh, 820px)"
/>
</c-card>

<c-card v-else data-test-id="markdown-preview" w-full>
<div class="markdown-preview-grid">
<section>
<h3>Source Markdown</h3>
<div class="markdown-preview-pane" data-test-id="source-markdown-preview">
<c-markdown :markdown="sourceMarkdown" />
</div>
</section>

<section>
<h3>Modified Markdown</h3>
<div class="markdown-preview-pane" data-test-id="modified-markdown-preview">
<c-markdown :markdown="targetMarkdown" />
</div>
</section>
</div>
</c-card>
</div>
</template>

<script setup lang="ts">

Check failure on line 41 in src/tools/markdown-diff/markdown-diff.vue

View workflow job for this annotation

GitHub Actions / ci

'<script setup lang=ts>' should be above '<template>' on line 1
import { ref } from 'vue';
import { modifiedMarkdown, originalMarkdown } from './markdown-diff.constants';

type MarkdownDiffMode = 'code' | 'preview';

const mode = ref<MarkdownDiffMode>('code');
const sourceMarkdown = ref(originalMarkdown);
const targetMarkdown = ref(modifiedMarkdown);

const modeOptions: Array<{ label: string; value: MarkdownDiffMode }> = [
{ label: 'Code', value: 'code' },
{ label: 'Preview', value: 'preview' },
];
</script>

<style lang="less" scoped>
.markdown-preview-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 24px;

section {
min-width: 0;
}

h3 {
margin: 0 0 12px;
font-size: 18px;
font-weight: 600;
}
}

.markdown-diff-tool {
width: 100%;
max-width: 1200px;
flex: 1 1 1200px !important;
}

.markdown-preview-pane {
min-height: clamp(620px, 72vh, 820px);
padding: 18px;
border: 1px solid #d8dee8;
border-radius: 8px;
overflow: auto;
}

:deep(.markdown-preview-pane) {
line-height: 1.65;
}

:deep(.markdown-preview-pane > div > *:first-child) {
margin-top: 0;
}

:deep(.markdown-preview-pane > div > *:last-child) {
margin-bottom: 0;
}

:deep(.markdown-preview-pane table) {
width: 100%;
border-collapse: collapse;
margin: 16px 0;
}

:deep(.markdown-preview-pane th),
:deep(.markdown-preview-pane td) {
padding: 10px 12px;
border: 1px solid #cfd6e2;
text-align: left;
}

:deep(.markdown-preview-pane th) {
font-weight: 600;
background: rgb(248 250 252);
}

:deep(.markdown-preview-pane code) {
padding: 2px 5px;
border-radius: 4px;
background: rgb(241 245 249);
}

:deep(.markdown-preview-pane pre) {
padding: 14px;
border-radius: 6px;
overflow: auto;
background: rgb(15 23 42);
}

:deep(.markdown-preview-pane pre code) {
padding: 0;
color: rgb(226 232 240);
background: transparent;
}

@media (max-width: 900px) {
.markdown-preview-grid {
grid-template-columns: 1fr;
}
}
</style>
2 changes: 1 addition & 1 deletion src/ui/c-buttons-select/c-buttons-select.vue
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ function selectOption(option: CButtonSelectOption<T>) {
:tooltip="option.tooltip"
>
<c-button
:test-id="option.value"
:data-test-id="option.value"
:size="size"
:type="option.value === value ? 'primary' : 'default'"
@click="selectOption(option)"
Expand Down
Loading
Loading