Files
edgeever/apps/ios/EditorSource/src/styles.css
T
tianma-if 676ae9c530 fix(editor): style default-theme external hyperlinks
External links were indistinguishable from body text under the default
theme because Tailwind preflight resets anchors. Add brand-green
underline styles for ProseMirror (Web, mobile web, Android, iOS) while
leaving note/attachment chips unchanged. Closes the styling half of #193;
Notion-style link cards remain deferred per #192.
2026-08-07 23:15:17 +08:00

457 lines
10 KiB
CSS

:root {
color-scheme: light dark;
--accent: #19ac73;
--text: #0f172a;
--muted: #64748b;
--border: #f1f5f9;
--toolbar-bg: #ffffff;
--toolbar-fg: #64748b;
--toolbar-active-bg: #ecfdf5;
--toolbar-active-border: #bbf7d0;
--toolbar-active-fg: #047857;
--code-bg: color-mix(in srgb, CanvasText 7%, transparent);
--placeholder: #94a3b8;
}
html[data-theme="dark"] {
--text: #f8fafc;
--muted: #cbd5e1;
--border: #334155;
--toolbar-bg: #0f172a;
--toolbar-fg: #cbd5e1;
--toolbar-active-bg: #14532d;
--toolbar-active-border: #166534;
--toolbar-active-fg: #86efac;
--placeholder: #64748b;
}
/* Android LocalTiptapEditor shell: column flex, toolbar fixed top, body scrolls. */
html,
body {
width: 100%;
max-width: 100%;
min-width: 0;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
background: #fff;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
color: var(--text);
-webkit-text-size-adjust: 100%;
}
html[data-theme="dark"],
html[data-theme="dark"] body {
background: #0f172a;
}
/* Full-height column; never let sticky/fixed fight the keyboard visual viewport. */
#shell {
display: flex;
flex-direction: column;
width: 100%;
max-width: 100%;
min-width: 0;
height: 100%;
min-height: 100%;
overflow: hidden;
background: inherit;
}
/*
* Formatting toolbar — always pinned under the native meta row / top of the WebView.
* Do NOT use position:sticky/fixed here: WKWebView + software keyboard reparents the
* visual viewport and sticky toolbars jump to the bottom-right (what the user hit).
*/
#toolbar {
display: none;
flex: 0 0 auto;
flex-direction: row;
align-items: center;
gap: 4px;
width: 100%;
max-width: 100%;
min-height: 38px;
box-sizing: border-box;
padding: 6px 12px;
border-bottom: 1px solid var(--border);
background: var(--toolbar-bg);
overflow-x: auto;
overflow-y: hidden;
-webkit-overflow-scrolling: touch;
scrollbar-width: none;
position: relative;
z-index: 5;
}
#toolbar::-webkit-scrollbar {
display: none;
}
#toolbar.editor-mode {
display: flex;
}
#toolbar button {
display: inline-flex;
flex: 0 0 auto;
align-items: center;
justify-content: center;
width: 36px;
min-height: 32px;
margin: 0;
padding: 0;
border: 1px solid transparent;
border-radius: 999px;
background: transparent;
font-size: 14px;
font-weight: 600;
line-height: 1;
color: var(--toolbar-fg);
}
#toolbar button:active,
#toolbar button.is-active {
border-color: var(--toolbar-active-border);
background: var(--toolbar-active-bg);
color: var(--toolbar-active-fg);
}
/* Scrollable editor body — only this region pans, toolbar stays put. */
#editor {
flex: 1 1 auto;
min-height: 0;
min-width: 0;
width: 100%;
max-width: 100%;
overflow-x: hidden;
overflow-y: auto;
overscroll-behavior: contain;
-webkit-overflow-scrolling: touch;
}
/* TipTap mounts .ProseMirror inside #editor */
.edgeever-prose,
.tiptap,
.ProseMirror {
outline: none;
box-sizing: border-box;
width: 100%;
max-width: 100%;
min-width: 0;
min-height: 100%;
padding: 12px 16px 48px;
line-height: 1.55;
word-break: break-word;
caret-color: #0f766e;
}
.edgeever-prose p.is-editor-empty:first-child::before,
.ProseMirror p.is-editor-empty:first-child::before {
color: var(--placeholder);
content: attr(data-placeholder);
float: left;
height: 0;
pointer-events: none;
}
.edgeever-prose img,
.ProseMirror img {
max-width: 100%;
height: auto;
border-radius: 8px;
display: block;
margin: 12px 0;
}
/* Android-parity image node: figure + ⋯ actions + size presets.
* Width % is applied on the figure so the layout box shrinks with the image.
* Do not leave the figure at 100% while only scaling the <img> — that leaves
* an empty full-width slot (the pale selection wash users see). */
.edgeever-image-node {
position: relative;
display: block;
box-sizing: border-box;
width: var(--ee-image-width, 100%);
max-width: 100%;
margin: 14px auto;
line-height: 0;
/* Prevent WebKit contenteditable from painting a full-line selection wash */
-webkit-user-select: none;
user-select: none;
background: transparent;
}
.edgeever-image-node.has-width {
/* Reinforced by inline style; class helps when attrs rehydrate */
max-width: min(100%, var(--ee-image-width, 100%));
}
.edgeever-image-node.is-loading,
.edgeever-image-node.is-failed {
min-height: 120px;
overflow: hidden;
border-radius: 10px;
background: #f1f5f9;
}
.edgeever-image-loading {
display: flex;
min-height: 120px;
align-items: center;
justify-content: center;
gap: 10px;
padding: 16px;
color: #64748b;
font-size: 13px;
font-weight: 600;
line-height: 1.3;
}
.edgeever-image-loading[hidden] {
display: none;
}
.edgeever-image-loading-label {
text-align: center;
}
.edgeever-image-node > img {
display: block;
width: 100%;
max-width: 100%;
height: auto;
margin: 0 !important;
border-radius: 10px;
vertical-align: top;
}
.edgeever-image-node > img[hidden] {
display: none;
}
/* Outline the figure box (matches actual occupied width), not a full-width wash */
.edgeever-image-node.is-selected,
.edgeever-image-node.ProseMirror-selectednode {
outline: 2px solid #0f766e;
outline-offset: 3px;
background: transparent !important;
box-shadow: none !important;
}
.edgeever-image-node.is-selected > img,
.edgeever-image-node.ProseMirror-selectednode > img {
outline: none;
}
/* Kill native selection fill that WebKit paints across the whole content line */
.ProseMirror-selectednode {
background: transparent !important;
}
.edgeever-image-node::selection,
.edgeever-image-node *::selection {
background: transparent;
}
.edgeever-image-actions {
position: absolute;
right: 8px;
bottom: 8px;
z-index: 5;
display: inline-flex;
width: 42px;
height: 42px;
appearance: none;
-webkit-appearance: none;
align-items: center;
justify-content: center;
border: 1px solid #cbd5e1;
border-radius: 999px;
background: rgba(255, 255, 255, 0.92);
color: #334155;
font-size: 24px;
font-weight: 700;
line-height: 1;
box-shadow: 0 3px 12px rgba(15, 23, 42, 0.2);
padding: 0;
pointer-events: auto;
touch-action: manipulation;
-webkit-user-select: none;
user-select: none;
-webkit-touch-callout: none;
}
.edgeever-image-actions[hidden] {
display: none;
}
.edgeever-image-size-controls {
position: absolute;
top: 8px;
left: 50%;
z-index: 5;
display: flex;
width: max-content;
max-width: calc(100% - 16px);
align-items: center;
gap: 3px;
transform: translateX(-50%);
border: 1px solid #bbf7d0;
border-radius: 9px;
padding: 4px;
background: rgba(255, 255, 255, 0.96);
box-shadow: 0 8px 24px rgba(15, 23, 42, 0.2);
line-height: 1.15;
pointer-events: auto;
touch-action: manipulation;
-webkit-user-select: none;
user-select: none;
}
.edgeever-image-size-controls[hidden] {
display: none !important;
}
.edgeever-image-size-button {
display: inline-flex;
min-width: 52px;
min-height: 38px;
appearance: none;
-webkit-appearance: none;
align-items: center;
justify-content: center;
border: 0;
border-radius: 7px;
padding: 4px 7px;
background: transparent;
color: #475569;
font: inherit;
font-size: 12px;
font-weight: 700;
pointer-events: auto;
touch-action: manipulation;
-webkit-user-select: none;
user-select: none;
-webkit-touch-callout: none;
cursor: pointer;
}
.edgeever-image-size-button.is-active {
background: #ccfbf1;
color: #0f766e;
}
.edgeever-image-upload-spinner {
width: 18px;
height: 18px;
border: 2px solid #cbd5e1;
border-top-color: #0f766e;
border-radius: 999px;
animation: edgeever-image-upload-spin 0.8s linear infinite;
}
@keyframes edgeever-image-upload-spin {
to {
transform: rotate(360deg);
}
}
html[data-theme="dark"] .edgeever-image-node.is-loading,
html[data-theme="dark"] .edgeever-image-node.is-failed {
background: #1e293b;
}
html[data-theme="dark"] .edgeever-image-actions {
border-color: #475569;
background: rgba(15, 23, 42, 0.9);
color: #e2e8f0;
}
html[data-theme="dark"] .edgeever-image-size-controls {
border-color: #475569;
background: rgba(15, 23, 42, 0.96);
}
html[data-theme="dark"] .edgeever-image-size-button {
color: #cbd5e1;
}
html[data-theme="dark"] .edgeever-image-size-button.is-active {
background: #134e4a;
color: #99f6e4;
}
.edgeever-prose pre,
.ProseMirror pre {
background: var(--code-bg);
padding: 10px 12px;
border-radius: 8px;
overflow-x: auto;
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
font-size: 0.9em;
}
/* External hyperlinks — match Web/Android default styling. */
.edgeever-prose a,
.ProseMirror a {
color: #00751f;
font-weight: 500;
text-decoration: underline;
text-decoration-color: rgba(0, 117, 31, 0.45);
text-underline-offset: 2px;
cursor: pointer;
}
.edgeever-prose a:active,
.ProseMirror a:active {
color: #00a82d;
text-decoration-color: #00a82d;
}
html[data-theme="dark"] .edgeever-prose a,
html[data-theme="dark"] .ProseMirror a {
color: #86efac;
text-decoration-color: rgba(134, 239, 172, 0.45);
}
html[data-theme="dark"] .edgeever-prose a:active,
html[data-theme="dark"] .ProseMirror a:active {
color: #4ade80;
text-decoration-color: #4ade80;
}
.edgeever-prose a.edgeever-attachment-link,
.ProseMirror a.edgeever-attachment-link,
.edgeever-prose a[href*="/api/v1/resources/"],
.ProseMirror a[href*="/api/v1/resources/"] {
text-decoration: none;
}
.edgeever-prose blockquote,
.ProseMirror blockquote {
margin: 0.5em 0;
padding-left: 12px;
border-left: 3px solid var(--accent);
color: var(--muted);
}
.edgeever-prose table,
.ProseMirror table {
border-collapse: collapse;
width: 100%;
margin: 8px 0;
}
.edgeever-prose th,
.edgeever-prose td,
.ProseMirror th,
.ProseMirror td {
border: 1px solid var(--border);
padding: 6px 8px;
}
.edgeever-prose h1,
.edgeever-prose h2,
.edgeever-prose h3,
.ProseMirror h1,
.ProseMirror h2,
.ProseMirror h3 {
line-height: 1.25;
}
.edgeever-mermaid {
overflow-x: auto;
margin: 12px 0;
padding: 8px;
border-radius: 8px;
background: var(--code-bg);
}
.edgeever-mermaid svg {
max-width: 100%;
height: auto;
}
/* Viewer: no toolbar band, full-area scroll still via #editor */
body.viewer-mode #toolbar {
display: none !important;
}