agent-zero/plugins/_editor/webui/editor-panel.html
Alessandro 89901b64f0
Some checks are pending
Build And Publish Docker Images / plan (push) Waiting to run
Build And Publish Docker Images / build (push) Blocked by required conditions
Polish native Markdown editor experience
Expand the dedicated Editor surface with safe rendered preview mode, ACE-backed source editing, browser-style tabs, toolbar/file actions, preview search, and richer Markdown rendering for code blocks, task lists, images, tables, math, local links, and footnotes.

Keep open Markdown files synchronized with the active context and saved tool edits, including live refresh for document_artifact and text_editor results without routing Markdown through Desktop/Office.

Add inline preview-page editing, clickable preview task-list checkboxes, source editor rehydration after preview-mode refreshes, and regression coverage for the new editor wiring and sync behavior.
2026-05-15 04:47:24 +02:00

1054 lines
36 KiB
HTML

<html>
<head>
<script type="module">
import { store } from "/plugins/_editor/webui/editor-store.js";
</script>
</head>
<body>
<div x-data>
<template x-if="$store.editor">
<div class="editor-panel" x-create="$store.editor.onMount($el, xAttrs($el) || {})" x-destroy="$store.editor.cleanup()">
<div class="editor-shell" @keydown="$store.editor.handleEditorKeydown($event)">
<div class="editor-tabs" x-show="$store.editor.visibleTabs().length > 0" style="display: none;" role="tablist" aria-label="Open Markdown files">
<template x-for="tab in $store.editor.visibleTabs()" :key="tab.tab_id">
<div
class="editor-tab-shell"
:class="{ 'is-active': $store.editor.isActiveTab(tab), 'is-dirty': $store.editor.isTabDirty(tab), 'is-pending-close': $store.editor.pendingClose?.tabId === tab.tab_id }"
>
<button
type="button"
class="editor-tab"
role="tab"
:aria-selected="$store.editor.isActiveTab(tab).toString()"
:title="$store.editor.tabLabel(tab)"
@click="$store.editor.selectTab(tab.tab_id)"
>
<span class="material-symbols-outlined editor-tab-icon" aria-hidden="true" x-text="$store.editor.tabIcon(tab)"></span>
<span class="editor-tab-title" x-text="$store.editor.tabTitle(tab)"></span>
</button>
<button
type="button"
class="editor-tab-close"
title="Close file"
aria-label="Close file"
@pointerdown.stop
@click.stop="$store.editor.closeTab(tab.tab_id)"
>
<span class="material-symbols-outlined" aria-hidden="true">close</span>
</button>
</div>
</template>
<button
type="button"
class="editor-new-tab"
title="New Markdown"
aria-label="New Markdown"
:disabled="$store.editor.loading || $store.editor.saving"
@click="$store.editor.runNewMenuAction('markdown')"
>
<span class="material-symbols-outlined" aria-hidden="true">add</span>
</button>
</div>
<div class="editor-close-confirm" x-show="$store.editor.hasPendingClose()" style="display: none;" role="status">
<span class="material-symbols-outlined editor-close-confirm-icon" aria-hidden="true">warning</span>
<div class="editor-close-confirm-copy">
<span class="editor-close-confirm-title" x-text="$store.editor.pendingCloseTitle()"></span>
<span class="editor-close-confirm-message" x-text="$store.editor.pendingCloseMessage()"></span>
</div>
<div class="editor-close-confirm-actions">
<button
type="button"
class="editor-text-button is-primary"
x-show="$store.editor.pendingCloseHasDirty()"
:disabled="$store.editor.saving"
@click="$store.editor.confirmPendingClose({ save: true })"
>
Save &amp; Close
</button>
<button
type="button"
class="editor-text-button"
:disabled="$store.editor.saving"
@click="$store.editor.confirmPendingClose({ save: false })"
x-text="$store.editor.pendingCloseDiscardLabel()"
></button>
<button type="button" class="editor-text-button" :disabled="$store.editor.saving" @click="$store.editor.cancelPendingClose()">Cancel</button>
</div>
</div>
<div class="editor-toolbar" x-show="$store.editor.session" style="display: none;">
<div class="editor-toolbar-row">
<div class="editor-tool-group editor-source-tools" x-show="$store.editor.isMarkdown() && $store.editor.isSourceMode()" style="display: none;">
<button type="button" class="editor-icon-button" title="Undo" aria-label="Undo" :disabled="!$store.editor.canUndo()" @click="$store.editor.undo()">
<span class="material-symbols-outlined">undo</span>
</button>
<button type="button" class="editor-icon-button" title="Redo" aria-label="Redo" :disabled="!$store.editor.canRedo()" @click="$store.editor.redo()">
<span class="material-symbols-outlined">redo</span>
</button>
<button type="button" class="editor-icon-button" title="Bold" aria-label="Bold" @click="$store.editor.format('bold')">
<span class="material-symbols-outlined">format_bold</span>
</button>
<button type="button" class="editor-icon-button" title="Italic" aria-label="Italic" @click="$store.editor.format('italic')">
<span class="material-symbols-outlined">format_italic</span>
</button>
<button type="button" class="editor-icon-button" title="List" aria-label="List" @click="$store.editor.format('list')">
<span class="material-symbols-outlined">format_list_bulleted</span>
</button>
<button type="button" class="editor-icon-button" title="Numbered list" aria-label="Numbered list" @click="$store.editor.format('numbered')">
<span class="material-symbols-outlined">format_list_numbered</span>
</button>
<button type="button" class="editor-icon-button" title="Table" aria-label="Table" @click="$store.editor.format('table')">
<span class="material-symbols-outlined">table</span>
</button>
</div>
<div class="editor-tool-group editor-preview-tools" x-show="$store.editor.isPreviewMode()" style="display: none;">
<button type="button" class="editor-icon-button" title="Previous page" aria-label="Previous page" :disabled="$store.editor.previewEditing || $store.editor.activePageIndex <= 0" @click="$store.editor.previousPage()">
<span class="material-symbols-outlined">chevron_left</span>
</button>
<span class="editor-page-count" x-text="$store.editor.pagePositionLabel()"></span>
<button type="button" class="editor-icon-button" title="Next page" aria-label="Next page" :disabled="$store.editor.previewEditing || $store.editor.activePageIndex >= $store.editor.pages().length - 1" @click="$store.editor.nextPage()">
<span class="material-symbols-outlined">chevron_right</span>
</button>
<button type="button" class="editor-icon-button" title="Edit page" aria-label="Edit page" x-show="!$store.editor.previewEditing" @click="$store.editor.startPreviewEdit()">
<span class="material-symbols-outlined">edit_note</span>
</button>
<button type="button" class="editor-icon-button is-primary" title="Apply page edit" aria-label="Apply page edit" x-show="$store.editor.previewEditing" @click="$store.editor.applyPreviewEdit()">
<span class="material-symbols-outlined">check</span>
</button>
<button type="button" class="editor-icon-button" title="Cancel page edit" aria-label="Cancel page edit" x-show="$store.editor.previewEditing" @click="$store.editor.cancelPreviewEdit()">
<span class="material-symbols-outlined">close</span>
</button>
<button type="button" class="editor-icon-button" title="Search" aria-label="Search" :disabled="$store.editor.previewEditing" @click="$store.editor.openSearch()">
<span class="material-symbols-outlined">search</span>
</button>
</div>
<span class="editor-toolbar-spacer"></span>
<button
type="button"
class="editor-icon-button editor-mode-toggle"
:title="$store.editor.viewModeTitle()"
:aria-label="$store.editor.viewModeTitle()"
@click="$store.editor.toggleViewMode()"
>
<span class="material-symbols-outlined" aria-hidden="true" x-text="$store.editor.viewModeIcon()"></span>
</button>
<div class="editor-file-actions" x-data="{ open: false }" @click.outside="open = false" @keydown.escape.window="open = false">
<button
type="button"
class="editor-icon-button editor-file-menu-button"
title="File actions"
aria-label="File actions"
aria-haspopup="menu"
:aria-expanded="open.toString()"
:disabled="$store.editor.saving"
@click.stop="open = !open"
>
<span class="material-symbols-outlined">more_vert</span>
</button>
<div class="editor-new-menu editor-file-menu" role="menu" x-show="open" @click.stop>
<button type="button" class="editor-new-menu-item" :class="{ 'is-emphasized': $store.editor.dirty }" role="menuitem" :disabled="$store.editor.saving" @click="open = false; $store.editor.save()">
<span class="material-symbols-outlined" :class="{ spinning: $store.editor.saving }" x-text="$store.editor.saving ? 'progress_activity' : 'save'"></span>
<span>Save</span>
</button>
<button type="button" class="editor-new-menu-item" role="menuitem" :disabled="$store.editor.saving" @click="open = false; $store.editor.renameActiveFile()">
<span class="material-symbols-outlined" aria-hidden="true">edit</span>
<span>Rename</span>
</button>
<button type="button" class="editor-new-menu-item" role="menuitem" :disabled="$store.editor.loading" @click="open = false; $store.editor.closeActiveFile()">
<span class="material-symbols-outlined" aria-hidden="true">close</span>
<span>Close File</span>
</button>
<button type="button" class="editor-new-menu-item" role="menuitem" :disabled="$store.editor.loading || $store.editor.visibleTabs().length === 0" @click="open = false; $store.editor.closeAllFiles()">
<span class="material-symbols-outlined" aria-hidden="true">close</span>
<span>Close All</span>
</button>
</div>
</div>
</div>
</div>
<div class="editor-search-bar" x-show="$store.editor.searchOpen" style="display: none;">
<span class="material-symbols-outlined" aria-hidden="true">search</span>
<input
type="search"
data-editor-search
aria-label="Search preview"
x-model="$store.editor.searchQuery"
@input="$store.editor.runSearch()"
@keydown.enter.prevent="$event.shiftKey ? $store.editor.previousSearchMatch() : $store.editor.nextSearchMatch()"
@keydown.escape.prevent="$store.editor.closeSearch()"
/>
<span class="editor-search-count" x-text="$store.editor.searchCountLabel()"></span>
<button type="button" class="editor-icon-button" title="Previous match" aria-label="Previous match" :disabled="$store.editor.searchMatches.length === 0" @click="$store.editor.previousSearchMatch()">
<span class="material-symbols-outlined">keyboard_arrow_up</span>
</button>
<button type="button" class="editor-icon-button" title="Next match" aria-label="Next match" :disabled="$store.editor.searchMatches.length === 0" @click="$store.editor.nextSearchMatch()">
<span class="material-symbols-outlined">keyboard_arrow_down</span>
</button>
<button type="button" class="editor-icon-button" title="Close search" aria-label="Close search" @click="$store.editor.closeSearch()">
<span class="material-symbols-outlined">close</span>
</button>
</div>
<div class="editor-state-line" x-show="$store.editor.message || $store.editor.error || $store.editor.loading" style="display: none;">
<span class="material-symbols-outlined" :class="{ spinning: $store.editor.loading }" x-text="$store.editor.loading ? 'progress_activity' : ($store.editor.error ? 'error' : 'check_circle')"></span>
<span x-text="$store.editor.error || $store.editor.message || 'Working'"></span>
</div>
<div class="editor-body">
<div class="editor-wrap" x-show="$store.editor.session && $store.editor.isSourceMode()" style="display: none;">
<div class="editor-scroll" @click.self="$store.editor.focusEditor()">
<div class="editor-ace" data-editor-ace x-show="!$store.editor.aceUnavailable"></div>
<textarea
class="editor-source-editor"
data-editor-source
aria-label="Markdown source"
x-show="$store.editor.aceUnavailable"
x-model="$store.editor.editorText"
@input="$store.editor.onSourceInput()"
@blur="$store.editor.flushInput()"
spellcheck="true"
style="display: none;"
></textarea>
</div>
</div>
<div class="editor-preview-shell" x-show="$store.editor.session && $store.editor.isPreviewMode()" style="display: none;">
<div class="editor-preview-title">
<h1 x-text="$store.editor.pageTitle()"></h1>
</div>
<div class="editor-preview-edit-shell" x-show="$store.editor.previewEditing" style="display: none;">
<textarea
class="editor-preview-page-editor"
data-editor-preview-source
aria-label="Markdown page source"
x-model="$store.editor.previewEditText"
@input="$store.editor.onPreviewEditInput()"
@keydown.meta.enter.prevent="$store.editor.applyPreviewEdit()"
@keydown.ctrl.enter.prevent="$store.editor.applyPreviewEdit()"
@keydown.escape.prevent="$store.editor.cancelPreviewEdit()"
spellcheck="true"
></textarea>
</div>
<div
class="editor-preview-content msg-content"
data-editor-preview
x-show="!$store.editor.previewEditing"
x-html="$store.editor.previewHtml()"
x-effect="$store.editor.activePageIndex; $store.editor.editorText; $store.editor.searchQuery; $store.editor.searchIndex; $store.editor.schedulePreviewEnhance()"
@click="$store.editor.handlePreviewClick($event)"
></div>
</div>
<div class="editor-empty" x-show="!$store.editor.session && !$store.editor.loading" style="display: none;">
<div class="editor-empty-actions">
<button type="button" class="editor-icon-button editor-command-button" @click="$store.editor.runNewMenuAction('open')">
<span class="material-symbols-outlined" aria-hidden="true">folder_open</span>
<span class="editor-button-label">Open</span>
</button>
<button type="button" class="editor-icon-button editor-command-button" @click="$store.editor.runNewMenuAction('markdown')">
<span class="material-symbols-outlined" aria-hidden="true">article</span>
<span class="editor-button-label">Markdown</span>
</button>
</div>
</div>
</div>
</div>
</div>
</template>
</div>
<style>
.editor-panel,
.editor-shell {
--editor-chrome-surface: color-mix(in srgb, var(--color-background) 92%, #000 8%);
--editor-chrome-border: color-mix(in srgb, var(--color-border) 58%, transparent);
--editor-tab-hover-border: color-mix(in srgb, var(--color-border) 78%, transparent);
--editor-tab-height: 36px;
--editor-tab-close-size: 32px;
--editor-control-radius: 0.55rem;
display: flex;
flex: 1 1 auto;
flex-direction: column;
width: 100%;
height: 100%;
min-width: 0;
min-height: 0;
background: var(--color-background);
color: var(--color-text);
}
.editor-panel {
container-type: inline-size;
}
.modal-inner.editor-modal {
box-sizing: border-box;
width: min(1040px, calc(100vw - 32px));
height: min(760px, calc(100vh - 32px));
min-width: min(640px, calc(100vw - 16px));
min-height: min(460px, calc(100vh - 16px));
max-width: none;
max-height: none;
resize: both;
overflow: hidden;
}
.modal-inner.editor-modal.is-focus-mode {
left: 8px !important;
top: 8px !important;
width: calc(100vw - 16px) !important;
height: calc(100vh - 16px) !important;
transform: none !important;
}
.modal-inner.editor-modal .modal-scroll,
.modal-inner.editor-modal .modal-bd.editor-modal-body,
.modal-inner.editor-modal .modal-bd.editor-modal-body > x-component,
.modal-inner.editor-modal .modal-bd.editor-modal-body > x-component > div[x-data],
.modal-inner.editor-modal .modal-bd.editor-modal-body > x-component > div[x-data] > .editor-panel {
display: flex;
flex: 1 1 auto;
min-width: 0;
min-height: 0;
width: 100%;
height: 100%;
max-height: none;
overflow: hidden;
padding: 0;
}
.modal-inner.editor-modal .modal-header {
grid-template-columns: minmax(0, 1fr) repeat(4, auto);
}
.editor-tabs {
display: flex;
align-items: end;
gap: 4px;
min-height: 44px;
padding: 7px 10px 0;
overflow-x: auto;
overflow-y: hidden;
border-bottom: 1px solid var(--editor-chrome-border);
background: var(--editor-chrome-surface);
scrollbar-width: thin;
}
.editor-tabs::-webkit-scrollbar {
height: 4px;
}
.editor-tabs::-webkit-scrollbar-track {
background: transparent;
}
.editor-tabs::-webkit-scrollbar-thumb {
background: color-mix(in srgb, var(--color-border) 78%, transparent);
border-radius: 999px;
}
.editor-tab-shell {
flex: 0 1 210px;
position: relative;
display: grid;
grid-template-columns: minmax(0, 1fr) var(--editor-tab-close-size);
align-items: center;
gap: 3px;
min-width: 128px;
max-width: 250px;
height: var(--editor-tab-height);
padding: 0 7px 0 10px;
border: 1px solid transparent;
border-radius: var(--editor-control-radius) var(--editor-control-radius) 0 0;
background: transparent;
opacity: 0.72;
transition: border-color 0.18s cubic-bezier(0.4, 0, 0.2, 1),
color 0.18s cubic-bezier(0.4, 0, 0.2, 1),
opacity 0.18s cubic-bezier(0.4, 0, 0.2, 1);
}
.editor-new-tab {
display: inline-flex;
align-items: center;
justify-content: center;
flex: 0 0 34px;
width: 34px;
min-width: 34px;
height: 34px;
min-height: 34px;
padding: 0;
border: 1px solid transparent;
border-radius: var(--editor-control-radius);
background: transparent;
color: color-mix(in srgb, var(--color-text) 62%, var(--color-primary) 38%);
cursor: pointer;
transition: background-color 0.18s cubic-bezier(0.4, 0, 0.2, 1),
border-color 0.18s cubic-bezier(0.4, 0, 0.2, 1),
color 0.18s cubic-bezier(0.4, 0, 0.2, 1),
opacity 0.18s cubic-bezier(0.4, 0, 0.2, 1);
}
.editor-icon-button {
border: 1px solid color-mix(in srgb, var(--color-border), transparent 12%);
border-radius: 8px;
background: color-mix(in srgb, var(--color-panel), var(--color-background) 16%);
color: inherit;
cursor: pointer;
transition: border-color 120ms ease, background 120ms ease;
}
.editor-tab {
display: flex;
align-items: center;
gap: 8px;
min-width: 0;
width: 100%;
height: 100%;
padding: 0;
border: 0;
border-radius: 0;
background: transparent;
color: inherit;
cursor: pointer;
font: inherit;
text-align: left;
}
.editor-tab-close {
display: inline-flex;
align-items: center;
justify-content: center;
width: var(--editor-tab-close-size);
min-width: var(--editor-tab-close-size);
height: var(--editor-tab-close-size);
min-height: var(--editor-tab-close-size);
padding: 0;
border: 0;
border-radius: 6px;
background: transparent;
color: color-mix(in srgb, var(--color-text) 52%, var(--color-primary) 48%);
cursor: pointer;
opacity: 0.72;
transition: background-color 0.18s cubic-bezier(0.4, 0, 0.2, 1),
color 0.18s cubic-bezier(0.4, 0, 0.2, 1),
opacity 0.18s cubic-bezier(0.4, 0, 0.2, 1);
}
.editor-tab-shell:hover,
.editor-tab-shell:focus-within {
border-color: var(--editor-tab-hover-border);
opacity: 0.94;
}
.editor-tab-shell.is-active {
z-index: 2;
margin-bottom: -1px;
border-color: var(--editor-chrome-border);
background: transparent;
opacity: 1;
box-shadow: none;
}
.editor-icon-button.is-primary {
border-color: color-mix(in srgb, #2c7be5, var(--color-border) 36%);
background: color-mix(in srgb, #2c7be5, var(--color-panel) 88%);
}
.editor-tab-shell.is-pending-close {
border-color: color-mix(in srgb, #d98b2b, var(--color-border) 30%);
background: color-mix(in srgb, #d98b2b, var(--color-panel) 88%);
opacity: 1;
}
.editor-tab-shell.is-dirty .editor-tab-title::after {
content: " *";
color: #2ca58d;
}
.editor-tab-title {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.editor-tab-title {
font-size: 0.88rem;
font-weight: 600;
letter-spacing: 0;
}
.editor-toolbar,
.editor-state-line,
.editor-close-confirm,
.editor-search-bar {
display: flex;
align-items: center;
gap: 8px;
border-bottom: 1px solid color-mix(in srgb, var(--color-border), transparent 22%);
padding: 7px 10px;
min-width: 0;
}
.editor-close-confirm {
min-height: 44px;
background: color-mix(in srgb, #d98b2b 9%, var(--color-background));
color: var(--color-text);
}
.editor-close-confirm-icon {
color: #d98b2b;
font-size: 20px;
}
.editor-close-confirm-copy {
display: flex;
flex: 1 1 auto;
flex-direction: column;
min-width: 0;
gap: 2px;
}
.editor-close-confirm-title {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 13px;
font-weight: 800;
letter-spacing: 0;
}
.editor-close-confirm-message {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: var(--color-text-secondary);
font-size: 12px;
letter-spacing: 0;
}
.editor-close-confirm-actions {
display: flex;
flex: 0 0 auto;
align-items: center;
gap: 6px;
}
.editor-toolbar {
min-height: 46px;
padding: 6px 10px;
position: relative;
z-index: 20;
overflow: visible;
background: color-mix(in srgb, var(--color-background), var(--color-panel) 48%);
}
.editor-toolbar-row,
.editor-tool-group {
display: flex;
align-items: center;
gap: 5px;
min-width: 0;
}
.editor-toolbar-row {
width: 100%;
overflow: visible;
}
.editor-toolbar-spacer {
flex: 1 1 16px;
min-width: 8px;
}
.editor-page-count,
.editor-search-count {
min-width: 54px;
color: var(--color-text-secondary);
font-size: 12px;
font-weight: 700;
letter-spacing: 0;
text-align: center;
white-space: nowrap;
}
.editor-search-bar {
min-height: 40px;
background: color-mix(in srgb, var(--color-background), var(--color-panel) 30%);
}
.editor-search-bar input {
flex: 1 1 auto;
min-width: 90px;
height: 30px;
border: 1px solid color-mix(in srgb, var(--color-border), transparent 12%);
border-radius: 7px;
background: var(--color-background);
color: var(--color-text);
padding: 0 9px;
font: inherit;
font-size: 13px;
}
.editor-icon-button {
display: inline-grid;
place-items: center;
width: 32px;
height: 32px;
min-width: 32px;
padding: 0;
}
.editor-icon-button:hover:not(:disabled) {
border-color: color-mix(in srgb, #2c7be5, var(--color-border) 45%);
background: color-mix(in srgb, var(--color-panel), #2c7be5 8%);
}
.editor-tab:hover {
background: transparent;
}
.editor-tab:focus-visible,
.editor-tab-close:focus-visible {
outline: 1px solid color-mix(in srgb, var(--color-primary) 70%, transparent);
outline-offset: 1px;
}
.editor-tab-icon {
flex: 0 0 auto;
color: color-mix(in srgb, var(--color-text) 72%, var(--color-primary) 28%);
font-size: 1.04rem;
line-height: 1;
}
.editor-tab-close:hover {
background: color-mix(in srgb, var(--color-background-hover) 70%, transparent);
color: var(--color-text);
opacity: 1;
}
.editor-new-tab:hover:not(:disabled) {
background: color-mix(in srgb, var(--color-background-hover) 58%, transparent);
border-color: color-mix(in srgb, var(--color-primary) 24%, transparent);
color: var(--color-text);
}
.editor-new-tab:disabled {
cursor: default;
opacity: 0.42;
}
.editor-tab-close .material-symbols-outlined {
font-size: 1rem;
line-height: 1;
}
.editor-new-tab .material-symbols-outlined {
font-size: 1.12rem;
line-height: 1;
}
.editor-icon-button:disabled {
cursor: default;
opacity: 0.42;
}
.editor-file-actions,
.editor-header-actions {
position: relative;
display: inline-flex;
align-items: center;
flex: 0 0 auto;
z-index: 30;
}
.editor-new-menu {
position: absolute;
top: calc(100% + 6px);
right: 0;
z-index: 10000;
min-width: 184px;
padding: 5px;
border: 1px solid color-mix(in srgb, var(--color-border), transparent 10%);
border-radius: 8px;
background: color-mix(in srgb, var(--color-panel), var(--color-background) 10%);
box-shadow: 0 14px 34px rgba(0, 0, 0, 0.34);
}
.editor-new-menu[hidden] {
display: none;
}
.editor-new-menu-item,
.editor-header-new-button {
appearance: none;
display: flex;
align-items: center;
gap: 8px;
border: 1px solid transparent;
background: transparent;
color: var(--color-text);
cursor: pointer;
font: inherit;
font-size: 12px;
font-weight: 700;
letter-spacing: 0;
line-height: 1;
white-space: nowrap;
}
.editor-header-new-button {
justify-content: center;
height: 34px;
padding: 0 9px 0 8px;
border-radius: 7px;
opacity: 0.82;
}
.editor-new-menu-item {
width: 100%;
height: 32px;
padding: 0 8px;
border-radius: 6px;
text-align: left;
}
.editor-new-menu-item.is-emphasized {
color: var(--color-text);
background: color-mix(in srgb, #2c7be5 10%, transparent);
}
.editor-text-button {
appearance: none;
height: 28px;
padding: 0 9px;
border: 1px solid color-mix(in srgb, var(--color-border), transparent 10%);
border-radius: 7px;
background: color-mix(in srgb, var(--color-panel), var(--color-background) 18%);
color: var(--color-text);
cursor: pointer;
font: inherit;
font-size: 12px;
font-weight: 750;
letter-spacing: 0;
white-space: nowrap;
}
.editor-text-button.is-primary {
border-color: color-mix(in srgb, #2c7be5, var(--color-border) 35%);
background: color-mix(in srgb, #2c7be5, var(--color-panel) 82%);
}
.editor-header-new-button:hover,
.editor-header-actions.is-open .editor-header-new-button,
.editor-new-menu-item:hover,
.editor-text-button:hover:not(:disabled) {
opacity: 1;
border-color: color-mix(in srgb, var(--color-primary) 24%, transparent);
background: color-mix(in srgb, var(--color-background-hover) 76%, transparent);
}
.editor-text-button:disabled {
cursor: default;
opacity: 0.45;
}
@container (max-width: 560px) {
.editor-close-confirm {
align-items: stretch;
flex-direction: column;
}
.editor-close-confirm-actions {
flex-wrap: wrap;
}
}
.editor-body,
.editor-wrap,
.editor-scroll,
.editor-preview-shell {
display: flex;
flex: 1 1 auto;
min-width: 0;
min-height: 0;
}
.editor-body {
position: relative;
flex-direction: column;
overflow: hidden;
}
.editor-wrap,
.editor-scroll {
width: 100%;
height: 100%;
}
.editor-scroll {
position: relative;
}
.editor-ace {
flex: 1 1 auto;
width: 100%;
height: 100%;
min-width: 0;
min-height: 0;
font: 13px/1.55 ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
}
.editor-ace .ace_gutter {
display: none !important;
}
.editor-ace .ace_scroller {
left: 0 !important;
}
.editor-source-editor {
box-sizing: border-box;
width: 100%;
height: 100%;
min-width: 0;
min-height: 0;
resize: none;
border: 0;
outline: none;
padding: 18px 20px;
background: var(--color-background);
color: var(--color-text);
font: 13px/1.55 ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
letter-spacing: 0;
}
.editor-preview-shell {
flex-direction: column;
overflow: hidden;
background: var(--color-background);
}
.editor-preview-title {
flex: 0 0 auto;
padding: 20px 24px 10px;
border-bottom: 1px solid color-mix(in srgb, var(--color-border), transparent 30%);
}
.editor-preview-title h1 {
margin: 0;
color: var(--color-text);
font-size: 24px;
line-height: 1.2;
letter-spacing: 0;
}
.editor-preview-content {
flex: 1 1 auto;
min-width: 0;
min-height: 0;
overflow: auto;
padding: 20px 24px 36px;
color: var(--color-text);
line-height: 1.58;
}
.editor-preview-edit-shell {
display: flex;
flex: 1 1 auto;
min-width: 0;
min-height: 0;
padding: 16px 24px 36px;
background: var(--color-background);
}
.editor-preview-page-editor {
box-sizing: border-box;
flex: 1 1 auto;
width: 100%;
min-width: 0;
min-height: 0;
resize: none;
border: 1px solid color-mix(in srgb, var(--color-border), transparent 12%);
border-radius: 8px;
outline: none;
padding: 14px 16px;
background: color-mix(in srgb, var(--color-panel), var(--color-background) 28%);
color: var(--color-text);
font: 13px/1.55 ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
letter-spacing: 0;
}
.editor-preview-page-editor:focus {
border-color: color-mix(in srgb, #2c7be5, var(--color-border) 40%);
box-shadow: 0 0 0 1px color-mix(in srgb, #2c7be5 24%, transparent);
}
.editor-preview-content img {
display: block;
max-width: 100%;
height: auto;
margin: 12px 0;
}
.editor-preview-content h1,
.editor-preview-content h2,
.editor-preview-content h3,
.editor-preview-content h4,
.editor-preview-content h5,
.editor-preview-content h6 {
scroll-margin-top: 16px;
letter-spacing: 0;
}
.editor-preview-content blockquote {
margin: 12px 0;
padding: 1px 0 1px 14px;
border-left: 3px solid color-mix(in srgb, var(--color-primary), var(--color-border) 45%);
color: var(--color-text-secondary);
}
.editor-table-wrap {
max-width: 100%;
overflow-x: auto;
margin: 14px 0;
border: 1px solid color-mix(in srgb, var(--color-border), transparent 12%);
border-radius: 8px;
}
.editor-preview-content table {
width: 100%;
border-collapse: collapse;
min-width: 420px;
}
.editor-preview-content th,
.editor-preview-content td {
border-bottom: 1px solid color-mix(in srgb, var(--color-border), transparent 22%);
padding: 8px 10px;
text-align: inherit;
vertical-align: top;
}
.editor-preview-content th {
background: color-mix(in srgb, var(--color-panel), var(--color-background) 24%);
font-weight: 800;
}
.editor-preview-content input[type="checkbox"] {
margin-right: 6px;
cursor: pointer;
}
.editor-code-block {
overflow: hidden;
margin: 14px 0;
border: 1px solid color-mix(in srgb, var(--color-border), transparent 12%);
border-radius: 8px;
background: color-mix(in srgb, var(--color-panel), var(--color-background) 16%);
}
.editor-code-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
min-height: 32px;
padding: 0 8px 0 10px;
border-bottom: 1px solid color-mix(in srgb, var(--color-border), transparent 22%);
color: var(--color-text-secondary);
font-size: 12px;
font-weight: 700;
}
.editor-code-copy {
height: 24px;
border: 1px solid color-mix(in srgb, var(--color-border), transparent 12%);
border-radius: 6px;
background: var(--color-background);
color: var(--color-text);
cursor: pointer;
font: inherit;
font-size: 11px;
font-weight: 750;
}
.editor-code-block pre {
margin: 0;
overflow: auto;
padding: 12px;
}
.editor-code-block code {
font: 12px/1.55 ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
}
.editor-math-display {
margin: 14px 0;
overflow-x: auto;
text-align: center;
}
.editor-footnotes {
margin-top: 24px;
border-top: 1px solid color-mix(in srgb, var(--color-border), transparent 22%);
color: var(--color-text-secondary);
font-size: 13px;
}
mark.editor-search-mark {
border-radius: 3px;
background: color-mix(in srgb, #d9b12b 55%, transparent);
color: inherit;
padding: 0 1px;
}
mark.editor-search-mark.is-current {
background: color-mix(in srgb, #2c7be5 55%, transparent);
outline: 1px solid color-mix(in srgb, #2c7be5, var(--color-border) 24%);
}
.editor-empty {
display: grid;
flex: 1 1 auto;
place-items: center;
align-content: center;
gap: 12px;
color: color-mix(in srgb, var(--color-text) 70%, transparent);
}
.editor-empty-actions {
display: flex;
gap: 8px;
flex-wrap: wrap;
justify-content: center;
}
.editor-command-button {
display: inline-flex;
width: auto;
max-width: 140px;
padding: 0 9px;
gap: 6px;
}
.editor-button-label {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 11px;
font-weight: 700;
}
.editor-state-line {
min-height: 34px;
font-size: 12px;
color: color-mix(in srgb, var(--color-text) 80%, transparent);
}
</style>
</body>
</html>