fix(editor): watch floating editor availability

This commit is contained in:
purocean
2026-05-02 12:39:35 +08:00
parent 7bf288e641
commit ec709bcbd4
2 changed files with 146 additions and 134 deletions
+13 -10
View File
@@ -40,7 +40,7 @@
<script lang="ts">
import { defineComponent, onBeforeUnmount, onMounted, ref, toRefs, watchPostEffect } from 'vue'
import { $args, FLAG_DISABLE_XTERM } from '@fe/support/args'
import { emitResize, setContainerDom, toggleEditor, toggleSide, toggleView, toggleContentRightSide, toggleXterm, type LayoutContainerName } from '@fe/services/layout'
import { emitResize, setContainerDom, toggleEditor, toggleSide, toggleView, toggleContentRightSide, toggleXterm } from '@fe/services/layout'
import { isElectron } from '@fe/support/env'
import store from '@fe/support/store'
@@ -60,6 +60,16 @@ export default defineComponent({
const content = ref<HTMLElement | null>(null)
const contentRightSide = ref<HTMLElement | null>(null)
const refs: any = { aside, editor, terminal, contentRightSide }
const containerRefs = [
['layout', layout],
['aside', aside],
['right', right],
['content', content],
['editor', editor],
['preview', preview],
['terminal', terminal],
['contentRightSide', contentRightSide],
] as const
function resizeOutOfRange (ref: string, outOfRange: null | 'min' | 'max') {
if (ref === 'aside' && outOfRange === 'min') {
@@ -254,14 +264,7 @@ export default defineComponent({
})
onMounted(() => {
setContainerDom('layout', layout.value)
setContainerDom('aside', aside.value)
setContainerDom('right', right.value)
setContainerDom('content', content.value)
setContainerDom('editor', editor.value)
setContainerDom('preview', preview.value)
setContainerDom('terminal', terminal.value)
setContainerDom('contentRightSide', contentRightSide.value)
containerRefs.forEach(([name, element]) => setContainerDom(name, element.value))
window.addEventListener('resize', emitResize)
window.document.addEventListener('mousemove', resizeFrame)
@@ -269,7 +272,7 @@ export default defineComponent({
})
onBeforeUnmount(() => {
;(['layout', 'aside', 'right', 'content', 'editor', 'preview', 'terminal', 'contentRightSide'] as LayoutContainerName[]).forEach(name => setContainerDom(name, null))
containerRefs.forEach(([name]) => setContainerDom(name, null))
window.removeEventListener('resize', emitResize)
window.document.removeEventListener('mousemove', resizeFrame)
+133 -124
View File
@@ -28,6 +28,113 @@ const HINT_DURATION = 5000
const EDITOR_SCROLL_SYNC_PAUSE_TIMEOUT = 350
const CLOSE_SYNC_PAUSE_TIMEOUT = 800
const PREVIEW_CLICK_IGNORE_TAGS = ['button', 'input', 'textarea', 'select', 'option', 'img', 'canvas', 'video', 'audio', 'details', 'summary']
const FLOATING_EDITOR_STYLES = `
.floating-editor-active {
display: flex !important;
position: fixed !important;
z-index: 200000 !important;
box-sizing: border-box !important;
padding-top: ${TITLE_HEIGHT}px !important;
padding-bottom: 0 !important;
border: 1px solid var(--g-color-80) !important;
border-radius: 6px !important;
box-shadow: 0 8px 28px rgba(0, 0, 0, 0.28) !important;
background: var(--g-color-96) !important;
overflow: hidden !important;
min-width: 0 !important;
max-width: none !important;
flex: none !important;
}
.floating-editor-titlebar {
position: absolute;
left: 0;
right: 0;
top: 0;
height: ${TITLE_HEIGHT}px;
display: flex;
align-items: center;
gap: 6px;
box-sizing: border-box;
padding: 3px 6px 3px 10px;
border-bottom: 1px solid var(--g-color-86);
background: var(--g-color-96);
color: var(--g-color-20);
cursor: grab;
user-select: none;
z-index: 2;
}
.floating-editor-titlebar.floating-editor-dragging {
cursor: grabbing;
}
.floating-editor-title {
flex: 1;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 12px;
font-weight: 600;
text-align: right;
}
.floating-editor-button {
width: 22px;
height: 22px;
padding: 0;
margin: 0;
border: 0;
border-radius: 50%;
background: transparent;
color: var(--g-color-30);
cursor: default;
display: flex;
align-items: center;
justify-content: center;
flex: none;
}
.floating-editor-button:hover {
background: var(--g-color-86);
color: var(--g-color-0);
}
.floating-editor-resize-handle {
position: absolute;
left: 0;
right: 0;
height: ${RESIZE_HANDLE_SIZE}px;
cursor: ns-resize;
background: transparent;
z-index: 3;
}
.floating-editor-resize-top {
top: 0;
}
.floating-editor-resize-bottom {
bottom: 0;
}
.floating-editor-hint {
position: fixed;
z-index: 200001;
max-width: min(360px, calc(100vw - 32px));
padding: 7px 10px;
border: 1px solid var(--g-color-82);
border-radius: 6px;
background: var(--g-color-96);
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.18);
color: var(--g-color-40);
font-size: 12px;
line-height: 18px;
pointer-events: none;
user-select: none;
}
`
function clamp (value: number, min: number, max: number) {
return Math.min(max, Math.max(min, value))
@@ -60,113 +167,7 @@ export default {
return ctx.layout.getContainerDom('preview')
}
ctx.theme.addStyles(`
.floating-editor-active {
display: flex !important;
position: fixed !important;
z-index: 200000 !important;
box-sizing: border-box !important;
padding-top: ${TITLE_HEIGHT}px !important;
padding-bottom: 0 !important;
border: 1px solid var(--g-color-80) !important;
border-radius: 6px !important;
box-shadow: 0 8px 28px rgba(0, 0, 0, 0.28) !important;
background: var(--g-color-96) !important;
overflow: hidden !important;
min-width: 0 !important;
max-width: none !important;
flex: none !important;
}
.floating-editor-titlebar {
position: absolute;
left: 0;
right: 0;
top: 0;
height: ${TITLE_HEIGHT}px;
display: flex;
align-items: center;
gap: 6px;
box-sizing: border-box;
padding: 3px 6px 3px 10px;
border-bottom: 1px solid var(--g-color-86);
background: var(--g-color-96);
color: var(--g-color-20);
cursor: grab;
user-select: none;
z-index: 2;
}
.floating-editor-titlebar.floating-editor-dragging {
cursor: grabbing;
}
.floating-editor-title {
flex: 1;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 12px;
font-weight: 600;
text-align: right;
}
.floating-editor-button {
width: 22px;
height: 22px;
padding: 0;
margin: 0;
border: 0;
border-radius: 50%;
background: transparent;
color: var(--g-color-30);
cursor: default;
display: flex;
align-items: center;
justify-content: center;
flex: none;
}
.floating-editor-button:hover {
background: var(--g-color-86);
color: var(--g-color-0);
}
.floating-editor-resize-handle {
position: absolute;
left: 0;
right: 0;
height: ${RESIZE_HANDLE_SIZE}px;
cursor: ns-resize;
background: transparent;
z-index: 3;
}
.floating-editor-resize-top {
top: 0;
}
.floating-editor-resize-bottom {
bottom: 0;
}
.floating-editor-hint {
position: fixed;
z-index: 200001;
max-width: min(360px, calc(100vw - 32px));
padding: 7px 10px;
border: 1px solid var(--g-color-82);
border-radius: 6px;
background: var(--g-color-96);
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.18);
color: var(--g-color-40);
font-size: 12px;
line-height: 18px;
pointer-events: none;
user-select: none;
}
`).catch(console.warn)
ctx.theme.addStyles(FLOATING_EDITOR_STYLES).catch(console.warn)
function getHintText () {
const key = isMacOS ? 'Option' : 'Alt'
@@ -327,6 +328,16 @@ export default {
window.setTimeout(checkHint)
}
function resetFloatingState (scheduleHint = false) {
hideFloatingEditor()
hideHint()
lastHintEligible = false
if (scheduleHint) {
scheduleCheckHint()
}
}
function makeButton (title: string, onClick: () => void) {
const btn = document.createElement('button')
btn.type = 'button'
@@ -745,6 +756,19 @@ export default {
ctx.editor.whenEditorReady().then(({ editor }) => {
editor.onDidScrollChange(pausePreviewSyncForEditorScroll)
}).catch(console.warn)
ctx.lib.vue.watch(() => canShowFloatingEditor(), (canShow) => {
if (!canShow) {
if (visible) {
hideFloatingEditor()
}
hideHint()
lastHintEligible = false
return
}
checkHint()
})
ctx.registerHook('GLOBAL_RESIZE', () => {
if (visible) {
clampFrame()
@@ -752,27 +776,12 @@ export default {
}
positionHint()
})
ctx.registerHook('ACTION_BEFORE_RUN', ({ name }) => {
if (visible && name === 'layout.toggle-editor') {
hideFloatingEditor()
}
})
ctx.registerHook('ACTION_AFTER_RUN', ({ name }) => {
if (name === 'layout.toggle-editor') {
scheduleCheckHint()
}
})
ctx.registerHook('VIEW_RENDERED', scheduleCheckHint)
ctx.registerHook('DOC_BEFORE_SWITCH', () => {
hideFloatingEditor()
hideHint()
lastHintEligible = false
resetFloatingState()
})
ctx.registerHook('VIEW_FILE_CHANGE', () => {
hideFloatingEditor()
hideHint()
lastHintEligible = false
scheduleCheckHint()
resetFloatingState(true)
})
}
} as Plugin