mirror of
https://github.com/requarks/wiki.git
synced 2026-10-14 06:01:58 -05:00
fix: various markdown editor fixes
This commit is contained in:
5 files changed
+126
-23
No files matched your search
@@ -5,7 +5,7 @@
|
||||
never waits on (or depends on) the icon service. Regenerate with `npm run icons` after adding or
|
||||
removing an icon; `check-icons.mjs` fails the build if this drifts.
|
||||
|
||||
259 icons.
|
||||
260 icons.
|
||||
*/
|
||||
export const BUNDLED_ICONS = {
|
||||
"la:angle-double-right": {"body":"<path fill=\"currentColor\" d=\"M9.094 4.781L7.688 6.22l9.78 9.78l-9.78 9.781l1.406 1.438L20.313 16zm7 0L14.687 6.22L24.47 16l-9.782 9.781l1.407 1.438L27.312 16z\"/>","width":32,"height":32},
|
||||
@@ -266,5 +266,6 @@ export const BUNDLED_ICONS = {
|
||||
"mdi:transfer-down": {"body":"<path fill=\"currentColor\" d=\"M16 3v2H8V3zm0 4v2H8V7zm0 4v2H8v-2zM5 15h14l-7 7z\"/>","width":24,"height":24},
|
||||
"mdi:transfer-up": {"body":"<path fill=\"currentColor\" d=\"M8 21v-2h8v2zm0-4v-2h8v2zm0-4v-2h8v2zm11-4H5l7-7z\"/>","width":24,"height":24},
|
||||
"mdi:undo-variant": {"body":"<path fill=\"currentColor\" d=\"M13.5 7a6.5 6.5 0 0 1 6.5 6.5a6.5 6.5 0 0 1-6.5 6.5H10v-2h3.5c2.5 0 4.5-2 4.5-4.5S16 9 13.5 9H7.83l3.08 3.09L9.5 13.5L4 8l5.5-5.5l1.42 1.41L7.83 7zM6 18h2v2H6z\"/>","width":24,"height":24},
|
||||
"mdi:view-split-vertical": {"body":"<path fill=\"currentColor\" d=\"M13 5h8v14h-8zM3 5h8v2H3zm0 6V9h8v2zm0 8v-2h8v2zm0-4v-2h8v2z\"/>","width":24,"height":24},
|
||||
"mdi:web": {"body":"<path fill=\"currentColor\" d=\"M16.36 14c.08-.66.14-1.32.14-2s-.06-1.34-.14-2h3.38c.16.64.26 1.31.26 2s-.1 1.36-.26 2m-5.15 5.56c.6-1.11 1.06-2.31 1.38-3.56h2.95a8.03 8.03 0 0 1-4.33 3.56M14.34 14H9.66c-.1-.66-.16-1.32-.16-2s.06-1.35.16-2h4.68c.09.65.16 1.32.16 2s-.07 1.34-.16 2M12 19.96c-.83-1.2-1.5-2.53-1.91-3.96h3.82c-.41 1.43-1.08 2.76-1.91 3.96M8 8H5.08A7.92 7.92 0 0 1 9.4 4.44C8.8 5.55 8.35 6.75 8 8m-2.92 8H8c.35 1.25.8 2.45 1.4 3.56A8 8 0 0 1 5.08 16m-.82-2C4.1 13.36 4 12.69 4 12s.1-1.36.26-2h3.38c-.08.66-.14 1.32-.14 2s.06 1.34.14 2M12 4.03c.83 1.2 1.5 2.54 1.91 3.97h-3.82c.41-1.43 1.08-2.77 1.91-3.97M18.92 8h-2.95a15.7 15.7 0 0 0-1.38-3.56c1.84.63 3.37 1.9 4.33 3.56M12 2C6.47 2 2 6.5 2 12a10 10 0 0 0 10 10a10 10 0 0 0 10-10A10 10 0 0 0 12 2\"/>","width":24,"height":24}
|
||||
}
|
||||
@@ -233,6 +233,19 @@
|
||||
t('editor.markup.keyboardKey')
|
||||
}}</w-tooltip>
|
||||
</w-btn>
|
||||
<!-- -> The only way back once the preview is closed: its own toggle goes with it -->
|
||||
<template v-if="!state.previewShown">
|
||||
<w-space />
|
||||
<w-btn
|
||||
icon="mdi:view-split-vertical"
|
||||
padding="xs sm"
|
||||
flat
|
||||
@click="state.previewShown = true">
|
||||
<w-tooltip anchor="top middle" self="bottom middle">{{
|
||||
t('editor.togglePreviewPane')
|
||||
}}</w-tooltip>
|
||||
</w-btn>
|
||||
</template>
|
||||
</div>
|
||||
<!-- ------------------------------------------------------- -->
|
||||
<!-- MONACO EDITOR -->
|
||||
@@ -857,10 +870,6 @@ function processContent(newContent) {
|
||||
})
|
||||
}
|
||||
|
||||
function openEditorSettings() {
|
||||
siteStore.$patch({ overlay: 'EditorMarkdownConfig' })
|
||||
}
|
||||
|
||||
/**
|
||||
* Take files the author brought in — pasted or dropped — and write markdown for them at the cursor.
|
||||
*
|
||||
@@ -1203,7 +1212,6 @@ onMounted(async () => {
|
||||
EVENT_BUS.on('insertAsset', insertAssetClb)
|
||||
EVENT_BUS.on('insertTable', insertTableClb)
|
||||
EVENT_BUS.on('insertBlock', insertBlockClb)
|
||||
EVENT_BUS.on('openEditorSettings', openEditorSettings)
|
||||
EVENT_BUS.on('reloadEditorContent', reloadEditorContent)
|
||||
|
||||
// this.$root.$on('editorInsert', opts => {
|
||||
@@ -1243,7 +1251,6 @@ onBeforeUnmount(() => {
|
||||
EVENT_BUS.off('insertAsset', insertAssetClb)
|
||||
EVENT_BUS.off('insertTable', insertTableClb)
|
||||
EVENT_BUS.off('insertBlock', insertBlockClb)
|
||||
EVENT_BUS.off('openEditorSettings', openEditorSettings)
|
||||
EVENT_BUS.off('reloadEditorContent', reloadEditorContent)
|
||||
pasteCaptureNode?.removeEventListener('paste', onEditorPaste, true)
|
||||
monacoRef.value?.removeEventListener('dragover', onEditorDragOver)
|
||||
@@ -1283,6 +1290,14 @@ $editor-height-mobile: calc(100vh - 112px - 16px);
|
||||
height: $editor-height;
|
||||
position: relative;
|
||||
border-right: 5px solid $primary;
|
||||
/*
|
||||
Monaco writes its measured width in pixels onto its own elements, so this item's automatic
|
||||
min-width -- min-content, i.e. whatever Monaco last laid itself out at -- pins it to the full
|
||||
width it took while the preview was closed. Bringing the preview back then leaves it the few
|
||||
pixels the flex line has left over, and Monaco never re-measures because its container never
|
||||
shrinks. Zero lets the basis decide instead.
|
||||
*/
|
||||
min-width: 0;
|
||||
}
|
||||
&-editor {
|
||||
display: block;
|
||||
@@ -1318,16 +1333,21 @@ $editor-height-mobile: calc(100vh - 112px - 16px);
|
||||
// @include until($tablet) {
|
||||
// display: none;
|
||||
// }
|
||||
/*
|
||||
`-enter-from` is the Vue 3 name; as `-enter` it matched nothing, so the pane animated shut but
|
||||
snapped open. The inner selector was stale in the same way -- the content class is
|
||||
`-preview-content` -- which left the render reflowing for the length of the transition.
|
||||
*/
|
||||
&-enter-active,
|
||||
&-leave-active {
|
||||
transition: max-width 0.5s ease;
|
||||
max-width: 50vw;
|
||||
.editor-code-preview-content {
|
||||
.editor-markdown-preview-content {
|
||||
width: 50vw;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
&-enter,
|
||||
&-enter-from,
|
||||
&-leave-to {
|
||||
max-width: 0;
|
||||
}
|
||||
@@ -1445,6 +1465,9 @@ $editor-height-mobile: calc(100vh - 112px - 16px);
|
||||
border-left: 60px solid color.adjust($primary, $lightness: -5%);
|
||||
color: #fff;
|
||||
height: 32px;
|
||||
// -> Flex so the preview toggle can be pushed to the far right by `w-space`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
&-sidebar {
|
||||
background-color: $dark-4;
|
||||
|
||||
@@ -197,15 +197,6 @@
|
||||
type="a">
|
||||
<w-tooltip>{{ t(`common.actions.viewDocs`) }}</w-tooltip>
|
||||
</w-btn>
|
||||
<w-btn
|
||||
class="ml-2 acrylic-btn"
|
||||
icon="la:cog"
|
||||
flat
|
||||
color="grey"
|
||||
:aria-label="t(`editor.settings`)"
|
||||
@click="openEditorSettings">
|
||||
<w-tooltip>{{ t(`editor.settings`) }}</w-tooltip>
|
||||
</w-btn>
|
||||
</template>
|
||||
<!--
|
||||
Not `v-else-if` on the block below: changes made from the page properties panel put the header
|
||||
@@ -462,10 +453,6 @@ function onEditableBlur(field, event) {
|
||||
syncEditable(event.target, tidied)
|
||||
}
|
||||
|
||||
function openEditorSettings() {
|
||||
EVENT_BUS.emit('openEditorSettings')
|
||||
}
|
||||
|
||||
async function discardChanges() {
|
||||
// From create mode
|
||||
if (editorStore.mode === 'create') {
|
||||
|
||||
@@ -76,6 +76,11 @@
|
||||
|
||||
--content-mark: #fdf1a0;
|
||||
|
||||
/* The box behind the tick of a done task-list item; the tick itself is white in both themes */
|
||||
--content-tick: #5b616b;
|
||||
/* -> And the empty box of an item still to do, white whatever surface the content sits on */
|
||||
--content-tick-empty: #fff;
|
||||
|
||||
/* Admonition hues: the bar and the wash behind it, per severity */
|
||||
--content-info: #1867c0;
|
||||
--content-info-wash: rgba(24, 103, 192, 0.08);
|
||||
@@ -153,6 +158,14 @@
|
||||
|
||||
--content-mark: #6b5d13;
|
||||
|
||||
/*
|
||||
Lighter than the light theme's, because the box has to be seen against a dark page -- but held
|
||||
well below the white of the tick on it, which is what makes the tick readable.
|
||||
*/
|
||||
--content-tick: rgba(255, 255, 255, 0.38);
|
||||
/* -> A white box would be a lit square on a dark page; the empty one is just its outline */
|
||||
--content-tick-empty: transparent;
|
||||
|
||||
--content-info: #6ab0ff;
|
||||
--content-info-wash: rgba(106, 176, 255, 0.12);
|
||||
--content-success: #4ecf9a;
|
||||
@@ -588,16 +601,53 @@
|
||||
/*
|
||||
Laid out inline rather than as a flex row: a task item can contain a nested list, and as a flex
|
||||
child that list would sit BESIDE the text instead of under it.
|
||||
|
||||
Drawn here rather than left to the browser, and NOT with `accent-color`: the renderer marks these
|
||||
inputs disabled, and a disabled checkbox ignores `accent-color` entirely -- Chrome paints it its
|
||||
own washed-out grey, which is the pale tick this replaces. `appearance: none` is what gets the
|
||||
box back under this stylesheet's control; the tick then has to be drawn too, since the one the
|
||||
browser supplies goes with the appearance.
|
||||
*/
|
||||
.task-list-item-checkbox {
|
||||
/*
|
||||
The prefixed form is for Safari before 15.4, which is the last engine that will not take the
|
||||
unprefixed one -- and which without it draws its own rounded box UNDER these rules, so the
|
||||
border and the fill land on a control that is still the platform's.
|
||||
*/
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
/* -> So the box measures 1em in every engine rather than 1em plus whatever the UA pads it by */
|
||||
box-sizing: border-box;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
/* -> Firefox gives a checkbox 3px of its own margin, Chrome 3px 3px 3px 4px */
|
||||
margin: 0 0.5em 0 0;
|
||||
accent-color: var(--color-primary);
|
||||
padding: 0;
|
||||
border: 1px solid var(--content-rule-strong);
|
||||
border-radius: 3px;
|
||||
/* -> White rather than the surface behind it, so the box reads as empty and not as unfilled */
|
||||
background-color: var(--content-tick-empty);
|
||||
/* -> Disabled by the renderer, so it must not read as something to try to click */
|
||||
cursor: default;
|
||||
/* -> Safari and Firefox both dim a disabled control; here it is a mark, not a dead input */
|
||||
opacity: 1;
|
||||
vertical-align: -0.1em;
|
||||
|
||||
/*
|
||||
A done item: a filled box with the tick laid over it. White in both themes -- the dark theme
|
||||
lightens the box rather than the tick, so this one mark works on either.
|
||||
*/
|
||||
&:checked {
|
||||
border-color: transparent;
|
||||
background-color: var(--content-tick);
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='none' stroke='%23fff' stroke-width='3.2' stroke-linecap='round' stroke-linejoin='round' d='M5 12.5l4.5 4.5L19 7'/%3E%3C/svg%3E");
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
background-size: 0.8em 0.8em;
|
||||
/* -> Print drops background fills, and a done item that prints as an empty box says the opposite */
|
||||
print-color-adjust: exact;
|
||||
-webkit-print-color-adjust: exact;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1091,6 +1141,18 @@
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
/*
|
||||
An image in a list item stays in the sentence. Tailwind's preflight declares `img { display:
|
||||
block }`, so without this the image takes a line of its own and the item's own words go under it,
|
||||
which reads as a broken item rather than as an illustrated one.
|
||||
|
||||
`align-left` / `align-right` / `align-center` still win on specificity, so an author who asks for
|
||||
one of those in a list still gets it.
|
||||
*/
|
||||
li img {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/* The alignment classes an author can attach through `markdown-it-attrs` */
|
||||
img,
|
||||
figure {
|
||||
|
||||
@@ -169,6 +169,36 @@ export class MarkdownRenderer {
|
||||
return inlineSpan(state, silent)
|
||||
})
|
||||
|
||||
/*
|
||||
MDC's inline props, `{.class}`, and `markdown-it-attrs` both claim `{`, and MDC gets there first
|
||||
— it runs while the inline is being parsed, `markdown-it-attrs` in a core rule afterwards, so
|
||||
whatever MDC takes is already gone by the time the braces would have become attributes.
|
||||
|
||||
That is what made `{.is-warning}` on the line under a blockquote do nothing at all: the braces
|
||||
were eaten and the class never reached the element. The same collision crashed the render
|
||||
outright — `Cannot read properties of undefined (reading 'tag')` out of MDC's own renderer —
|
||||
when the braces opened an inline, since the props it parsed then had no node to attach to. In
|
||||
the editor that reads as the preview freezing on the last good render, and a save then storing
|
||||
that stale HTML.
|
||||
|
||||
The two are told apart by what comes before the brace, which is also what each one means by it:
|
||||
MDC's props decorate the thing they are stuck to (`[text]{.cls}`, `{.cls}`), while a
|
||||
brace opening a line, or standing off behind a space, is `markdown-it-attrs` addressing the
|
||||
block as a whole. So MDC keeps every brace that abuts a preceding character and lets the rest
|
||||
fall through to the core rule.
|
||||
*/
|
||||
const propsRule = this.md.inline.ruler.__rules__.find(
|
||||
(rule) => rule.name === 'mdc_inline_props'
|
||||
)
|
||||
const inlineProps = propsRule.fn
|
||||
this.md.inline.ruler.at('mdc_inline_props', (state, silent) => {
|
||||
const preceding = state.src[state.pos - 1]
|
||||
if (preceding === undefined || /\s/.test(preceding)) {
|
||||
return false
|
||||
}
|
||||
return inlineProps(state, silent)
|
||||
})
|
||||
|
||||
if (config.underline) {
|
||||
this.md.use(mdUnderline)
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user