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
89 changes: 62 additions & 27 deletions src/main/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,22 @@ import { store } from './store'
process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = 'true' // Отключаем security warnings

const isDev = process.env.NODE_ENV === 'development'
const gotTheLock = app.requestSingleInstanceLock()

let mainWindow: BrowserWindow
let isQuitting = false

if (process.defaultApp) {
if (process.argv.length >= 2) {
app.setAsDefaultProtocolClient('masscode', process.execPath, [
path.resolve(process.argv[1]),
])
}
}
else {
app.setAsDefaultProtocolClient('masscode')
}

function createWindow() {
const bounds = store.app.get('bounds')
mainWindow = new BrowserWindow({
Expand Down Expand Up @@ -54,37 +66,60 @@ function createWindow() {
})
}

app.whenReady().then(() => {
createWindow()
registerIPC()
if (!gotTheLock) {
app.quit()
}
else {
app.whenReady().then(() => {
createWindow()
registerIPC()

initApi()
initApi()

if (store.app.get('isAutoMigratedFromJson')) {
return
}
if (store.app.get('isAutoMigratedFromJson')) {
return
}

try {
const jsonDbPath = `${store.preferences.get('storagePath')}/db.json`
const jsonData = readFileSync(jsonDbPath, 'utf8')
try {
const jsonDbPath = `${store.preferences.get('storagePath')}/db.json`
const jsonData = readFileSync(jsonDbPath, 'utf8')

migrateJsonToSqlite(JSON.parse(jsonData))
store.app.set('isAutoMigratedFromJson', true)
}
catch (err) {
console.error('Error on auto migration JSON to SQLite:', err)
}
})
migrateJsonToSqlite(JSON.parse(jsonData))
store.app.set('isAutoMigratedFromJson', true)
}
catch (err) {
console.error('Error on auto migration JSON to SQLite:', err)
}
})

app.on('activate', () => {
mainWindow.show()
})
app.on('activate', () => {
mainWindow.show()
})

app.on('before-quit', () => {
isQuitting = true
})

app.on('before-quit', () => {
isQuitting = true
})
app.on('window-all-closed', () => {
if (process.platform !== 'darwin')
app.quit()
})

app.on('second-instance', (_, argv) => {
if (mainWindow) {
mainWindow.isMinimized() ? mainWindow.restore() : mainWindow.focus()
}

app.on('window-all-closed', () => {
if (process.platform !== 'darwin')
app.quit()
})
if (process.platform !== 'darwin') {
const url = argv.find(i => i.startsWith('masscode://'))
BrowserWindow.getFocusedWindow()?.webContents.send(
'system:deep-link',
url,
)
}
})

app.on('open-url', (_, url) => {
BrowserWindow.getFocusedWindow()?.webContents.send('system:deep-link', url)
})
}
2 changes: 1 addition & 1 deletion src/main/types/ipc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ type MainMenuAction =
| 'presentation-mode'

type DBAction = 'relaod' | 'move' | 'migrate' | 'clear'
type SystemAction = 'reload' | 'open-external'
type SystemAction = 'reload' | 'open-external' | 'deep-link'
type PrettierAction = 'format'
type FsAction = 'assets'

Expand Down
4 changes: 0 additions & 4 deletions src/renderer/components/editor/code-image/CodeImage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -217,10 +217,6 @@ onMounted(() => {
{{ i18n.t("button.background") }}
</span>
</div>
<!-- <div class="flex items-center gap-2">
<div class="w-4 h-4 rounded-sm gradient-disco border border-text" />
<div class="w-4 h-4 rounded-sm gradient-salad" />
</div> -->
<EditorCodeImageBackgroundSwitch v-model:active="activeBackground" />
</div>
<div>
Expand Down
77 changes: 36 additions & 41 deletions src/renderer/components/editor/markdown/Markdown.vue
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,8 @@ marked.use({
return `<div id="${id}" class="code-block"></div>`
},
link({ href, text }) {
if (/^masscode:\/\/snippets/.test(href)) {
const id = href.split('/').pop()
return `<a href="${href}" class="snippet-link" data-snippet-id="${id}">${text}</a>`
if (href.startsWith('masscode://')) {
return `<a href="${href}" class="deep-link">${text}</a>`
}
else {
return `<a href="${href}" class="external">${text}</a>`
Expand Down Expand Up @@ -134,19 +133,10 @@ function onLinkClick(event: MouseEvent) {

if (target.tagName === 'A') {
const href = target.getAttribute('href')
if (href) {
if (target.classList.contains('snippet-link')) {
const snippetId = target.getAttribute('data-snippet-id')
if (snippetId) {
// TODO: Implement snippet link

event.preventDefault()
}
}
else if (target.classList.contains('external')) {
ipc.invoke('system:open-external', href)
event.preventDefault()
}

if (href && target.classList.contains('external')) {
ipc.invoke('system:open-external', href)
event.preventDefault()
}
}
}
Expand Down Expand Up @@ -176,33 +166,38 @@ watch(isDark, (value) => {
</script>

<template>
<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
data-editor-markdown
class="grid grid-rows-[auto_1fr] overflow-scroll"
>
<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>
<UiActionButton
:tooltip="i18n.t('button.zoomIn')"
@click="onZoom('in')"
</EditorHeaderTool>
<PerfectScrollbar :options="{ minScrollbarLength: 20 }">
<div
ref="markdownRef"
class="markdown-content"
>
<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>
<div v-html="renderedContent" />
</div>
</PerfectScrollbar>
</div>
</template>

<style>
Expand Down
16 changes: 11 additions & 5 deletions src/renderer/components/sidebar/folders/TreeNode.vue
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,19 @@ const {
contextMenu,
} = inject(treeKeys)!

const { highlightedFolderId, highlightedSnippetIds, highlightedTagId, state }
= useApp()
const {
highlightedFolderId,
highlightedSnippetIds,
highlightedTagId,
state,
focusedFolderId,
} = useApp()
const { displayedSnippets, updateSnippets, selectFirstSnippet } = useSnippets()
const { updateFolder, renameFolderId } = useFolders()

const hoveredId = ref()
const overPosition = ref<Position>()
const isDragged = ref(false)
const isFocused = ref(false)

const rowRef = ref<HTMLElement>()

Expand All @@ -56,9 +60,11 @@ const isHovered = computed(() => {
})

const isSelected = computed(() => state.folderId === props.node.id)

const isHighlighted = computed(
() => highlightedFolderId.value === props.node.id,
)
const isFocused = computed(() => focusedFolderId.value === props.node.id)

const isShowBetweenLine = computed(() => {
if (!isAllowed.value)
Expand Down Expand Up @@ -99,7 +105,7 @@ function onClickNode(id: string | number) {
highlightedFolderId.value = undefined
highlightedTagId.value = undefined
state.tagId = undefined
isFocused.value = true
focusedFolderId.value = Number(id)
clickNode(Number(id))
}

Expand Down Expand Up @@ -203,7 +209,7 @@ async function onDrop(e: DragEvent) {
}

onClickOutside(rowRef, () => {
isFocused.value = false
focusedFolderId.value = undefined
highlightedFolderId.value = undefined
})

Expand Down
23 changes: 19 additions & 4 deletions src/renderer/components/snippet/Item.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import * as ContextMenu from '@/components/ui/shadcn/context-menu'
import { useApp, useDialog, useSnippets } from '@/composables'
import { LibraryFilter } from '@/composables/types'
import { i18n } from '@/electron'
import { onClickOutside } from '@vueuse/core'
import { onClickOutside, useClipboard } from '@vueuse/core'
import { format } from 'date-fns'

interface Props {
Expand All @@ -17,6 +17,7 @@ const {
highlightedSnippetIds,
highlightedFolderId,
isFocusedSnippetName,
focusedSnippetId,
state,
} = useApp()

Expand All @@ -33,11 +34,12 @@ const {
} = useSnippets()

const { confirm } = useDialog()
const { copy } = useClipboard()

const isFocused = ref(false)
const snippetRef = ref<HTMLDivElement>()

const isSelected = computed(() => state.snippetId === props.snippet.id)

const isInMultiSelection = computed(
() =>
selectedSnippetIds.value.length > 1
Expand All @@ -47,6 +49,8 @@ const isHighlighted = computed(() =>
highlightedSnippetIds.value.has(props.snippet.id),
)

const isFocused = computed(() => focusedSnippetId.value === props.snippet.id)

const isDuplicateDisabled = computed(
() => highlightedSnippetIds.value.size > 1,
)
Expand All @@ -73,7 +77,7 @@ const folderName = computed(() => {

function onSnippetClick(id: number, event: MouseEvent) {
selectSnippet(id, event.shiftKey)
isFocused.value = true
focusedSnippetId.value = id
}

function onClickContextMenu() {
Expand Down Expand Up @@ -185,6 +189,13 @@ async function onDuplicate() {
isFocusedSnippetName.value = true
}

function onCopySnippetLink() {
// copy(`masscode://folder/${state.folderId}/snippet/${props.snippet.id}`)
copy(
`masscode://goto?folderId=${state.folderId}&snippetId=${props.snippet.id}`,
)
}

function onDragStart(event: DragEvent) {
const ids
= selectedSnippetIds.value.length > 1
Expand Down Expand Up @@ -218,7 +229,7 @@ function onDragStart(event: DragEvent) {
}

onClickOutside(snippetRef, () => {
isFocused.value = false
focusedSnippetId.value = undefined
highlightedSnippetIds.value.clear()
})
</script>
Expand Down Expand Up @@ -267,6 +278,10 @@ onClickOutside(snippetRef, () => {
}}
</ContextMenu.Item>
<ContextMenu.Separator />
<ContextMenu.Item @click="onCopySnippetLink">
{{ i18n.t("action.copy.snippetLink") }}
</ContextMenu.Item>
<ContextMenu.Separator />
<ContextMenu.Item
:disabled="isDuplicateDisabled"
@click="onDuplicate"
Expand Down
4 changes: 4 additions & 0 deletions src/renderer/composables/useApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ const state = reactive<SavedState>(store.app.get('state') as SavedState)
const highlightedFolderId = ref<number>()
const highlightedSnippetIds = ref<Set<number>>(new Set())
const highlightedTagId = ref<number>()
const focusedFolderId = ref<number | undefined>()
const focusedSnippetId = ref<number | undefined>()

const isFocusedSnippetName = ref(false)
const isShowMarkdown = ref(false)
Expand Down Expand Up @@ -57,6 +59,8 @@ watch(state, () => {

export function useApp() {
return {
focusedFolderId,
focusedSnippetId,
highlightedFolderId,
highlightedSnippetIds,
highlightedTagId,
Expand Down
2 changes: 2 additions & 0 deletions src/renderer/ipc/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { registerMainMenuListeners } from './listeners/main-menu'
import { registerSystemListeners } from './listeners/system'

export function registerIPCListeners() {
registerMainMenuListeners()
registerSystemListeners()
}
Loading