Skip to content

Commit add654a

Browse files
author
codewec
committed
feat: images without api url
1 parent 2d20b3d commit add654a

21 files changed

Lines changed: 1014 additions & 21 deletions

app/components/editoro/MainContent.vue

Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { computed } from 'vue'
33
import { ListKeymap, TaskItem, TaskList } from '@tiptap/extension-list'
44
import type { DirectoryTreeNode, EditorSuggestionItems, EditorToolbarItems, EditorViewMode, TreeNode, TreeNodeType } from '~/types/editoro'
55
import { useEditoroMainContentMedia } from '~/composables/editor/useEditoroMainContentMedia'
6+
import { transformMarkdownLinks } from '~/utils/editoro-vault-links'
67
78
const { t } = useI18n()
89
@@ -52,10 +53,29 @@ const {
5253
onFileInputChange
5354
} = useEditoroMainContentMedia({
5455
canUploadImage: () => props.canUploadImage,
56+
getCurrentFilePath: () => props.selectedPath,
5557
uploadImage: props.uploadImage,
56-
uploadFile: props.uploadFile
58+
uploadFile: props.uploadFile,
59+
openMarkdownPath: (path: string) => emit('selectPath', path)
5760
})
5861
62+
const richEditorContent = computed(() => {
63+
if (!props.selectedPath) {
64+
return props.editorContent
65+
}
66+
67+
return transformMarkdownLinks(props.editorContent, props.selectedPath, 'to-rich')
68+
})
69+
70+
function onRichEditorContentChange(value: string) {
71+
if (!props.selectedPath) {
72+
emit('updateEditorContent', value)
73+
return
74+
}
75+
76+
emit('updateEditorContent', transformMarkdownLinks(value, props.selectedPath, 'to-raw'))
77+
}
78+
5979
const richEditorHandlers = computed(() => ({
6080
...editorHandlers,
6181
// Disable prompt-based link handler; link editing is handled by custom popover UI.
@@ -192,13 +212,13 @@ const editorExtensions = [TaskList, TaskItem, ListKeymap]
192212

193213
<UEditor
194214
v-else
195-
:model-value="props.editorContent"
215+
:model-value="richEditorContent"
196216
class="editoro-editor"
197217
content-type="markdown"
198218
:extensions="editorExtensions"
199219
:handlers="richEditorHandlers"
200220
:placeholder="t('main.editorPlaceholder')"
201-
@update:model-value="emit('updateEditorContent', String($event || ''))"
221+
@update:model-value="onRichEditorContentChange(String($event || ''))"
202222
>
203223
<template #default="{ editor }">
204224
<span
@@ -368,12 +388,16 @@ const editorExtensions = [TaskList, TaskItem, ListKeymap]
368388
line-height: 1.5;
369389
}
370390
371-
.editoro-editor :deep(.tiptap a[href*='/api/files/file?path=']) {
391+
.editoro-editor :deep(.tiptap a[href^='/']:not([href^='//'])),
392+
.editoro-editor :deep(.tiptap a[href^='./']),
393+
.editoro-editor :deep(.tiptap a[href^='../']) {
372394
position: relative;
373395
padding-left: 1.1rem;
374396
}
375397
376-
.editoro-editor :deep(.tiptap a[href*='/api/files/file?path=']::before) {
398+
.editoro-editor :deep(.tiptap a[href^='/']:not([href^='//'])::before),
399+
.editoro-editor :deep(.tiptap a[href^='./']::before),
400+
.editoro-editor :deep(.tiptap a[href^='../']::before) {
377401
content: '';
378402
position: absolute;
379403
left: 0;
@@ -388,6 +412,32 @@ const editorExtensions = [TaskList, TaskItem, ListKeymap]
388412
pointer-events: none;
389413
}
390414
415+
.editoro-editor :deep(.tiptap a[href^='http://']),
416+
.editoro-editor :deep(.tiptap a[href^='https://']),
417+
.editoro-editor :deep(.tiptap a[href^='mailto:']),
418+
.editoro-editor :deep(.tiptap a[href^='tel:']) {
419+
position: relative;
420+
padding-left: 1.1rem;
421+
}
422+
423+
.editoro-editor :deep(.tiptap a[href^='http://']::before),
424+
.editoro-editor :deep(.tiptap a[href^='https://']::before),
425+
.editoro-editor :deep(.tiptap a[href^='mailto:']::before),
426+
.editoro-editor :deep(.tiptap a[href^='tel:']::before) {
427+
content: '';
428+
position: absolute;
429+
left: 0;
430+
top: 50%;
431+
width: 14px;
432+
height: 14px;
433+
transform: translateY(-50%);
434+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%236b7280' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M15 3h6v6'/%3E%3Cpath d='M10 14 21 3'/%3E%3Cpath d='M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6'/%3E%3C/svg%3E");
435+
background-repeat: no-repeat;
436+
background-position: center;
437+
background-size: 14px 14px;
438+
pointer-events: none;
439+
}
440+
391441
.editoro-editor :deep(.editoro-link-modifier a[href]) {
392442
cursor: pointer;
393443
}

app/components/editoro/Modals.vue

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ const props = defineProps<{
1717
deleteModalOpen: boolean
1818
deleteTitle: string
1919
selectedPath: string
20+
moveProgressModalOpen: boolean
21+
moveProgressValue: number
22+
moveProgressStage: string
23+
moveSharedAttachmentCount: number
2024
}>()
2125
2226
const emit = defineEmits<{
@@ -155,6 +159,34 @@ const emit = defineEmits<{
155159
</div>
156160
</template>
157161
</UModal>
162+
163+
<UModal
164+
:open="props.moveProgressModalOpen"
165+
:dismissible="false"
166+
:title="t('move.title')"
167+
:description="t('move.description')"
168+
>
169+
<template #body>
170+
<div class="editoro-modal-body">
171+
<div class="editoro-move-stage">
172+
<UIcon
173+
name="i-lucide-loader-circle"
174+
class="editoro-move-spinner"
175+
/>
176+
<span>{{ props.moveProgressStage }}</span>
177+
</div>
178+
179+
<UProgress :value="props.moveProgressValue" />
180+
181+
<span
182+
v-if="props.moveSharedAttachmentCount > 0"
183+
class="editoro-move-note"
184+
>
185+
{{ t('move.sharedAttachments', { count: props.moveSharedAttachmentCount }) }}
186+
</span>
187+
</div>
188+
</template>
189+
</UModal>
158190
</template>
159191

160192
<style scoped>
@@ -170,4 +202,32 @@ const emit = defineEmits<{
170202
gap: 0.5rem;
171203
width: 100%;
172204
}
205+
206+
.editoro-move-stage {
207+
display: flex;
208+
align-items: center;
209+
gap: 0.5rem;
210+
font-size: 0.875rem;
211+
}
212+
213+
.editoro-move-spinner {
214+
width: 1rem;
215+
height: 1rem;
216+
animation: editoro-spin 0.9s linear infinite;
217+
}
218+
219+
.editoro-move-note {
220+
color: var(--ui-text-muted);
221+
font-size: 0.75rem;
222+
}
223+
224+
@keyframes editoro-spin {
225+
from {
226+
transform: rotate(0deg);
227+
}
228+
229+
to {
230+
transform: rotate(360deg);
231+
}
232+
}
173233
</style>

app/composables/api/buildTreeApi.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ export function buildTreeApi(options: BuildEditoroApiOptions) {
1313
selectedNode: treeRefs.selectedNode,
1414
expandedPaths: treeRefs.expandedTreePaths,
1515
isLoading: treeRefs.isLoadingTree,
16+
isMoveInProgress: treeRefs.moveProgressModalOpen,
17+
moveProgressValue: treeRefs.moveProgressValue,
18+
moveProgressStage: treeRefs.moveProgressStage,
19+
moveSharedAttachmentCount: treeRefs.moveSharedAttachmentCount,
1620
draggingPath: treeRefs.draggingPath,
1721
dragOverTargetPath: treeRefs.dragOverTargetPath,
1822
selectNodeByPath: treeStore.selectNodeByPath,

app/composables/editor/useEditoroEditorUploads.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import type { Ref } from 'vue'
66
import type { Translator } from '~/types/editoro'
77
import { uploadFileApi, uploadImageApi } from '~/services/files-api'
8+
import { toRelativeVaultLink } from '~/utils/editoro-vault-links'
89

910
type EditoroEditorUploadsOptions = {
1011
t: Translator
@@ -21,7 +22,7 @@ export function useEditoroEditorUploads(options: EditoroEditorUploadsOptions) {
2122

2223
try {
2324
const result = await uploadImageApi(options.activeFilePath.value, file)
24-
return result.url
25+
return toRelativeVaultLink(options.activeFilePath.value, result.path)
2526
} catch (error) {
2627
console.error(error)
2728
options.notifyError(options.t('errors.uploadImage'))
@@ -37,7 +38,7 @@ export function useEditoroEditorUploads(options: EditoroEditorUploadsOptions) {
3738

3839
try {
3940
const result = await uploadFileApi(options.activeFilePath.value, file)
40-
return result.url
41+
return toRelativeVaultLink(options.activeFilePath.value, result.path)
4142
} catch (error) {
4243
console.error(error)
4344
options.notifyError(options.t('errors.uploadFile'))

app/composables/editor/useEditoroMainContentMedia.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import { onBeforeUnmount, ref, watch } from 'vue'
2+
import { isMarkdownPath } from '~/utils/editoro-file'
3+
import { resolveVaultPath, toRuntimeLinkUrl } from '~/utils/editoro-vault-links'
24

35
type EditorLike = {
46
view?: {
@@ -34,8 +36,10 @@ type EditorHandlerLike = {
3436

3537
type EditoroMainContentMediaOptions = {
3638
canUploadImage: () => boolean
39+
getCurrentFilePath: () => string
3740
uploadImage: (file: File) => Promise<string | null>
3841
uploadFile: (file: File) => Promise<string | null>
42+
openMarkdownPath: (path: string) => void
3943
}
4044

4145
/**
@@ -265,9 +269,23 @@ export function useEditoroMainContentMedia(options: EditoroMainContentMediaOptio
265269
event.preventDefault()
266270
if (isExternalHref(href)) {
267271
window.open(href, '_blank', 'noopener,noreferrer')
268-
} else {
269-
window.location.assign(href)
272+
return
273+
}
274+
275+
const currentFilePath = options.getCurrentFilePath()
276+
const vaultPath = resolveVaultPath(href, currentFilePath)
277+
if (vaultPath && isMarkdownPath(vaultPath)) {
278+
options.openMarkdownPath(vaultPath)
279+
return
270280
}
281+
282+
const runtimeUrl = toRuntimeLinkUrl(href, currentFilePath)
283+
if (isExternalHref(runtimeUrl)) {
284+
window.open(runtimeUrl, '_blank', 'noopener,noreferrer')
285+
return
286+
}
287+
288+
window.location.assign(runtimeUrl)
271289
}
272290

273291
const onKeyDown = (event: KeyboardEvent) => {

app/composables/useEditoroContext.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ export function useEditoroContext() {
6060
renameInputName: uiRefs.renameInputName,
6161
selectedBaseDirectory: viewModel.selectedBaseDirectory,
6262
loadTree,
63+
moveEntry: treeStore.moveEntryWithProgress,
6364
uiActions: {
6465
closeCreateModal: uiStore.closeCreateModal,
6566
openRenameModal: uiStore.openRenameModal,

app/composables/useEditoroEntryActions.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*/
55
import type { ComputedRef, Ref } from 'vue'
66
import type { CreateTargetType, TreeNode } from '~/types/editoro'
7-
import { createEntryApi, deleteEntryApi, moveEntryApi } from '~/services/files-api'
7+
import { createEntryApi, deleteEntryApi } from '~/services/files-api'
88
import { getFileExtension, isMarkdownPath } from '~/utils/editoro-file'
99
import { normalizeCreatePathInput } from '~/utils/editoro-create'
1010
import { buildPath, findNodeByPath, getParentPath } from '~/utils/editoro-path'
@@ -27,6 +27,7 @@ type EntryActionOptions = {
2727
renameInputName: Ref<string>
2828
selectedBaseDirectory: ComputedRef<string>
2929
loadTree: (preferPath?: string) => Promise<void>
30+
moveEntry: (from: string, to: string) => Promise<string>
3031
uiActions: UiActionsLike
3132
}
3233

@@ -110,9 +111,9 @@ export function useEditoroEntryActions(options: EntryActionOptions) {
110111
const targetPath = buildPath(getParentPath(node.path), nextName)
111112

112113
try {
113-
const result = await moveEntryApi(node.path, targetPath)
114+
const movedPath = await options.moveEntry(node.path, targetPath)
114115

115-
await options.loadTree(result.path)
116+
await options.loadTree(movedPath)
116117
options.uiActions.closeRenameModal()
117118
options.renameInputName.value = ''
118119
} catch (error) {

app/composables/useEditoroTreeDnD.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44
*/
55
import type { Ref } from 'vue'
66
import type { TreeNode } from '~/types/editoro'
7-
import { moveEntryApi } from '~/services/files-api'
87
import { buildPath, findNodeByPath, getBaseName, getParentPath } from '~/utils/editoro-path'
98

109
type TreeDnDOptions = {
1110
treeItems: Ref<TreeNode[]>
11+
moveEntry: (from: string, to: string) => Promise<string>
1212
loadTree: (preferPath?: string) => Promise<void>
1313
notifyMoveError: () => void
1414
}
@@ -109,8 +109,8 @@ export function useEditoroTreeDnD(options: TreeDnDOptions) {
109109
const targetPath = buildPath(targetDirectoryPath, getBaseName(sourceNode.path))
110110

111111
try {
112-
const result = await moveEntryApi(sourceNode.path, targetPath)
113-
await options.loadTree(result.path)
112+
const movedPath = await options.moveEntry(sourceNode.path, targetPath)
113+
await options.loadTree(movedPath)
114114
} catch (error) {
115115
console.error(error)
116116
options.notifyMoveError()

app/composables/workspace/useEditoroMainContentBindings.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ export function useEditoroMainContentBindings(options: WorkspaceBindingOptions)
3232
updateEditorContent: (value: string) => {
3333
state.editor.content.value = value
3434
},
35-
selectPath: state.tree.selectNodeByPath,
35+
selectPath: (path: string) => {
36+
void state.actions.openPath(path)
37+
},
3638
goParent: state.tree.goToFolderParent
3739
}
3840

app/composables/workspace/useEditoroModalsBindings.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,11 @@ export function useEditoroModalsBindings(state: EditoroState) {
1818
renameInputName: state.ui.renameInputName.value,
1919
deleteModalOpen: state.ui.deleteModalOpen.value,
2020
deleteTitle: state.view.deleteTitle.value,
21-
selectedPath: state.view.selectedPath.value
21+
selectedPath: state.view.selectedPath.value,
22+
moveProgressModalOpen: state.tree.isMoveInProgress.value,
23+
moveProgressValue: state.tree.moveProgressValue.value,
24+
moveProgressStage: state.tree.moveProgressStage.value,
25+
moveSharedAttachmentCount: state.tree.moveSharedAttachmentCount.value
2226
}))
2327

2428
const modalsHandlers = {

0 commit comments

Comments
 (0)