visual-theme.ts 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. import { EditorView } from '@codemirror/view'
  2. import { HighlightStyle, syntaxHighlighting } from '@codemirror/language'
  3. import { tags } from '@lezer/highlight'
  4. export const visualHighlightStyle = syntaxHighlighting(
  5. HighlightStyle.define([
  6. { tag: tags.link, class: 'ol-cm-link-text' },
  7. { tag: tags.url, class: 'ol-cm-url' },
  8. { tag: tags.typeName, class: 'ol-cm-monospace' },
  9. { tag: tags.attributeValue, class: 'ol-cm-monospace' },
  10. { tag: tags.keyword, class: 'ol-cm-monospace' },
  11. { tag: tags.string, class: 'ol-cm-monospace' },
  12. { tag: tags.punctuation, class: 'ol-cm-punctuation' },
  13. { tag: tags.literal, class: 'ol-cm-monospace' },
  14. {
  15. tag: tags.monospace,
  16. fontFamily: 'var(--source-font-family)',
  17. lineHeight: 1,
  18. overflowWrap: 'break-word',
  19. },
  20. ])
  21. )
  22. export const visualTheme = EditorView.theme({
  23. '&.cm-editor': {
  24. '--visual-font-family':
  25. "'Noto Serif', 'Palatino Linotype', 'Book Antiqua', Palatino, serif !important",
  26. '--visual-font-size': 'calc(var(--font-size) * 1.15)',
  27. '& .cm-content': {
  28. opacity: 0,
  29. },
  30. '&.ol-cm-parsed .cm-content': {
  31. opacity: 1,
  32. transition: 'opacity 0.1s ease-out',
  33. },
  34. },
  35. '.cm-content.cm-content': {
  36. overflowX: 'hidden', // needed so the callout elements don't overflow (requires line wrapping to be on)
  37. padding: '0 max(calc((100% - 100ch) / 2), 8%)', // max 100 characters per line
  38. fontFamily: 'var(--visual-font-family)',
  39. fontSize: 'var(--visual-font-size)',
  40. },
  41. '.cm-cursor-primary.cm-cursor-primary': {
  42. fontFamily: 'var(--visual-font-family)',
  43. fontSize: 'var(--visual-font-size)',
  44. },
  45. '.cm-line': {
  46. overflowX: 'visible', // needed so the callout elements can overflow when the content has padding
  47. },
  48. '.cm-gutter': {
  49. opacity: '0.5',
  50. },
  51. '.cm-tooltip': {
  52. fontSize: 'calc(var(--font-size) * 1.15) !important',
  53. },
  54. '.ol-cm-link-text': {
  55. textDecoration: 'underline',
  56. fontFamily: 'inherit',
  57. },
  58. '.ol-cm-monospace': {
  59. fontFamily: 'var(--source-font-family)',
  60. lineHeight: 1,
  61. fontWeight: 'normal',
  62. fontStyle: 'normal',
  63. fontVariant: 'normal',
  64. textDecoration: 'none',
  65. },
  66. '.ol-cm-punctuation': {
  67. fontFamily: 'var(--source-font-family)',
  68. lineHeight: 1,
  69. },
  70. '.ol-cm-brace': {
  71. opacity: '0.5',
  72. },
  73. '.ol-cm-math': {
  74. overflow: 'hidden', // stop the margin from the inner math element affecting the block height
  75. },
  76. '.ol-cm-maketitle': {
  77. textAlign: 'center',
  78. paddingBottom: '2em',
  79. },
  80. '.ol-cm-title': {
  81. fontSize: '1.7em',
  82. cursor: 'pointer',
  83. padding: '0.5em',
  84. lineHeight: 'calc(var(--line-height) * 5/6)',
  85. },
  86. '.ol-cm-author': {
  87. display: 'inline-block',
  88. maxWidth: '45%',
  89. minWidth: '200px',
  90. verticalAlign: 'top',
  91. cursor: 'pointer',
  92. },
  93. '.ol-cm-author:not(:first-child)': {
  94. display: 'inline-block',
  95. marginLeft: '5%',
  96. maxWidth: '45%',
  97. },
  98. '.ol-cm-icon-brace': {
  99. filter: 'grayscale(1)',
  100. marginRight: '2px',
  101. },
  102. '.ol-cm-begin': {
  103. fontFamily: 'var(--source-font-family)',
  104. minHeight: '1em',
  105. textAlign: 'center',
  106. display: 'flex',
  107. alignItems: 'center',
  108. justifyContent: 'center',
  109. },
  110. '.ol-cm-end': {
  111. fontFamily: 'var(--source-font-family)',
  112. padding: '0.5em 0 1.5em',
  113. minHeight: '1em',
  114. textAlign: 'center',
  115. justifyContent: 'center',
  116. background: `linear-gradient(180deg, rgba(0,0,0,0) calc(50% - 1px), rgba(192,192,192,1) calc(50%), rgba(0,0,0,0) calc(50% + 1px))`,
  117. },
  118. '.ol-cm-environment-top': {
  119. paddingTop: '1em',
  120. },
  121. '.ol-cm-environment-bottom': {
  122. paddingBottom: '1em',
  123. },
  124. '.ol-cm-environment-first-line': {
  125. paddingTop: '0.5em !important',
  126. borderTopLeftRadius: '8px',
  127. borderTopRightRadius: '8px',
  128. },
  129. '.ol-cm-environment-last-line': {
  130. paddingBottom: '1em !important',
  131. borderBottomLeftRadius: '8px',
  132. borderBottomRightRadius: '8px',
  133. },
  134. '.ol-cm-environment-figure.ol-cm-environment-line, .ol-cm-environment-table.ol-cm-environment-line':
  135. {
  136. backgroundColor: 'rgba(125, 125, 125, 0.05)',
  137. padding: '0 12px',
  138. },
  139. '.ol-cm-environment-figure.ol-cm-environment-last-line, .ol-cm-environment-table.ol-cm-environment-last-line, .ol-cm-preamble-line.ol-cm-environment-last-line':
  140. {
  141. boxShadow: '0 2px 5px -3px rgb(125, 125, 125, 0.5)',
  142. },
  143. '.ol-cm-environment-padding': {
  144. flex: 1,
  145. height: '1px',
  146. background: `linear-gradient(180deg, rgba(0,0,0,0) calc(50% - 1px), rgba(192,192,192,1) calc(50%), rgba(0,0,0,0) calc(50% + 1px))`,
  147. },
  148. '.ol-cm-environment-name': {
  149. padding: '0 1em',
  150. },
  151. '.ol-cm-environment-name-abstract': {
  152. fontFamily: 'var(--visual-font-family)',
  153. fontSize: '1.2em',
  154. fontWeight: 550,
  155. },
  156. '.ol-cm-environment-name-abstract:first-letter': {
  157. textTransform: 'uppercase',
  158. },
  159. '.ol-cm-item': {
  160. paddingInlineStart: 'calc(var(--list-depth) * 2ch)',
  161. },
  162. '.ol-cm-item::before': {
  163. counterReset: 'list-item var(--list-ordinal)',
  164. content: 'counter(list-item, var(--list-type)) var(--list-suffix)',
  165. },
  166. '.ol-cm-heading': {
  167. fontWeight: 550,
  168. lineHeight: '1.35',
  169. color: 'inherit !important',
  170. background: 'inherit !important',
  171. },
  172. '.ol-cm-command-part': {
  173. fontSize: '2em',
  174. },
  175. '.ol-cm-command-chapter': {
  176. fontSize: '1.6em',
  177. },
  178. '.ol-cm-command-section': {
  179. fontSize: '1.44em',
  180. },
  181. '.ol-cm-command-subsection': {
  182. fontSize: '1.2em',
  183. },
  184. '.ol-cm-command-subsubsection': {
  185. fontSize: '1em',
  186. },
  187. '.ol-cm-command-paragraph': {
  188. fontSize: '1em',
  189. },
  190. '.ol-cm-command-subparagraph': {
  191. fontSize: '1em',
  192. },
  193. '.ol-cm-frame-title': {
  194. fontSize: '1.44em',
  195. },
  196. '.ol-cm-frame-subtitle': {
  197. fontSize: '1em',
  198. },
  199. '.ol-cm-divider': {
  200. borderBottom: '1px solid rgba(125, 125, 125, 0.1)',
  201. padding: '0.5em 6px',
  202. '&.ol-cm-frame-widget': {
  203. borderBottom: 'none',
  204. borderTop: '1px solid rgba(125, 125, 125, 0.1)',
  205. },
  206. },
  207. '.ol-cm-command-textbf': {
  208. fontWeight: 700,
  209. },
  210. '.ol-cm-command-textit': {
  211. fontStyle: 'italic',
  212. },
  213. '.ol-cm-command-textsc': {
  214. fontVariant: 'small-caps',
  215. },
  216. '.ol-cm-command-texttt': {
  217. fontFamily: 'monospace',
  218. },
  219. '.ol-cm-command-underline': {
  220. textDecoration: 'underline',
  221. },
  222. '.ol-cm-command-sout': {
  223. textDecoration: 'line-through',
  224. },
  225. '.ol-cm-command-emph': {
  226. fontStyle: 'italic',
  227. '& .ol-cm-command-textit': {
  228. fontStyle: 'normal',
  229. },
  230. '.ol-cm-command-textit &': {
  231. fontStyle: 'normal',
  232. },
  233. },
  234. '.ol-cm-command-url': {
  235. textDecoration: 'underline',
  236. // copied from tags.monospace
  237. fontFamily: 'var(--source-font-family)',
  238. lineHeight: 1,
  239. overflowWrap: 'break-word',
  240. hyphens: 'auto',
  241. },
  242. '.ol-cm-environment-centered.ol-cm-caption-line': {
  243. padding: '0 10%',
  244. textAlign: 'center',
  245. },
  246. '.ol-cm-caption-line .ol-cm-label': {
  247. marginRight: '1ch',
  248. },
  249. '.ol-cm-tex': {
  250. textTransform: 'uppercase',
  251. '& sup': {
  252. position: 'inherit',
  253. fontSize: '0.85em',
  254. verticalAlign: '0.15em',
  255. marginLeft: '-0.36em',
  256. marginRight: '-0.15em',
  257. },
  258. '& sub': {
  259. position: 'inherit',
  260. fontSize: '1em',
  261. verticalAlign: '-0.5ex',
  262. marginLeft: '-0.1667em',
  263. marginRight: '-0.125em',
  264. },
  265. },
  266. '.ol-cm-graphics': {
  267. display: 'block',
  268. maxWidth: 'min(300px, 100%)',
  269. paddingTop: '1em',
  270. paddingBottom: '1em',
  271. cursor: 'pointer',
  272. '.ol-cm-graphics-inline &': {
  273. display: 'inline',
  274. },
  275. },
  276. '.ol-cm-graphics-loading': {
  277. height: '300px', // guess that the height is the same as the max width
  278. },
  279. '.ol-cm-graphics-error': {
  280. border: '1px solid red',
  281. padding: '8px',
  282. },
  283. '.ol-cm-environment-centered .ol-cm-graphics': {
  284. margin: '0 auto',
  285. },
  286. '.ol-cm-command-verb .ol-cm-monospace': {
  287. color: 'inherit', // remove syntax highlighting colour from verbatim content
  288. },
  289. '.ol-cm-preamble-wrapper': {
  290. padding: '0.5em 0',
  291. '&.ol-cm-preamble-expanded': {
  292. paddingBottom: '0',
  293. },
  294. },
  295. '.ol-cm-preamble-widget, .ol-cm-end-document-widget': {
  296. padding: '0.25em 1em',
  297. borderRadius: '8px',
  298. fontFamily: '"Lato", sans-serif',
  299. fontSize: '14px',
  300. '.ol-cm-preamble-expanded &': {
  301. borderBottomLeftRadius: '0',
  302. borderBottomRightRadius: '0',
  303. borderBottom: '1px solid rgba(125, 125, 125, 0.2)',
  304. },
  305. },
  306. '.ol-cm-preamble-widget': {
  307. cursor: 'pointer',
  308. display: 'flex',
  309. justifyContent: 'space-between',
  310. alignItems: 'center',
  311. },
  312. '.ol-cm-preamble-expand-icon': {
  313. width: '32px',
  314. lineHeight: '32px',
  315. textAlign: 'center',
  316. transition: '0.2s ease-out',
  317. opacity: '0.5',
  318. '.ol-cm-preamble-widget:hover &': {
  319. opacity: '1',
  320. },
  321. '.ol-cm-preamble-expanded &': {
  322. transform: 'rotate(180deg)',
  323. },
  324. },
  325. '.ol-cm-preamble-line, .ol-cm-end-document-widget, .ol-cm-preamble-widget': {
  326. backgroundColor: 'rgba(125, 125, 125, 0.05)',
  327. },
  328. '.ol-cm-preamble-line': {
  329. padding: '0 12px',
  330. '&.ol-cm-environment-first-line': {
  331. borderRadius: '0',
  332. },
  333. },
  334. '.ol-cm-end-document-widget': {
  335. textAlign: 'center',
  336. },
  337. })