Skip to content
Merged
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
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,13 @@
"markmap-lib": "^0.18.11",
"markmap-view": "^0.18.10",
"mermaid": "^11.6.0",
"nanoid": "^3.3.8",
"nearest-color": "^0.4.4",
"onigasm": "^2.2.5",
"prettier": "^3.5.3",
"radix-vue": "^1.9.17",
"sanitize-html": "^2.15.0",
"slash": "^3.0.0",
"slugify": "^1.6.6",
"tailwind-merge": "^3.0.2",
"toml": "^3.0.0",
Expand Down
20 changes: 16 additions & 4 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src/main/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ function createWindow() {
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
nodeIntegration: true,
webSecurity: false,
},
})

Expand Down
33 changes: 33 additions & 0 deletions src/main/ipc/handlers/fs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Buffer } from 'node:buffer'
import { join, parse } from 'node:path'
import { ipcMain } from 'electron'
import { ensureDirSync, writeFileSync } from 'fs-extra'
import { nanoid } from 'nanoid'
import slash from 'slash'
import { store } from '../../store'

const ASSETS_DIR = 'assets'

export function registerFsHandlers() {
ipcMain.handle('fs:assets', (event, { buffer, fileName }) => {
const storagePath = store.preferences.get('storagePath')

return new Promise((resolve, reject) => {
try {
const assetsPath = join(storagePath, ASSETS_DIR)

const { ext } = parse(fileName)
const name = `${nanoid()}${ext}`
const dest = join(assetsPath, name)

ensureDirSync(assetsPath)
writeFileSync(dest, Buffer.from(buffer))

resolve(slash(join(ASSETS_DIR, name)))
}
catch (error) {
reject(error)
}
})
})
}
2 changes: 2 additions & 0 deletions src/main/ipc/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { Channel } from '../types/ipc'
import { BrowserWindow } from 'electron'
import { registerDBHandlers } from './handlers/db'
import { registerDialogHandlers } from './handlers/dialog'
import { registerFsHandlers } from './handlers/fs'
import { registerPrettierHandlers } from './handlers/prettier'
import { registerSystemHandlers } from './handlers/system'

Expand All @@ -14,4 +15,5 @@ export function registerIPC() {
registerDBHandlers()
registerSystemHandlers()
registerPrettierHandlers()
registerFsHandlers()
}
7 changes: 7 additions & 0 deletions src/main/types/ipc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,20 @@ type MainMenuAction =
type DBAction = 'relaod' | 'move' | 'migrate' | 'clear'
type SystemAction = 'reload' | 'open-external'
type PrettierAction = 'format'
type FsAction = 'assets'

export type MainMenuChannel = CombineWith<MainMenuAction, 'main-menu'>
export type DBChannel = CombineWith<DBAction, 'db'>
export type SystemChannel = CombineWith<SystemAction, 'system'>
export type PrettierChannel = CombineWith<PrettierAction, 'prettier'>
export type FsChannel = CombineWith<FsAction, 'fs'>

export type Channel =
| MainMenuChannel
| DBChannel
| SystemChannel
| PrettierChannel
| FsChannel

