feat: add theme management with light and dark modes
- Implemented a new composable `useTheme` for managing theme state. - Added functions to read and write theme preference to local storage. - Applied theme styles to the DOM based on user preference. - Introduced a toggle function to switch between light and dark themes. refactor: enhance copilot plugin functionality - Improved request handling with sequence and document versioning. - Refactored ghost text handling to improve clarity and efficiency. - Updated markdown insertion logic to handle parsed content more robustly. - Enhanced error handling and logging for better debugging. style: update global styles for light and dark themes - Defined CSS variables for light and dark themes to streamline styling. - Improved overall styling consistency and responsiveness. - Added transitions for smoother theme changes and interactions.
This commit is contained in:
@@ -100,7 +100,8 @@ import { onMounted, onUnmounted, ref, computed } from 'vue'
|
||||
import { replaceAll } from '@milkdown/kit/utils'
|
||||
import { Crepe } from '@milkdown/crepe'
|
||||
import { editorViewCtx, serializerCtx } from '@milkdown/kit/core'
|
||||
import { copilotPlugin, copilotConfigCtx, copilotGhostMark, setCopilotEnabled, COPILOT_PLUGIN_KEY, SIZE_LIMIT, checkSizeLimit } from '../plugins/copilotPlugin'
|
||||
import { Selection } from '@milkdown/prose/state'
|
||||
import { copilotPlugin, copilotConfigCtx, copilotGhostMark, setCopilotEnabled, COPILOT_PLUGIN_KEY, SIZE_LIMIT, checkSizeLimit, clearGhostSuggestion } from '../plugins/copilotPlugin'
|
||||
import { fetchSuggestion } from '../utils/api.js'
|
||||
import { DEBUG, OCR_URL } from '../utils/config.js'
|
||||
import { setOcrCache, clearOcrCache, clearAllOcrCache } from '../utils/ocrCache.js'
|
||||
@@ -252,13 +253,7 @@ const logDebugInfo = async () => {
|
||||
}
|
||||
|
||||
const clearCurrentSuggestion = (view) => {
|
||||
const state = COPILOT_PLUGIN_KEY.getState(view.state)
|
||||
if (state?.suggestion && state.from < state.to) {
|
||||
const tr = view.state.tr
|
||||
.delete(state.from, state.to)
|
||||
.setMeta(COPILOT_PLUGIN_KEY, { from: 0, to: 0, suggestion: '' })
|
||||
view.dispatch(tr)
|
||||
}
|
||||
clearGhostSuggestion(view)
|
||||
}
|
||||
|
||||
const performOCR = async (file, cacheKey) => {
|
||||
@@ -289,11 +284,6 @@ const performOCR = async (file, cacheKey) => {
|
||||
if (data.text) {
|
||||
setOcrCache(cacheKey, data.text)
|
||||
setOcrCache(file.name, data.text)
|
||||
if (crepe?.editor) {
|
||||
crepe.editor.action((ctx) => {
|
||||
refreshSizeAndLimit(ctx)
|
||||
})
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('[OCR] Error:', e)
|
||||
@@ -438,13 +428,16 @@ const insertImageAtCursor = (src) => {
|
||||
const view = ctx.get(editorViewCtx)
|
||||
const { state } = view
|
||||
const { schema } = state
|
||||
const { from, to } = state.selection
|
||||
|
||||
const imageType = schema.nodes.image
|
||||
if (!imageType) return
|
||||
|
||||
const imageNode = imageType.create({ src })
|
||||
const tr = state.tr.replaceSelectionWith(imageNode)
|
||||
view.dispatch(tr)
|
||||
const tr = state.tr.replaceRangeWith(from, to, imageNode)
|
||||
const cursorPos = Math.min(from + imageNode.nodeSize, tr.doc.content.size)
|
||||
tr.setSelection(Selection.near(tr.doc.resolve(cursorPos), 1))
|
||||
view.dispatch(tr.scrollIntoView())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -513,61 +506,61 @@ onUnmounted(() => {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
padding: 10px;
|
||||
background-color: #fff;
|
||||
color: #666;
|
||||
border: 1px solid #ddd;
|
||||
background-color: var(--btn-bg);
|
||||
color: var(--btn-fg);
|
||||
border: 1px solid var(--panel-border);
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
||||
box-shadow: var(--panel-shadow);
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.action-btn:hover {
|
||||
background-color: #4a90d9;
|
||||
color: white;
|
||||
border-color: #4a90d9;
|
||||
background-color: var(--btn-hover-bg);
|
||||
color: var(--btn-hover-fg);
|
||||
border-color: var(--btn-hover-bg);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.action-btn.ai-disabled {
|
||||
background-color: #333;
|
||||
color: #fff;
|
||||
border-color: #333;
|
||||
background-color: var(--crepe-color-surface-low);
|
||||
color: var(--crepe-color-on-background);
|
||||
border-color: var(--panel-border);
|
||||
}
|
||||
|
||||
.action-btn.ai-disabled:hover {
|
||||
background-color: #4a90d9;
|
||||
color: white;
|
||||
border-color: #4a90d9;
|
||||
background-color: var(--btn-hover-bg);
|
||||
color: var(--btn-hover-fg);
|
||||
border-color: var(--btn-hover-bg);
|
||||
}
|
||||
|
||||
.action-btn.force-disabled {
|
||||
background-color: #ccc;
|
||||
color: #999;
|
||||
border-color: #ccc;
|
||||
background-color: var(--btn-disabled-bg);
|
||||
color: var(--btn-disabled-fg);
|
||||
border-color: var(--btn-disabled-bg);
|
||||
cursor: not-allowed;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.action-btn.force-disabled:hover {
|
||||
background-color: #ccc;
|
||||
color: #999;
|
||||
border-color: #ccc;
|
||||
background-color: var(--btn-disabled-bg);
|
||||
color: var(--btn-disabled-fg);
|
||||
border-color: var(--btn-disabled-bg);
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.size-indicator {
|
||||
font-size: 10px;
|
||||
color: #999;
|
||||
color: var(--muted-text);
|
||||
text-align: center;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.size-indicator.over-limit {
|
||||
color: #e74c3c;
|
||||
color: var(--danger-text);
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
@@ -580,8 +573,8 @@ onUnmounted(() => {
|
||||
right: 100%;
|
||||
transform: translateY(-50%);
|
||||
margin-right: 8px;
|
||||
background: #333;
|
||||
color: #fff;
|
||||
background: var(--tooltip-bg);
|
||||
color: var(--tooltip-fg);
|
||||
font-size: 12px;
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
@@ -608,10 +601,10 @@ onUnmounted(() => {
|
||||
bottom: 100%;
|
||||
right: 0;
|
||||
margin-bottom: 8px;
|
||||
background: #fff;
|
||||
border: 1px solid #ddd;
|
||||
background: var(--panel-bg);
|
||||
border: 1px solid var(--panel-border);
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
|
||||
box-shadow: var(--panel-shadow);
|
||||
overflow: hidden;
|
||||
z-index: 10000;
|
||||
min-width: 160px;
|
||||
@@ -626,11 +619,11 @@ onUnmounted(() => {
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
color: var(--app-text);
|
||||
}
|
||||
|
||||
.image-dropdown button:hover {
|
||||
background: #f5f5f5;
|
||||
background: var(--crepe-color-hover);
|
||||
}
|
||||
|
||||
.url-dialog-overlay {
|
||||
@@ -639,7 +632,7 @@ onUnmounted(() => {
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0,0,0,0.3);
|
||||
background: var(--overlay-bg);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@@ -647,32 +640,35 @@ onUnmounted(() => {
|
||||
}
|
||||
|
||||
.url-dialog {
|
||||
background: #fff;
|
||||
background: var(--panel-bg);
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 16px rgba(0,0,0,0.2);
|
||||
border: 1px solid var(--panel-border);
|
||||
box-shadow: var(--panel-shadow);
|
||||
min-width: 320px;
|
||||
}
|
||||
|
||||
.url-dialog h3 {
|
||||
margin: 0 0 12px 0;
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
color: var(--app-text);
|
||||
}
|
||||
|
||||
.url-dialog input {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid #ddd;
|
||||
border: 1px solid var(--panel-border);
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
margin-bottom: 16px;
|
||||
color: var(--app-text);
|
||||
background: var(--crepe-color-background);
|
||||
}
|
||||
|
||||
.url-dialog input:focus {
|
||||
outline: none;
|
||||
border-color: #4a90d9;
|
||||
border-color: var(--focus-ring);
|
||||
}
|
||||
|
||||
.url-dialog-buttons {
|
||||
@@ -683,32 +679,32 @@ onUnmounted(() => {
|
||||
|
||||
.dialog-btn {
|
||||
padding: 8px 16px;
|
||||
border: 1px solid #ddd;
|
||||
border: 1px solid var(--panel-border);
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
background: #fff;
|
||||
color: #333;
|
||||
background: var(--btn-bg);
|
||||
color: var(--btn-fg);
|
||||
}
|
||||
|
||||
.dialog-btn:hover {
|
||||
background: #f5f5f5;
|
||||
background: var(--crepe-color-hover);
|
||||
}
|
||||
|
||||
.dialog-btn.primary {
|
||||
background: #4a90d9;
|
||||
color: #fff;
|
||||
border-color: #4a90d9;
|
||||
background: var(--btn-hover-bg);
|
||||
color: var(--btn-hover-fg);
|
||||
border-color: var(--btn-hover-bg);
|
||||
}
|
||||
|
||||
.dialog-btn.primary:hover {
|
||||
background: #3a80c9;
|
||||
filter: brightness(0.92);
|
||||
}
|
||||
|
||||
.milkdown-editor {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #ffffff;
|
||||
background-color: var(--crepe-color-background);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
@@ -770,10 +766,14 @@ onUnmounted(() => {
|
||||
}
|
||||
|
||||
.milkdown-editor::-webkit-scrollbar-thumb {
|
||||
background-color: #ddd;
|
||||
background-color: var(--scrollbar-thumb);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.milkdown-editor::-webkit-scrollbar-thumb:hover {
|
||||
background-color: var(--scrollbar-thumb-hover);
|
||||
}
|
||||
|
||||
.milkdown-editor :deep(.milkdown__toolbar),
|
||||
.milkdown-editor :deep(.milkdown__menu),
|
||||
.milkdown-editor :deep(.milkdown__statusbar),
|
||||
@@ -799,8 +799,8 @@ onUnmounted(() => {
|
||||
|
||||
<style>
|
||||
.copilot-ghost-text {
|
||||
color: #999;
|
||||
opacity: 0.6;
|
||||
color: var(--ghost-text);
|
||||
opacity: 0.72;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
@@ -817,7 +817,7 @@ onUnmounted(() => {
|
||||
}
|
||||
|
||||
.copilot-ghost-text code {
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
background-color: var(--ghost-code-bg);
|
||||
padding: 0.2em 0.4em;
|
||||
border-radius: 3px;
|
||||
}
|
||||
@@ -825,4 +825,16 @@ onUnmounted(() => {
|
||||
.copilot-ghost-text a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.copilot-ghost-block {
|
||||
color: var(--ghost-text);
|
||||
opacity: 0.72;
|
||||
}
|
||||
|
||||
.copilot-ghost-block code,
|
||||
.copilot-ghost-block pre,
|
||||
.copilot-ghost-block a {
|
||||
color: inherit;
|
||||
opacity: inherit;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user