export interface DialogOptions {
properties?: OpenDialogOptions['properties']
Expand All @@ -45,3 +48,7 @@ export interface PrettierOptions {
text: string
parser: string
}

export interface FsAssetsOptions {
path: string
}
28 changes: 28 additions & 0 deletions src/renderer/components/editor/Editor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,34 @@ async function init() {

editor.on('scroll', hideScrollbar)

editor.on('drop', async (cm, e) => {
if (selectedSnippetContent.value?.language === 'markdown') {
const file = e.dataTransfer?.files[0]

if (!file)
return

if (!file.type.startsWith('image/'))
return

try {
const arrayBuffer = await file.arrayBuffer()
const buffer = Array.from(new Uint8Array(arrayBuffer))

// Вызываем IPC хендлер для сохранения файла из буфера
const relativePath = await ipc.invoke('fs:assets', {
buffer,
fileName: file.name,
})

cm.replaceSelection(`![${file.name}](./${relativePath})`)
}
catch (error) {
console.error('Ошибка при добавлении изображения:', error)
}
}
})

ipc.on('main-menu:font-size-increase', () => {
settings.fontSize++
fontSize.value = `${settings.fontSize}px`
Expand Down
65 changes: 36 additions & 29 deletions src/renderer/components/editor/markdown/Markdown.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script setup lang="ts">
import { useSnippets } from '@/composables'
import { i18n, ipc } from '@/electron'
import { i18n, ipc, store } from '@/electron'
import { useDark } from '@vueuse/core'
import CodeMirror from 'codemirror'
import { Minus, Plus } from 'lucide-vue-next'
Expand All @@ -14,6 +14,8 @@ import 'codemirror/addon/runmode/runmode'
import 'codemirror/theme/neo.css'
import 'codemirror/theme/oceanic-next.css'

const isDev = import.meta.env.DEV

const { selectedSnippetContent } = useSnippets()
const isDark = useDark()
const { scaleToShow, onZoom } = useMarkdown()
Expand Down Expand Up @@ -58,7 +60,7 @@ async function renderMarkdown() {
if (selectedSnippetContent.value?.value) {
const markdownHtml = await marked.parse(selectedSnippetContent.value.value)

const sanitizedHtml = sanitizeHtml(markdownHtml, {
let sanitizedHtml = sanitizeHtml(markdownHtml, {
allowedTags: sanitizeHtml.defaults.allowedTags.concat(['img', 'del']),
allowedAttributes: {
'*': [
Expand All @@ -81,6 +83,13 @@ async function renderMarkdown() {
allowedSchemes: ['http', 'https', 'masscode'],
})

const re = /src="\.\//g
const path = store.preferences.get('storagePath')

sanitizedHtml = isDev
? sanitizedHtml.replace(re, `src="file://${path}/`)
: sanitizedHtml.replace(re, `src="${path}/`)

renderedContent.value = sanitizedHtml
}
else {
Expand Down Expand Up @@ -167,35 +176,33 @@ watch(isDark, (value) => {
</script>

<template>
<div>
<EditorHeaderTool v-if="isShowHeaderTool">
<div class="flex w-full justify-end gap-2 px-2">
<UiActionButton
:tooltip="i18n.t('button.zoomOut')"
@click="onZoom('out')"
>
<Minus class="h-3 w-3" />
</UiActionButton>
<div class="tabular-nums select-none">
{{ scaleToShow }}
</div>
<UiActionButton
:tooltip="i18n.t('button.zoomIn')"
@click="onZoom('in')"
>
<Plus class="h-3 w-3" />
</UiActionButton>
</div>
</EditorHeaderTool>
<PerfectScrollbar :options="{ minScrollbarLength: 20 }">
<div
ref="markdownRef"
class="markdown-content"
<EditorHeaderTool v-if="isShowHeaderTool">
<div class="flex w-full justify-end gap-2 px-2">
<UiActionButton
:tooltip="i18n.t('button.zoomOut')"
@click="onZoom('out')"
>
<div v-html="renderedContent" />
<Minus class="h-3 w-3" />
</UiActionButton>
<div class="tabular-nums select-none">
{{ scaleToShow }}
</div>
</PerfectScrollbar>
</div>
<UiActionButton
:tooltip="i18n.t('button.zoomIn')"
@click="onZoom('in')"
>
<Plus class="h-3 w-3" />
</UiActionButton>
</div>
</EditorHeaderTool>
<PerfectScrollbar :options="{ minScrollbarLength: 20 }">
<div
ref="markdownRef"
class="markdown-content"
>
<div v-html="renderedContent" />
</div>
</PerfectScrollbar>
</template>

<style>
Expand Down
1 change: 0 additions & 1 deletion src/renderer/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="Content-Security-Policy" content="default-src * self blob: data: gap:; style-src * self 'unsafe-inline' blob: data: gap:; script-src * 'self' 'unsafe-eval' 'unsafe-inline' blob: data: gap:; object-src * 'self' blob: data: gap:; img-src * self 'unsafe-inline' blob: data: gap:; connect-src * self 'unsafe-inline' blob: data: gap:; frame-src * self blob: data: gap:;">
<title>massCode</title>
</head>
<body>
Expand Down