diff --git a/.babelrc b/.babelrc deleted file mode 100644 index e8c16d89c4..0000000000 --- a/.babelrc +++ /dev/null @@ -1,49 +0,0 @@ -{ - "comments": false, - "env": { - "test": { - "presets": [ - ["env", { - "targets": { "node": 8 } - }], - "stage-0" - ], - "plugins": ["istanbul"] - }, - "main": { - "presets": [ - ["env", { - "targets": { "node": 8 } - }], - "stage-0" - ] - }, - "renderer": { - "presets": [ - ["env", { - "modules": false, - "useBuiltIns": true, - "targets": { - "browsers": [ - "Chrome >= 66" - ] - } - }], - "stage-0" - ] - }, - "web": { - "presets": [ - ["env", { - "modules": false - }], - "stage-0" - ] - } - }, - "plugins": [["component", { - "style": false, - "libraryName": "element-ui" - } - ], "transform-runtime"] -} diff --git a/.editorconfig b/.editorconfig index 9f89e70533..3dce4145ff 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,10 +1,9 @@ root = true -# Unix-style newlines with a newline ending every file [*] charset = utf-8 -trim_trailing_whitespace = true -end_of_line = lf -insert_final_newline = true indent_style = space indent_size = 2 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true \ No newline at end of file diff --git a/.electron-vue/build.js b/.electron-vue/build.js deleted file mode 100644 index 7cb7f26cc1..0000000000 --- a/.electron-vue/build.js +++ /dev/null @@ -1,121 +0,0 @@ -'use strict' - -process.env.NODE_ENV = 'production' - -const { say } = require('cfonts') -const path = require('path') -const chalk = require('chalk') -const del = require('del') -const fs = require('fs-extra') -const webpack = require('webpack') -const Multispinner = require('multispinner') - - -const mainConfig = require('./webpack.main.config') -const rendererConfig = require('./webpack.renderer.config') - -const doneLog = chalk.bgGreen.white(' DONE ') + ' ' -const errorLog = chalk.bgRed.white(' ERROR ') + ' ' -const okayLog = chalk.bgBlue.white(' OKAY ') + ' ' -const isCI = process.env.CI || false - -if (process.env.BUILD_TARGET === 'clean') clean() -else if (process.env.BUILD_TARGET === 'web') web() -else build() - -function clean () { - del.sync(['build/*', '!build/icons', '!build/icons/icon.*']) - console.log(`\n${doneLog}\n`) - process.exit() -} - -async function build () { - greeting() - - del.sync(['dist/electron/*', '!.gitkeep']) - del.sync(['static/themes/*']) - - const from = path.resolve(__dirname, '../src/muya/themes') - const to = path.resolve(__dirname, '../static/themes') - await fs.copy(from, to) - - const tasks = ['main', 'renderer'] - const m = new Multispinner(tasks, { - preText: 'building', - postText: 'process' - }) - - let results = '' - - m.on('success', () => { - process.stdout.write('\x1B[2J\x1B[0f') - console.log(`\n\n${results}`) - console.log(`${okayLog}take it away ${chalk.yellow('`electron-builder`')}\n`) - process.exit() - }) - - pack(mainConfig).then(result => { - results += result + '\n\n' - m.success('main') - }).catch(err => { - m.error('main') - console.log(`\n ${errorLog}failed to build main process`) - console.error(`\n${err}\n`) - process.exit(1) - }) - - pack(rendererConfig).then(result => { - results += result + '\n\n' - m.success('renderer') - }).catch(err => { - m.error('renderer') - console.log(`\n ${errorLog}failed to build renderer process`) - console.error(`\n${err}\n`) - process.exit(1) - }) -} - -function pack (config) { - return new Promise((resolve, reject) => { - webpack(config, (err, stats) => { - if (err) reject(err.stack || err) - else if (stats.hasErrors()) { - let err = '' - - stats.toString({ - chunks: false, - colors: true - }) - .split(/\r?\n/) - .forEach(line => { - err += ` ${line}\n` - }) - - reject(err) - } else { - resolve(stats.toString({ - chunks: false, - colors: true - })) - } - }) - }) -} - -function greeting () { - const cols = process.stdout.columns - let text = '' - - if (cols > 85) text = 'lets-build' - else if (cols > 60) text = 'lets-|build' - else text = false - - if (text && !isCI) { - say(text, { - colors: ['yellow'], - font: 'simple3d', - space: false - }) - } else console.log(chalk.yellow.bold('\n lets-build')) - console.log() -} diff --git a/.electron-vue/dev-client.js b/.electron-vue/dev-client.js deleted file mode 100644 index 2913ea4b07..0000000000 --- a/.electron-vue/dev-client.js +++ /dev/null @@ -1,40 +0,0 @@ -const hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true') - -hotClient.subscribe(event => { - /** - * Reload browser when HTMLWebpackPlugin emits a new index.html - * - * Currently disabled until jantimon/html-webpack-plugin#680 is resolved. - * https://github.com/SimulatedGREG/electron-vue/issues/437 - * https://github.com/jantimon/html-webpack-plugin/issues/680 - */ - // if (event.action === 'reload') { - // window.location.reload() - // } - - /** - * Notify `mainWindow` when `main` process is compiling, - * giving notice for an expected reload of the `electron` process - */ - if (event.action === 'compiling') { - document.body.innerHTML += ` - - -
- Compiling Main Process... -
- ` - } -}) diff --git a/.electron-vue/dev-runner.js b/.electron-vue/dev-runner.js deleted file mode 100644 index 63684c14d6..0000000000 --- a/.electron-vue/dev-runner.js +++ /dev/null @@ -1,183 +0,0 @@ -'use strict' - -const chalk = require('chalk') -const electron = require('electron') -const path = require('path') -const { say } = require('cfonts') -const { spawn } = require('child_process') -const webpack = require('webpack') -const WebpackDevServer = require('webpack-dev-server') -const webpackHotMiddleware = require('webpack-hot-middleware') - -const mainConfig = require('./webpack.main.config') -const rendererConfig = require('./webpack.renderer.config') - -let electronProcess = null -let manualRestart = false -let hotMiddleware - -function logStats (proc, data) { - let log = '' - - log += chalk.yellow.bold(`┏ ${proc} Process ${new Array((19 - proc.length) + 1).join('-')}`) - log += '\n\n' - - if (typeof data === 'object') { - data.toString({ - colors: true, - chunks: false - }).split(/\r?\n/).forEach(line => { - log += ' ' + line + '\n' - }) - } else { - log += ` ${data}\n` - } - - log += '\n' + chalk.yellow.bold(`┗ ${new Array(28 + 1).join('-')}`) + '\n' - - console.log(log) -} - -function startRenderer () { - return new Promise((resolve, reject) => { - rendererConfig.entry.renderer = [path.join(__dirname, 'dev-client')].concat(rendererConfig.entry.renderer) - - const compiler = webpack(rendererConfig) - hotMiddleware = webpackHotMiddleware(compiler, { - log: false, - heartbeat: 2500 - }) - - compiler.plugin('compilation', compilation => { - compilation.plugin('html-webpack-plugin-after-emit', (data, cb) => { - hotMiddleware.publish({ action: 'reload' }) - cb && cb() - }) - }) - - compiler.plugin('done', stats => { - logStats('Renderer', stats) - }) - - const server = new WebpackDevServer( - compiler, - { - contentBase: path.join(__dirname, '../'), - quiet: true, - setup (app, ctx) { - app.use(hotMiddleware) - ctx.middleware.waitUntilValid(() => { - resolve() - }) - } - } - ) - - server.listen(9091) - }) -} - -function startMain () { - return new Promise((resolve, reject) => { - mainConfig.entry.main = [path.join(__dirname, '../src/main/index.dev.js')].concat(mainConfig.entry.main) - - const compiler = webpack(mainConfig) - - compiler.plugin('watch-run', (compilation, done) => { - logStats('Main', chalk.white.bold('compiling...')) - hotMiddleware.publish({ action: 'compiling' }) - done() - }) - - compiler.watch({}, (err, stats) => { - if (err) { - console.log(err) - return - } - - logStats('Main', stats) - - if (electronProcess && electronProcess.kill) { - manualRestart = true - process.kill(electronProcess.pid) - electronProcess = null - startElectron() - - setTimeout(() => { - manualRestart = false - }, 5000) - } - - resolve() - }) - }) -} - -function startElectron () { - electronProcess = spawn(electron, [ - '--inspect=5861', - '--remote-debugging-port=8315', - '--nolazy', - path.join(__dirname, '../dist/electron/main.js') - ]) - - electronProcess.stdout.on('data', data => { - electronLog(data, 'blue') - }) - electronProcess.stderr.on('data', data => { - electronLog(data, 'red') - }) - - electronProcess.on('close', () => { - if (!manualRestart) process.exit() - }) -} - -function electronLog (data, color) { - let log = '' - data = data.toString().split(/\r?\n/) - data.forEach(line => { - log += ` ${line}\n` - }) - if (/[0-9A-z]+/.test(log)) { - console.log( - chalk[color].bold('┏ Electron -------------------') + - '\n\n' + - log + - chalk[color].bold('┗ ----------------------------') + - '\n' - ) - } -} - -function greeting () { - const cols = process.stdout.columns - let text = '' - - if (cols > 104) text = 'electron-vue' - else if (cols > 76) text = 'electron-|vue' - else text = false - - if (text) { - say(text, { - colors: ['yellow'], - font: 'simple3d', - space: false - }) - } else console.log(chalk.yellow.bold('\n electron-vue')) - console.log(chalk.blue(' getting ready...') + '\n') -} - -function init () { - greeting() - - Promise.all([startRenderer(), startMain()]) - .then(() => { - startElectron() - }) - .catch(err => { - console.error(err) - }) -} - -init() diff --git a/.electron-vue/marktextEnvironment.js b/.electron-vue/marktextEnvironment.js deleted file mode 100644 index a7f21975ba..0000000000 --- a/.electron-vue/marktextEnvironment.js +++ /dev/null @@ -1,38 +0,0 @@ -const GitRevisionPlugin = require('git-revision-webpack-plugin') -const { version } = require('../package.json') - -const getEnvironmentDefinitions = function () { - let shortHash = 'N/A' - let fullHash = 'N/A' - try { - const gitRevisionPlugin = new GitRevisionPlugin() - shortHash = gitRevisionPlugin.version() - fullHash = gitRevisionPlugin.commithash() - } catch(_) { - // Ignore error if we build without git. - } - const isOfficialRelease = !!process.env.MARKTEXT_IS_OFFICIAL_RELEASE - const versionSuffix = isOfficialRelease ? '' : ` (${shortHash})` - - return { - 'global.MARKTEXT_GIT_SHORT_HASH': JSON.stringify(shortHash), - 'global.MARKTEXT_GIT_HASH': JSON.stringify(fullHash), - - 'global.MARKTEXT_VERSION': JSON.stringify(version), - 'global.MARKTEXT_VERSION_STRING': JSON.stringify(`v${version}${versionSuffix}`), - 'global.MARKTEXT_IS_OFFICIAL_RELEASE': JSON.stringify(isOfficialRelease) - } -} - -const getRendererEnvironmentDefinitions = function () { - const env = getEnvironmentDefinitions() - return { - 'process.versions.MARKTEXT_VERSION': env['global.MARKTEXT_VERSION'], - 'process.versions.MARKTEXT_VERSION_STRING': env['global.MARKTEXT_VERSION_STRING'], - } -} - -module.exports = { - getEnvironmentDefinitions: getEnvironmentDefinitions, - getRendererEnvironmentDefinitions: getRendererEnvironmentDefinitions -} diff --git a/.electron-vue/preinstall.js b/.electron-vue/preinstall.js deleted file mode 100644 index faf45141b4..0000000000 --- a/.electron-vue/preinstall.js +++ /dev/null @@ -1,6 +0,0 @@ -'use strict' - -if (!/yarn\.js$/.test(process.env.npm_execpath)) { - console.error('Please use yarn to install dependencies.\n') - process.exit(1) -} diff --git a/.electron-vue/thirdPartyChecker.js b/.electron-vue/thirdPartyChecker.js deleted file mode 100644 index 6374640389..0000000000 --- a/.electron-vue/thirdPartyChecker.js +++ /dev/null @@ -1,36 +0,0 @@ -'use strict' - -const checker = require('license-checker') - -const getLicenses = (rootDir, callback) => { - checker.init({ - start: rootDir, - production: true, - development: false, - direct: true, - json: true, - onlyAllow: 'Unlicense;WTFPL;ISC;MIT;BSD;ISC;Apache-2.0;MIT*;Apache*;BSD*', - customPath: { - "licenses": "", - "licenseText": "none" - } - }, function(err, packages) { - callback(err, packages, checker) - }) -} - -// Check that all production dependencies are allowed. -const validateLicenses = rootDir => { - getLicenses(rootDir, (err, packages, checker) => { - if (err) { - console.log(`[ERROR] ${err}`) - process.exit(1) - } - console.log(checker.asSummary(packages)) - }) -} - -module.exports = { - getLicenses: getLicenses, - validateLicenses: validateLicenses -} diff --git a/.electron-vue/webpack.main.config.js b/.electron-vue/webpack.main.config.js deleted file mode 100644 index f1e2f1c1d0..0000000000 --- a/.electron-vue/webpack.main.config.js +++ /dev/null @@ -1,92 +0,0 @@ -'use strict' - -process.env.BABEL_ENV = 'main' - -const path = require('path') -const { getEnvironmentDefinitions } = require('./marktextEnvironment') -const { dependencies } = require('../package.json') -const webpack = require('webpack') -const proMode = process.env.NODE_ENV === 'production' - -const mainConfig = { - mode: 'development', - devtool: '#cheap-module-eval-source-map', - entry: { - main: path.join(__dirname, '../src/main/index.js') - }, - externals: [ - ...Object.keys(dependencies || {}) - ], - module: { - rules: [ - { - test: /\.(js)$/, - enforce: 'pre', - exclude: /node_modules/, - use: { - loader: 'eslint-loader', - options: { - formatter: require('eslint-friendly-formatter'), - failOnError: true - } - } - }, - { - test: /\.js$/, - use: 'babel-loader', - exclude: /node_modules/ - }, - { - test: /\.node$/, - use: 'node-loader' - } - ] - }, - node: { - __dirname: !proMode, - __filename: !proMode - }, - output: { - filename: '[name].js', - libraryTarget: 'commonjs2', - path: path.join(__dirname, '../dist/electron') - }, - plugins: [ - new webpack.NoEmitOnErrorsPlugin(), - // Add global environment definitions. - new webpack.DefinePlugin(getEnvironmentDefinitions()) - ], - resolve: { - alias: { - 'common': path.join(__dirname, '../src/common') - }, - extensions: ['.js', '.json', '.node'] - }, - target: 'electron-main' -} - -// Fix debugger breakpoints -if (!proMode && process.env.MARKTEXT_BUILD_VSCODE_DEBUG) { - mainConfig.devtool = '#inline-source-map' -} - -/** - * Adjust mainConfig for development settings - */ -if (!proMode) { - mainConfig.plugins.push( - new webpack.DefinePlugin({ - '__static': `"${path.join(__dirname, '../static').replace(/\\/g, '\\\\')}"` - }) - ) -} - -/** - * Adjust mainConfig for production settings - */ -if (proMode) { - mainConfig.devtool = '#nosources-source-map' - mainConfig.mode = 'production' -} - -module.exports = mainConfig diff --git a/.electron-vue/webpack.renderer.config.js b/.electron-vue/webpack.renderer.config.js deleted file mode 100644 index d4613346d9..0000000000 --- a/.electron-vue/webpack.renderer.config.js +++ /dev/null @@ -1,236 +0,0 @@ -'use strict' - -process.env.BABEL_ENV = 'renderer' - -const path = require('path') -const webpack = require('webpack') -const CopyWebpackPlugin = require('copy-webpack-plugin') -const MiniCssExtractPlugin = require("mini-css-extract-plugin") -const HtmlWebpackPlugin = require('html-webpack-plugin') -const VueLoaderPlugin = require('vue-loader/lib/plugin') -const SpritePlugin = require('svg-sprite-loader/plugin') -const postcssPresetEnv = require('postcss-preset-env') -const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer') - -const { getRendererEnvironmentDefinitions } = require('./marktextEnvironment') -const { dependencies } = require('../package.json') -const proMode = process.env.NODE_ENV === 'production' -/** - * List of node_modules to include in webpack bundle - * Required for specific packages like Vue UI libraries - * that provide pure *.vue files that need compiling - * https://simulatedgreg.gitbooks.io/electron-vue/content/en/webpack-configurations.html#white-listing-externals - */ -const whiteListedModules = ['vue'] - -const rendererConfig = { - mode: 'development', - devtool: '#cheap-module-eval-source-map', - entry: { - renderer: path.join(__dirname, '../src/renderer/main.js') - }, - externals: [ - ...Object.keys(dependencies || {}).filter(d => !whiteListedModules.includes(d)) - ], - module: { - rules: [ - { - test: /\.(js|vue)$/, - enforce: 'pre', - exclude: /node_modules/, - use: { - loader: 'eslint-loader', - options: { - formatter: require('eslint-friendly-formatter'), - failOnError: true - } - } - }, - { - test: /(theme\-chalk(?:\/|\\)index|katex|github\-markdown|prism[\-a-z]*|\.theme)\.css$/, - use: [ - 'to-string-loader', - 'css-loader' - ] - }, - { - test: /\.css$/, - exclude: /(theme\-chalk(?:\/|\\)index|katex|github\-markdown|prism[\-a-z]*|\.theme)\.css$/, - use: [ - proMode ? MiniCssExtractPlugin.loader : 'style-loader', - { loader: 'css-loader', options: { importLoaders: 1 } }, - { loader: 'postcss-loader', options: { - ident: 'postcss', - plugins: () => [ - postcssPresetEnv({ - stage: 0 - }) - ] - } } - ] - }, - { - test: /\.html$/, - use: 'vue-html-loader' - }, - { - test: /\.js$/, - use: 'babel-loader', - exclude: /node_modules/ - }, - { - test: /\.node$/, - use: 'node-loader' - }, - { - test: /\.vue$/, - use: { - loader: 'vue-loader', - options: { - sourceMap: true - } - } - }, - { - test: /\.svg$/, - use: [ - { - loader: 'svg-sprite-loader', - options: { - extract: true, - publicPath: './static/' - } - }, - 'svgo-loader' - ] - }, - { - test: /\.(png|jpe?g|gif)(\?.*)?$/, - use: { - loader: 'url-loader', - query: { - limit: 10000, - name: 'imgs/[name]--[folder].[ext]' - } - } - }, - { - test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, - loader: 'url-loader', - options: { - limit: 10000, - name: 'media/[name]--[folder].[ext]' - } - }, - { - test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, - use: { - loader: 'url-loader', - query: { - limit: 100000, - name: 'fonts/[name]--[folder].[ext]' - } - } - }, - { - test: /\.md$/, - use: [ - 'raw-loader' - ] - } - ] - }, - node: { - __dirname: !proMode, - __filename: !proMode - }, - plugins: [ - new SpritePlugin(), - new HtmlWebpackPlugin({ - filename: 'index.html', - template: path.resolve(__dirname, '../src/index.ejs'), - minify: { - collapseWhitespace: true, - removeAttributeQuotes: true, - removeComments: true - }, - nodeModules: process.env.NODE_ENV !== 'production' - ? path.resolve(__dirname, '../node_modules') - : false - }), - new webpack.NoEmitOnErrorsPlugin(), - new webpack.DefinePlugin(getRendererEnvironmentDefinitions()), - new VueLoaderPlugin() - ], - output: { - filename: '[name].js', - libraryTarget: 'commonjs2', - path: path.join(__dirname, '../dist/electron') - }, - resolve: { - alias: { - '@': path.join(__dirname, '../src/renderer'), - 'common': path.join(__dirname, '../src/common'), - 'muya': path.join(__dirname, '../src/muya'), - 'vue$': 'vue/dist/vue.esm.js' - }, - extensions: ['.js', '.vue', '.json', '.css', '.node'] - }, - target: 'electron-renderer' -} - -/** - * Adjust rendererConfig for development settings - */ -if (!proMode) { - rendererConfig.plugins.push( - new webpack.DefinePlugin({ - '__static': `"${path.join(__dirname, '../static').replace(/\\/g, '\\\\')}"` - }), - new webpack.HotModuleReplacementPlugin() - ) -} - -if (process.env.NODE_ENV !== 'production' && process.env.NODE_ENV !== 'test' && - !process.env.MARKTEXT_DEV_HIDE_BROWSER_ANALYZER) { - rendererConfig.plugins.push( - new BundleAnalyzerPlugin() - ) -} - -// Fix debugger breakpoints -if (!proMode && process.env.MARKTEXT_BUILD_VSCODE_DEBUG) { - rendererConfig.devtool = '#inline-source-map' -} - -/** - * Adjust rendererConfig for production settings - */ -if (proMode) { - rendererConfig.devtool = '#nosources-source-map' - rendererConfig.mode = 'production' - rendererConfig.plugins.push( - new MiniCssExtractPlugin({ - // Options similar to the same options in webpackOptions.output - // both options are optional - filename: '[name].[hash].css', - chunkFilename: '[id].[hash].css' - }), - new CopyWebpackPlugin([ - { - from: path.join(__dirname, '../static'), - to: path.join(__dirname, '../dist/electron/static'), - ignore: ['.*'] - }, - { - from: path.resolve(__dirname, '../node_modules/codemirror/mode/*/*'), - to: path.join(__dirname, '../dist/electron/codemirror/mode/[name]/[name].js') - } - ]), - new webpack.LoaderOptionsPlugin({ - minimize: true - }) - ) -} - -module.exports = rendererConfig diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 4deafc0f9c..0000000000 --- a/.eslintignore +++ /dev/null @@ -1,5 +0,0 @@ -test/unit/coverage/** -test/unit/*.js -test/e2e/*.js -src/renderer/assets/symbolIcon/index.js -src/muya/lib/assets/libs/*.js diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index 311806381d..0000000000 --- a/.eslintrc.js +++ /dev/null @@ -1,55 +0,0 @@ -module.exports = { - root: true, - parserOptions: { - parser: 'babel-eslint', - ecmaVersion: 8, - ecmaFeatures: { - impliedStrict: true - }, - sourceType: 'module' - }, - env: { - browser: true, - es6: true, - node: true - }, - extends: [ - 'standard', - 'eslint:recommended', - 'plugin:vue/base', - 'plugin:import/errors', - 'plugin:import/warnings' - ], - globals: { - __static: true - }, - plugins: [ - 'html', - 'vue' - ], - rules: { - // allow paren-less arrow functions - 'arrow-parens': 0, - // allow async-await - 'generator-star-spacing': 0, - // allow console - 'no-console': 0, - // allow debugger during development - 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0, - // disallow semicolons - semi: [2, "never"] - }, - settings: { - 'import/resolver': { - alias: { - map: [ - ['common', './src/common'], - // Normally only valid for renderer/ - ['@', './src/renderer'], - ['muya', './src/muya'] - ], - extensions: ['.js', '.vue', '.json', '.css', '.node'] - } - } - } -} diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md deleted file mode 100644 index 434e1457de..0000000000 --- a/.github/CHANGELOG.md +++ /dev/null @@ -1,552 +0,0 @@ -## [unrelease] - -**:warning:Breaking Changes:** - -- `preference.md` is deprecated and no longer supported. Please use the GUI or edit `preferences.json` manually. - -**:cactus:Feature** - -- The cursor jump to the end of format or to the next brackets when press `tab`(#976) -- Tab drag & drop inside the window -- Scrollable tabs - -**:butterfly:Optimization** - -- Rewrite `select all` when press `CtrlOrCmd + A` (#937) -- Set the cursor at the end of `#` in header when press arrow down to jump to the next paragraph.(#978) -- Improved startup time - -**:beetle:Bug fix** - -- Fixed some commonmark failed examples and add test case (#943) -- Fixed some bugs after press `backspace` (#934, #938) -- Change `inline math` vertical align to `top` (#977) -- Prevent to open the same file twice, instead select the existing tab (#878) - -### 0.14.0 - -This update **fixes a XSS security vulnerability** when exporting a document. - -**:warning:Breaking Changes:** - -- Minimum supported macOS version is 10.10 (Yosemite) -- Remove `lightColor` and `darkColor` in user preference (color change in view menu does not work any, and will remove when add custom theme.) -- We recommend user not use block element in paragraph, please use block element in html block. - -*Not Recommended* - -```md -foo
bar
zar -``` - -*Recommended* - -```md -
- foo -
- bar -
- zar -
-``` - -**:cactus:Feature** - -- Improve exception and error handling -- Support for user-defined titlebar style -- Support to open files in a new tab instead a new window (#574) -- Add inline math to format menu and float box (#649) -- GTK integration (#690) -- Add recently used directories to recently opened files (#643) -- Making images display smaller (#659) -- Open local markdown file when you click on it in another tab (#359) -- Clicking a link should open it in the browser (#425) -- Support maxOS `dark mode`, when you change `mode dark or light` in system, Mark Text will change its theme. -- Add new themes: Ulysses Light, Graphite Light, Material Dark and One Dark. -- Watch file changed in tabs and show a notice(autoSave is `false`) or update the file(autoSave is `true`) -- Support input inline Ruby charactors as raw html (#257) -- Added unsaved tab indicator -- Add front Menu by click the front menu icon (#875) -- Support diagram: [flowchart](https://github.com/adrai/flowchart.js), [vega-lite](https://github.com/vega/vega-lite), [mermaid](https://github.com/knsv/mermaid), [sequence](https://github.com/bramp/js-sequence-diagrams) (#914) -- Support create indent code block in preview mode.(#920) - -**:butterfly:Optimization** - -- Respect existing image title if no source is specified (#562) -- Separate font and font size for code blocks and source code mode (#373, #467) -- Opened files and opened directories/files can now be folded (#475, #602) -- You can now hide the quick insert hint (#621) -- Adjusted quote inline math color (#592) -- Fix inline math text align (#593) -- Added MIME type to Linux desktop file -- What is the character and number of left-top? (#666) -- Inserting Codeblock should automatically set cursor into language field (#684) -- Upstream: prismjs highlighting issues (#709) -- Improvements for "Open Recent" (#616) -- Make table of contents in sidebar collapsible (#404) -- Hide titlebar control buttons in custom titlebar style -- Corrected hamburger menu offset -- Optimization of inline html displa, now you can nest other inline syntax in inline html(#849) -- Use CmdOrCtrl + C/V to copy rich text to `word`(Windows) or `page`(macOS) (#885) - -**:beetle:Bug fix** - -- Fix dark preview box background color (#587) -- Use white PDF background color (#583) -- Fix document printing -- Restore default Mark Text style after exporting/printing -- Prevent enter key as language identifier (#569) -- Allow pasting text into the code block language text-box (#553) -- Fixed a crash when opening a directory with an unknown file extension -- Fixed an issue with `Save all` and `Delete all` buttons in the side bar -- Fixed exception when exporting a code block (#591) -- Fixed recommended filename -- Fixed multiple sidebar issues -- Fixed wrong font and theme when opening a directory (#696) -- Switching to another tab will now work in source-code mode too (#606) -- Fixed forced line break in a list is display wrong. (#672) -- Relative images are broken after exporting (#678) -- Unable to paste text in table cell(#670) -- Wrong padding when copy loose list to tight list(#706) -- Display Autocompletion in inline math(#673) -- Unable to export a document when the language identifier is undefined(#591) -- Incorrect rendering of pipe in code block within table(#660) -- Using extended code identifiers breaks code blocks (#697) -- Renderer exception when pasting text with new line(s) into a heading (#671) -- Fatal error when a directory is removed (#661) -- Wrong font and theme when opening file/directory (#696) -- Automatically wrap code block lines when printing or exporting as PDF (#710) -- Can't change tab in source code mode (#606) -- Minor checkbox list bug (#576) -- A hard line break followed by a list doesn't work in preview mode (#708) -- Ctrl + X (#622) -- Exception when removing a code block in a specific case (#568) -- List items are always copied as loose list (#705) -- Runtime bug when insert order list by quick insert (#760) -- Image inside HTML is not loaded (#754) -- No space around copy-pasted links (#752) -- Relative image reference in HTML is broken (#782) -- Selection cannot be cancelled by up / down keys (#630) -- Cannot create table while in typewriter mode (#679) -- Emojis don't work properly (#769) -- Fixed multiple parser issues (update marked.js to v0.6.1) -- Fixed nest math block issue (#586) -- Can't make a comma-separated list of dollar ($) amounts (#740) -- Fixed [...] is displayed in gray and orange (#432) -- Fixed an issue that relative images are not loaded after closing a tab -- Add symbolic link support -- Fixed bug when combine pre list and next list into one when inline update #707 -- Fix renderer error when selection in sidebar (#625) -- Fixed list parse error [more info](https://github.com/marktext/marktext/issues/831#issuecomment-477719256) -- Fixed source code mode tab switching -- Fixed source code mode to preview switching -- Mark Text didn't remove highlight when I delete the markdown symbol like * or `. (#893) -- After delete ``` at the beginning to paragraph by backspace, then type other text foo, the color will be strange, if you type 1. bar. error happened. (#892) -- Fix highlight error in code block (#545 #890) -- Fix files sorting in folder (#438) - -### 0.13.65 - -**:butterfly:Optimization** - -- Show tab bar when opening a new tab -- Use default bold (`CmdOrCtrl+B`) and italics (`CmdOrCtrl+I`) key binding (#346) -- Don't show save dialog for an empty document (#422) -- Sidebar and tab redesign -- Calculate artifact checksum after uploading (#566) -- Use `CmdOrCltr+Enter` to add table row bellow. - -**:beetle:Bug fix** - -- fix: #451 empty list item error -- fix: #522 paste bug when paste into empty line -- fix: #521 -- fix: #534 -- fix: #535 Application menu is not updated when switching windows -- fix #216 and #311 key binding issues on Linux and Windows -- fix #546 paste issue in table -- fix: Blank document was always encoded as `LF` -- fix: #541 - -### 0.13.50 - -**:cactus:Feature** - -- (#421) Add experiment function RTL support (#439) -- feat: #487 Show filename while hovering over marktext file on dock -- feat: export files in file menu -- feat: drag to import -- feat: quick insert paragraph -- feat: inline format float box -- feat: import files: TEX\ WIKI\ DOCX etc -- feat: portable Windows application (#369) -- feat: support search and replace in code block -- feat: support GFM diff in code block -- feat: suppoet quick input html in html block, eg: input div, press `tab` will auto input \<\/div> - -**:butterfly:Optimization** - -- Update linux documentation and remove snappy build (#381) -- Update Japanese Document Latest Release Update. -- add alfred workflow into readme (#394) -- French translation of README.md (#398) -- optimization: add gauss blur effect when open a modal (#407) -- Improvement math preview styles (#419) (#424) -- Turkish language translation for README.md (#427) -- Improvement: #414 Add functional bracket auto-completion (#428) -- feature: vscode debug config support (#446) -- Exclude hard-line-break from printing. (#454) -- export styled HTML with heading id's (#460) -- opti: #485 Open Project command. Maybe rename to Open folder -- Added Spanish translation (#499) -- feat: add tooltip to editor -- opti: #429 Support DataURL images (#480) -- opti: rewrite image picker -- opti: notify the user about the deletion url of the uploaded image -- rewrite code block, html block, math block, front matter - -**:beetle:Bug fix** - -- fix download url in docs. (#379) -- fix: #371 wrong paste behavior -- fix: #380 wrong action of list shortcut -- bugfix: inline math style error in list item (#405) -- bugfix: #406 relative image path not display (#411) -- bugfix: #400 (#410) -- fix: wrong mouse click position #416 (#423) -- fix: title bar resizing in north direction (#455) -- fix: #441 #451 empty list item has no paragraph (#456) -- fix: task list item centering (#457) -- fix: #402 table of contents sidebar scroll bug (#461) -- fix: recommend filename can be empty (#462) -- Formatting cleanups (#463) -- Arrow key up/down navigation in a table (#470) -- fix: #481 add missing dot to parser markdown files only (#483) -- fix: YAML frontmatter duplicates a new line on each opening of the file #494 -- fix(#431): broken math expression -- fix(#434): no need to auto pair in math block -- fix(#450) style error when render inline math -- fix: #399 #476 #490 math render with style miss -- fix: #393 - -### 0.12.25 - -**:cactus:Feature** - -**:butterfly:Optimization** - -- optimization: #361 easy sidebar toggle (#368) - -**:beetle:Bug fix** - -- fix: #348 do not export tabs and sidebar when export PDF -- bugfix: #360 No page breaks in PDF export -- bugfix: #167 #357 #344 -- fix: #343 Inconsistent color scheme in source code mode (#363) - -### 0.12.20 - -**:cactus:Feature** - -- feature: file list in side bar: tree view and list view. #71 -- feature: search in project in side bar. -- feature: table of content of the current edit file. -- feature: copy table from Number(MacOs App) -- feature: new file, new directory, copy, cut, paste, rename, remove to trash in side bar. -- feature: save all the opened files and close all the opened files. -- feature: Support reference link. #297 -- feature: Support reference image. -- feature: copy table in context menu (#331) -- feature: feedback via twitter -- feature: can use delete key now, #301 - -**:butterfly:Optimization** - -- optimization: rewirte table picker use popper -- optimization: add animation to checkbox when clicked -- Bundle desktop files and resources (#336) -- Rewrite notification (#337) - -**:beetle:Bug fix** - -- fix: can not copy full link #312 -- fix: can not export table markdown #313 -- bugfix: #328 source code mode shortcut not work (#332) -- bugfix: copy paste title delete text #321 (#333) -- fix: text cursor skip lines in paragraph #330 - -### 0.11.42 - -**:cactus:Feature** - -- feature: add editorFont setting in user preference. (#175) - Anderson -- feature: line break, support event and import and export markdown - Jocs -- feature: unindent list item - Jocs -- feature: Support for CRLF and LF line endings -- feature: Click filename to `rename` or `save` in title bar(**macOS ONLY**). -- feature: Support YAML Front Matter -- feature: Support `setext` heading but the default heading style is `atx` -- feature: User list item marker setting in preference file. -- feature: Select text from selected table (cell) only if you press Ctrl+A -- feature: Support Multiple lines math #242 -- feature: Support context menu: `copy`, `cut`, `paste`, `insert paragraph`, `edit table rows and columns` #169 - -**:butterfly:Optimization** - -- ATX headings strictly follow the GFM Spec #177 - Jocs -- no need to auto pair when * is to open a list item - Jocs -- optimization: add sticky to block html tag - Jocs -- Add Japanese readme (#191) - Neetshin -- Disable update menu for snap and not supported packages (#196) - Felix Häusler -- Check whether window size is larger than screen size (#192) - Felix Häusler -- Add fallback editor font family (#209) - Felix Häusler -- Use `partialRender` instead of `render` when render the file, this will speed up the render phase. -- optimization: reduce the width of scroll bar in float box. -- Smaller scrollbars and hover color (#245) -- update electron to v2.0.2 [SECURITY] -- Add support for tab indentation (#125) - -**:beetle:Bug fix** - -- fix: #94 history error -- fix: #213 style error when render math -- fix: the error 'Cannot read property 'forEach' of undefined' (#178) - 鸿则 -- fix: Change Source Code Mode Accelerator (#180) - Mice -- fix: #153 Double space between tasklist checkbox and text - Jocs -- fix: #198 navigation in table -- fix: #190 Delete user settings on uninstall (NSIS) (#203) - Felix Häusler -- fix: html block style error when active - Jocs -- fix: PDF Export is contacted by LaTeX hightlight #194 -- fix: Table inside a list is not supported #202 -- fix: Cannot open file when window is started maximized or in full-screen mode #217 -- fix: #243 (#260) -- fix: #232 (#259) -- fix: #251 -- fix: #248 dark background disappears when export PDF (#252) -- fix: #231 cut not work in code block -- fix: #274 can not selection codes in code block when the cursor is outside of code block. -- fix: frameless window drag -- fix: #79 detect image type by mime type - -### 0.10.21 - -**:notebook_with_decorative_cover:​Note** - -You need uninstall the old version of Mark Text before install version 0.10.21, because we changed the AppId when build. - -**:cactus:Feature** - -- block html #110 -- raw html #110 -- you can now indent list items with tab key -- auto pair `markdown syntax`, `quote`, `bracket` -- ability to insert an empty line between elements #33 -- recently used documents on Linux and Windows (#139) - -**:butterfly:Optimization** - -- Update third-party packages to the latest version -- Use HTTPS instead of HTTP (#158) -- Add Polish readme (#154) -- Optimization: sanitize html to avoid XSS attack #127 (#132) - -**:beetle:Bug fix** - -- fix: update outdated preferences on startup #100 -- fix: reset modification indicator after successfully saved changes -- fix: disable tab focus -- fix: strong and em parse error #116 -- fix horizontal line style #120 -- fix user preferences #122 -- fix: style error when export PDF/HTML with hr @Jocs -- fix UTF-8 BOM encoding -- fix: #162 support php language -- fix: #152 emoji error -- fix: #149 can not delete code block content - -### 0.9.25 - -**:cactus:Feature** - -- display and inline math support #36 -- Image path auto complement #96 -- Feature: Toggle loose list item in paragraph menu #103 -- Add loose and tight list compatibility #74 - -**:butterfly:Optimization** - -- adjust lineHeight and fontSize in typewriter mode -- optimization of output unstylish html @fxha -- Use 'fuzzaldrin' to filter language when insert code block -- Optimization: Obey the GFM and optimization of thematic break update. - Jocs -- Optimization: More than six # characters is not a heading So we don't need to highlight `#` - Jocs -- Optimization: A closing sequence of # characters is optional when write ATX heading - Jocs -- Optimization: watch image path change and rebuild the cache - Jocs -- Update: update vue and snabbdom to the latest version - Jocs -- Optimization: Use 'fuzzaldrin' to filter language when insert code block - Jocs -- Update travis-ci (#92) - Felix Häusler - -**:beetle:Bug fix** - -- fix: #81 -- fix: #55 -- fix: #63 -- fix: crash on first launch due missing directory (#78, #90, #93) -- fix: #101 -- Bugfix: #112 - Jocs -- Bugfix: can not empty the content in source code mode #105 - Jocs -- Bugfix: #107 -- fix: #88 (#108) - Felix Häusler -- Allow exiting full screen with maximize button on windows (#109) - Felix Häusler -- Bugfix: Caret can not move right when it's at the end of math format. #101 - Jocs - - -### 0.8.12 - -**:cactus:Feature** - -- Add user preferences in `Mark Text menu`, the shoutcut is `CmdorCtrl + ,`, you can set the default `theme` and `autoSave`. -- Add `autoSave` to `file menu`, the default value is in `preferences.md` which you can open in `Mark Text menu`. #45 -- Add drag and drop to open Markdown file with Mark Text @fxha -- User setting: fontSize, lineHeight, color in realtime mode. -- Move your file to other folder @DXXL -- Rename filename - -**:butterfly:Optimization** - -- Theme can be saved in user preferences now #16 -- Custom About dialog @fxha - -**:beetle:Bug fix** - -- fix: prevent open image or file directly when drag and drop over Mark Text #42 -- fix: set theme to all the open window not just the active one. -- fix: set correct application menu offset on windows #44 -- fix: Missing preferences menu in Linux and Windows. @fxha - -### 0.7.17 - -**Features** - -1. Check for updates..., and auto update when update available.(Still need signature...:cry:) - -2. Insert Image: ( In edit menu ) - - - absolute path - - - relative path - - - Upload Image to cloud - -3. Add file icons to languages when create code block or change language in code block. - -**Bug fix** - -1. It's hard to focus the input in code fence. - -2. When input the language in code block, click the language item will not cause hide the float box. - -3. other bugs in code block. - -4. Windows user can not use open with feature. - -5. The menu disapear in Linux sysyem. - -6. Fix the bug that the language highlight disapear when open markdown file with code block - -7. remove the symbol in output styled html. #41 - -8. escape the raw Markdown when open the markdown file. #37 - -**Optimization** - -1. allow user to change install directory on windows. - -2. Show notification when output HTML and PDF successfully. - -3. update css-tree to latest version. - -4. Add lineWrapping is true to codeMirror config - -### 0.6.14 - -**Features** - -- Add **dark** theme and **light** theme in both realtime preview mode and source code mode. - -- Insert `doutu` into the document, use CMD + / to open the panel. - -**Optimization** - -- Customize the scroll bar background color and thumb color. - -- Add collection of doutu. - -- Add History search word of doutu. - -**Bug fix** - -- Fix bug when search key in code block will cause the search input lose focus. - -- Fix the bug the editor will lose cursor after input Chinese. - -### 0.5.2 - -**Features** - -- Add Typewriter Mode, The current line will always in the center of the document. If you change the current line, it will be auto scroll to the new line. - -- Add Focus Mode, the current paragraph's will be focused. - -- Add Dark theme, Light theme. - -**Optimization** - -- Optimize the display of path name and file name in title bar. - -- Eidtor will auto scroll to the highlight word when click Find Prev or Find Next. - -**Bug fix** - -- Set back the cursor when mode change between source code mode and normal mode - -### 0.4.0 - -**Feature** - -- Search value in document, Use **FIND PREV** and **FIND NEXT** to selection previous one or next one. - - Add animation of highlight word. - - Auto focus the search input when open search panel. - - close the search panel will auto selection the last highlight word by ESC button. - -- Replace value - - Replace All - - Replace one and auto highlight the next word. - -**Bug fix** - -- fix the bug that click at the edge of code block will caused the code block does not be focused. - -**Optimization** - -- Optimize the display of word count in title bar. we also delete the background color of title bar to make it more concise. - -- Customize the style of checkbox in Task List Item. - -- Change the display of Insert Table dialog. - -### 0.3.0 - -**Features** - -- Export PDF - -**Bug fix** - -- fix the bug that editor can only print the first page. diff --git a/.github/COMMENTING-GUIDELINES.md b/.github/COMMENTING-GUIDELINES.md new file mode 100644 index 0000000000..4d17097297 --- /dev/null +++ b/.github/COMMENTING-GUIDELINES.md @@ -0,0 +1,148 @@ +# Commenting Guidelines + +Distilled from John Ousterhout's *A Philosophy of Software Design* (ch. 12–16). +One rule governs everything below: + +> **Comments should describe things that aren't obvious from the code.** + +A comment captures what was in the designer's mind but couldn't be expressed in +the code itself — the rationale, the constraints, the abstraction. If a comment +only restates the code, it has no value. + +--- + +## What goes in a comment + +There is real design information that code cannot express: the informal meaning +of a method, the units of a value, why a line exists, the rule the author +followed ("always call `a` before `b`"). Comments exist to record exactly this. + +Comments also make abstraction possible. An abstraction hides complexity so you +can use a module without reading its implementation — and the only way to +describe an abstraction is in prose. Without comments, the sole abstraction of a +function is its signature, which leaves out too much to be useful (does +`substring(start, end)` include `end`? what if `start > end`?). + +So a comment is good when it sits at a **different level of detail** than the +code: either lower (more precise) or higher (more intuitive). A comment at the +same level as the code is just restating it. + +--- + +## Rules + +**Don't repeat the code.** Before keeping a comment, ask: *could someone write +this just by looking at the code next to it?* If yes, delete it. A special case +of this: don't build the comment out of the words already in the name — +`// Normalize the resource name` above `getNormalizedResourceName()` adds nothing. + +**Lower-level comments add precision.** Most valuable on declarations — +instance variables, parameters, return values, where the name and type aren't +enough. Spell out: units; whether bounds are inclusive or exclusive; what `null` +means if allowed; who owns/frees a resource; any invariant ("this list always +has at least one entry"). Describe what a variable *is*, not how the code +mutates it — think nouns, not verbs. + +**Higher-level comments add intuition.** One sentence on what a block *does* and +why, omitting the mechanics. A reader who has that sentence can explain the rest +of the code themselves — and judge whether it's correct. These are harder to +write: ask yourself "what is the simplest thing I can say that explains +everything here?" + +**Separate interface from implementation comments.** Interface comments tell a +caller what they need to use the thing — they *are* the abstraction. +Implementation comments explain how it works inside. Never let one leak into the +other. A caller should not have to read a method's body to call it correctly. + + - *Class:* the abstraction it provides, what an instance represents, its + limitations. No method-by-method detail. + - *Method:* behavior from the caller's view; every parameter and the return + value, precisely; side effects; exceptions; preconditions. Keep + preconditions few, but document the ones that remain. + - The test for any fact: *does a caller need it to use this?* Wire formats, + internal data structures, transparent crash recovery — no, those are + implementation. A comparison being string-vs-integer, or whether requests + fire concurrently (affects performance) — yes. + +**Implementation comments say what and why, not how.** Most short methods need +none. For longer ones, put a high-level line before each major block or +non-trivial loop. Always explain anything subtle the code can't show — a +bug-fix whose purpose isn't obvious gets a comment (reference the issue rather +than restating it: `// Fixes #436 — autolink overrun on pasted URLs`). + +**Cross-module decisions need a findable home.** When a decision spans several +files, document it where developers will actually trip over it — e.g. at the enum +declaration they must edit, list every other place that needs updating. If +there's no natural center, keep a `designNotes` file and point to it from each +site (`// See "Zombies" in designNotes`). + +--- + +## Write the comments first + +Comments written last are bad comments: by then you've checked out mentally, your +memory of the design is fuzzy, and you write them by reading the code — so they +repeat it. + +Instead, for a new class: + +1. Write the class interface comment. +2. Write interface comments and signatures for the key public methods; leave the + bodies empty. +3. Iterate until the structure feels right. +4. Write declarations and comments for the key instance variables. +5. Fill in the bodies, adding implementation comments as needed. + +When the code is done, the comments are done — there's no backlog. And it costs +almost nothing: typing code and comments together is a small fraction of total +development time. + +**A comment is a complexity detector.** The comment for a method or variable +should be short *and* complete. If you can't write one that's both, the thing +you're describing is probably badly designed — that's the signal to fix the +design, not the comment. + +> 🚩 If the interface comment has to describe the implementation, the method is +> too shallow. 🚩 If a comment merely repeats the code, it's noise. 🚩 If +> something is hard to describe, the design has a problem. + +--- + +## Keep them alive + +**Put the comment next to the code it describes.** The farther away, the less +likely it gets updated. A method's interface comment belongs right by its body, +not in a separate header. Push implementation comments down to the narrowest +scope they cover rather than stacking them at the top. As a corollary: the +farther a comment is from its code, the more abstract it should be. + +**Comments belong in the code, not the commit log.** If a future developer will +need the information, put it where they'll see it. A commit message explaining a +subtle fix is invisible to the next person who "simplifies" it back into a bug. + +**Don't duplicate.** Document each decision once, in the most obvious place, and +reference it from the others. Don't re-document one module inside another, and +don't restate things already in an external spec or manual — link to them. + +**Check the diff before committing.** Scan every change and confirm the +surrounding comments still hold. This also catches stray debug code and stale +TODOs. + +**Higher-level comments are easier to maintain** — they survive minor code +changes because they don't depend on details. Reserve precise, detailed comments +for the places that genuinely need them. + +--- + +## Names are documentation too (ch. 14) + +A good name reduces the need for comments. Make names **precise** — `getCount()` +counts *what*? — and make them **paint an image** of what the thing is and isn't, +in two or three words. A single vague name once cost the author a six-month bug +hunt: `block` meant both a disk block and a file block; `diskBlock` / `fileBlock` +would have prevented it. Don't settle for "close enough." + +--- + +**The test for any comment:** is it something you *couldn't* read off the code, +and is it both short and complete? diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 2635946c40..33b6928388 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,61 +1,70 @@ -# Mark Text Contributing Guide +# MarkText Contributing Guide -Hi, I'm really excited that you are interested in contributing to Mark Text :tada:. Before submitting your contribution though, please make sure to take a moment and read through the following guidelines. +We are really excited that you are interested in contributing to MarkText :tada:. Before submitting your contribution, please make sure to take a moment and read through the following guidelines. -- [Code of Conduct](https://github.com/marktext/marktext/blob/master/.github/CODE_OF_CONDUCT.md) -- [Issue Reporting Guidelines](#issue-reporting-guidelines) -- [Pull Request Guidelines](#pull-request-guidelines) +- [Code of Conduct](../packages/website/content/docs/dev/CODE_OF_CONDUCT.md) +- [Philosophy](#philosophy) +- [Issue reporting guidelines](#issue-reporting-guidelines) +- [Pull request guidelines](#pull-request-guidelines) - [Where should I start?](#where-should-i-start) -- [Quick Start](#quick-start) - - [Build Instructions](#build-instructions) +- [Quick start](#quick-start) + - [Build instructions](#build-instructions) - [Style guide](#style-guide) -- [Project Structure](#project-structure) + - [Commenting guidelines](#commenting-guidelines) +- [Developer documentation](#developer-documentation) + +## Philosophy + +🔑 Our philosophy is to keep things clean, simple and minimal. +MarkText is constantly changing and we want these improvements to align with our philosophy. For example, look at the side bar and tabs; these two panels provide awesome functionality *and* aren't distracting to the user. We'll continue adding more features (like plugins) that can be activated via 'settings' to improve MarkText. This will allow everyone to customize MarkText for their needs and provide a minimal default interface. ## Issue Reporting Guidelines -Please search for similar issues before opening an issue and always follow the [issue template](https://github.com/marktext/marktext/blob/master/.github/ISSUE_TEMPLATE.md). Please provide a detailed description of the problem in your PR and live demo or screenshots are preferred. +Please search for similar issues before opening an issue and always follow the [issue template](.github/ISSUE_TEMPLATE/). Please review the following Pull Request guidelines before making your own PR. ## Pull Request Guidelines -- Submit PRs directly to the `develop` branch. +**In *all* Pull Requests:** provide a detailed description of the problem, as well as a demonstration with screen recordings and/or screenshots. -- Work in the `src` folder and **DO NOT** checkin `dist` in commits. +Please make sure the following is done before submitting a PR: -- If you adding new feature: - - - Open a suggestion issue first. - - Provide convincing reason to add this feature. - - Then submit your PR. +- Submit PRs directly to the `develop` branch. +- Reference the related issue in the PR comment. +- Utilize [JSDoc](https://github.com/jsdoc/jsdoc) for better code documentation. +- Ensure all tests pass. +- Please lint (`pnpm run lint`) your PR. +- All PRs need to pass the **CI** before merged. If it fails, please try to solve the issue(s) and feel free to ask for any help. -- If fixing a bug: +If you add new feature: - - If you are resolving a special issue, add `fix: #xxx[,#xxx]` (`#xxx` is the issue id) in your PR title for a better release log, e.g.`fix: #3899 update entities encoding/decoding`. - - Update `.github/CHANGELOG.md` for notable changes - like bug fixes and features. - - Provide detailed description of the bug in your PR and/or link to the issue. You can also include screenshots. +- Open a suggestion issue first. +- Provide your reasoning on why you want to add this feature. +- Submit your PR. -- Please lint and test your PR before submitting. +If you fix a bug: -- All PRs need to pass the **Travis CI** before merged. If it fails, please try to solve the issue(s) and feel free to ask for any help. +- If you are resolving a special issue, please add `fix: # ` in your PR title (e.g.`fix: #3899 update entities encoding/decoding`). +- Provide a detailed description of the bug in your PR and/or link to the issue. ### Where should I start? -Find a issue flagged as a `bug`, `help wanted` or `enhancement `. The `good first issue` issues are good for new comers. Discuss the solution in the issue and after the final solution is approved by the Mark Text members, you can submit/work on the PR. For small fixes, you can directly open a PR. +A good way to start is to find an [issue](https://github.com/marktext/marktext/issues) labeled as `bug`, `help wanted` or `feature request`. The `good first issue` issues are good for newcomers. Please discuss the solution for larger issues first and after the final solution is approved by the MarkText members, you can submit/work on the PR. For small changes you can directly open a PR. Other ways to help: -- Documentation (*1) -- Translation (*1) -- Help to answer `more detail` issues or discuss changes and features. -- Report bugs and feature ideas. +- Documentation +- Translation (currently unavailable) +- Design icons and logos +- Improve the UI +- Write tests for MarkText +- Share your thoughts! We want to hear about features you think are missing, any bugs you find, and why you :heart: MarkText. -***1**: More or less blocked until v1.0 release because of early development phase. - -## Quick Start +## Quick start 1. Fork the repository. 2. Clone your fork: `git clone git@github.com:/marktext.git` 3. Create a feature branch: `git checkout -b feature` -4. Make you changes and push your branch. +4. Make your changes and push your branch. 5. Create a PR against `develop` and describe your changes. **Rebase your PR:** @@ -68,60 +77,21 @@ If there are conflicts or you want to update your local branch, please do the fo ### Build Instructions -**Prerequisites:** - -Before you can get started developing, you need set up your build environment: - -- Node.js `>=v8.12.0`, npm and yarn -- Python `v2.7.x` for node-gyp -- C++ compiler and development tools - -**Additional development dependencies on Linux:** +🔗 [Build Instructions](https://marktext.me/docs/dev/build) -- libx11 (dev) -- libxkbfile (dev) +### Style Guide -On Debian-based Linux: `sudo apt-get install libx11-dev libxkbfile-dev` -On Red Hat-based Linux: `sudo dnf install libx11-devel libxkbfile-devel` +You can run ESLint (`pnpm run lint`) to help you to follow the style guide. -**Let's build:** - -1. Go to `marktext` folder -2. Install dependencies: `yarn install` or `yarn install --frozen-lockfile` -3. Build Mark Text: `npm run build` -4. Mark Text binary is located under `build` folder - -Copy the build app to applications folder, or if on Windows run the executable installer. - -**Important scripts:** - -``` -$ npm run + + diff --git a/packages/desktop/src/renderer/src/Main.vue b/packages/desktop/src/renderer/src/Main.vue new file mode 100644 index 0000000000..d87f550a52 --- /dev/null +++ b/packages/desktop/src/renderer/src/Main.vue @@ -0,0 +1,8 @@ + + + diff --git a/src/renderer/assets/.gitkeep b/packages/desktop/src/renderer/src/assets/.gitkeep similarity index 100% rename from src/renderer/assets/.gitkeep rename to packages/desktop/src/renderer/src/assets/.gitkeep diff --git a/src/renderer/assets/icons/import_file.svg b/packages/desktop/src/renderer/src/assets/icons/import_file.svg similarity index 100% rename from src/renderer/assets/icons/import_file.svg rename to packages/desktop/src/renderer/src/assets/icons/import_file.svg diff --git a/packages/desktop/src/renderer/src/assets/icons/searchIcons/LICENSE b/packages/desktop/src/renderer/src/assets/icons/searchIcons/LICENSE new file mode 100644 index 0000000000..9f58a16923 --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/icons/searchIcons/LICENSE @@ -0,0 +1,22 @@ +Icons are copied from https://github.com/atom/find-and-replace/blob/aea59a5aed11940d8a4f14aa8a13cf656d41e19c/lib/find-view.js#L112 under MIT license: + +Copyright (c) 2014 GitHub Inc. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/desktop/src/renderer/src/assets/icons/searchIcons/iconCase.svg b/packages/desktop/src/renderer/src/assets/icons/searchIcons/iconCase.svg new file mode 100644 index 0000000000..93abc5a83d --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/icons/searchIcons/iconCase.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/desktop/src/renderer/src/assets/icons/searchIcons/iconRegex.svg b/packages/desktop/src/renderer/src/assets/icons/searchIcons/iconRegex.svg new file mode 100644 index 0000000000..8659298454 --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/icons/searchIcons/iconRegex.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/packages/desktop/src/renderer/src/assets/icons/searchIcons/iconWord.svg b/packages/desktop/src/renderer/src/assets/icons/searchIcons/iconWord.svg new file mode 100644 index 0000000000..29805e3648 --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/icons/searchIcons/iconWord.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/renderer/assets/images/logo.png b/packages/desktop/src/renderer/src/assets/images/logo.png similarity index 100% rename from src/renderer/assets/images/logo.png rename to packages/desktop/src/renderer/src/assets/images/logo.png diff --git a/packages/desktop/src/renderer/src/assets/styles/index.css b/packages/desktop/src/renderer/src/assets/styles/index.css new file mode 100644 index 0000000000..336c800bf0 --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/styles/index.css @@ -0,0 +1,430 @@ +/* Common CSS use by both light and dark themes */ +:root { + --titleBarHeight: 32px; + --editorAreaWidth: 750px; + + /*editor*/ + /*Theme color cluster*/ + --themeColor: rgba(33, 181, 111, 1); + --themeColor90: rgba(33, 181, 111, 0.9); + --themeColor80: rgba(33, 181, 111, 0.8); + --themeColor70: rgba(33, 181, 111, 0.7); + --themeColor60: rgba(33, 181, 111, 0.6); + --themeColor50: rgba(33, 181, 111, 0.5); + --themeColor40: rgba(33, 181, 111, 0.4); + --themeColor30: rgba(33, 181, 111, 0.3); + --themeColor20: rgba(33, 181, 111, 0.2); + --themeColor10: rgba(33, 181, 111, 0.1); + --highlightThemeColor: var(--themeColor); + + --highlightColor: rgba(33, 181, 111, 0.4); + --selectionColor: rgba(0, 0, 0, 0.1); + --editorColor: #4d4d4d; + --editorColor80: #333333; + --editorColor60: #666666; + --editorColor50: #808080; + --editorColor40: #999999; + --editorColor30: #b3b3b3; + --editorColor10: #e6e6e6; + --editorColor04: #f7f7f7; + --editorBgColor: rgba(255, 255, 255, 1); + --deleteColor: #ff6969; + --iconColor: #6b737b; + --codeBgColor: #d8d8d869; + --codeBlockBgColor: rgba(0, 0, 0, 0.03); + --footnoteBgColor: rgba(0, 0, 0, 0.03); + --inputBgColor: rgba(0, 0, 0, 0.06); + --focusColor: var(--themeColor); + + --buttonFontColor: var(--editorColor); + --buttonBgColor: #ffffff; + --buttonBorder: 1px solid #dcdfe6; + --buttonShadow: none; + --buttonFontColorHover: var(--buttonFontColor); + --buttonBgColorHover: linear-gradient(#f9f9f9, #f2f2f2); + --buttonBorderHover: var(--buttonBorder); + --buttonFontColorActive: var(--buttonFontColor); + --buttonBgColorActive: #f0f0f0; + --buttonBorderActive: var(--buttonBorder); + --buttonFocusBorder: 1px solid var(--themeColor); + + --buttonPrimaryFontColor: #ffffff; + --buttonPrimaryBgColor: var(--themeColor); + --buttonPrimaryBorder: none; + --buttonPrimaryShadow: 0 0 8px 0 rgba(0, 0, 0, 0.1); + --buttonPrimaryFontColorHover: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorHover: var(--buttonPrimaryBgColor); + --buttonPrimaryBorderHover: var(--buttonPrimaryBorder); + --buttonPrimaryFontColorActive: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorActive: var(--buttonPrimaryBgColor); + --buttonPrimaryBorderActive: var(--buttonPrimaryBorder); + --buttonPrimaryFocusBorder: none; + --buttonPrimaryFocusShadow: inset 0 0 0 1px rgba(13, 91, 55, 0.5), 0 0 0 1px var(--themeColor); + --tableBorderColor: #e5e5e5; + + /* Markdown element colors */ + --headingColor: var(--editorColor80); + --h1Color: var(--headingColor); + --h2Color: var(--headingColor); + --h3Color: var(--headingColor); + --h4Color: var(--headingColor); + --h5Color: var(--headingColor); + --h6Color: var(--headingColor); + --blockquoteTextColor: var(--editorColor50); + --blockquoteBorderColor: var(--themeColor); + --hrColor: var(--editorColor10); + --linkColor: var(--themeColor); + --strongColor: var(--editorColor); + --emColor: var(--editorColor); + --listMarkerColor: var(--editorColor50); + + /*marktext*/ + --sideBarColor: rgba(0, 0, 0, 0.6); + --sideBarIconColor: var(--iconColor); + --sideBarTitleColor: rgba(0, 0, 0, 1); + --sideBarTextColor: rgba(0, 0, 0, 0.4); + --sideBarBgColor: rgba(242, 242, 242, 0.9); + --sideBarItemHoverBgColor: rgba(0, 0, 0, 0.03); + --itemBgColor: rgba(255, 255, 255, 0.6); + + --floatFontColor: rgba(0, 0, 0, 0.6); + --floatBgColor: #fff; + --floatHoverColor: rgba(0, 0, 0, 0.04); + --floatBorderColor: rgba(0, 0, 0, 0.1); + --floatShadow: + rgba(15, 15, 15, 0.03) 0px 0px 0px 1px, rgba(15, 15, 15, 0.04) 0px 3px 6px, + rgba(15, 15, 15, 0.05) 0px 9px 24px; + --maskColor: rgba(232, 232, 232, 0.8); + + /* Notification colors */ + --notificationPrimaryColor: #ffffff; + --notificationPrimaryBg: var(--themeColor); + --notificationInfoColor: #ffffff; + --notificationInfoBg: #909399; + --notificationWarningColor: #ffffff; + --notificationWarningBg: #ff8800; + --notificationErrorColor: #ffffff; + --notificationErrorBg: #ff6969; +} + +::-webkit-scrollbar, +::-webkit-scrollbar-corner { + background: var(--floatHoverColor); +} + +::-webkit-scrollbar:vertical { + width: 12px; +} + +::-webkit-scrollbar-thumb { + background: var(--editorColor30); +} + +::-webkit-scrollbar-thumb:hover { + background: var(--editorColor50); +} + +:focus { + outline: none; +} + +html, +body { + margin: 0; + padding: 0; + background: transparent !important; + caret-color: var(--editorColor); +} + +body { + position: fixed; + width: 100vw; + height: 100vh; + top: 0; + left: 0; + right: 0; + bottom: 0; +} + +.icon { + width: 1em; + height: 1em; + vertical-align: -0.15em; + fill: currentColor; + overflow: hidden; +} + +.text-overflow { + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; +} + +/* Overwrite Element style */ +.el-tooltip__popper { + background: var(--floatBgColor); + color: var(--sideBarColor); + box-shadow: 0 4px 8px 0 var(--floatBorderColor); + border: solid 1px var(--floatBorderColor); +} + +.el-tooltip__popper[data-popper-placement^='left'] .popper__arrow { + border-left-color: #303133; +} +.el-tooltip__popper[data-popper-placement^='top'] .popper__arrow { + border-top-color: #303133; +} +.el-tooltip__popper[data-popper-placement^='right'] .popper__arrow { + border-right-color: #303133; +} +.el-tooltip__popper[data-popper-placement^='bottom'] .popper__arrow { + border-bottom-color: #303133; +} + +.el-button:hover, +.el-button--primary:hover { + border-color: unset; +} +.el-button:active, +.el-button--primary:active { + border-color: unset; +} +.el-button:focus, +.el-button--primary:focus { + border-color: unset; +} + +.el-slider__button { + border-color: var(--themeColor); +} + +.el-slider__bar { + background-color: var(--themeColor); +} + +.el-radio__input.is-checked .el-radio__inner { + border-color: var(--themeColor); + background-color: var(--themeColor); +} + +.el-radio__input.is-checked + .el-radio__label { + color: var(--editorColor); +} + +.el-dialog, +.el-dialog.ag-dialog-table { + border-radius: 8px; + box-shadow: 0 4px 8px 0 var(--floatBorderColor); + border: solid 1px var(--floatBorderColor); + background-color: var(--floatBgColor); +} +.el-dialog__header { + position: relative; + display: block; + padding: 8px 6px; +} + +input.el-input__inner { + background: var(--inputBgColor); + border: 1px solid var(--editorColor10); + color: var(--editorColor80); +} + +.el-input.is-focus .el-input__wrapper, +.el-input__wrapper.is-focus { + box-shadow: 0 0 0 1px var(--themeColor) inset; +} + +.el-input-number.is-controls-right span.el-input-number__decrease, +.el-input-number.is-controls-right span.el-input-number__increase { + background: var(--buttonBgColor); + border-left: none; + color: var(--editorColor80); +} + +.el-input-number.is-controls-right span.el-input-number__increase { + border-bottom: none; +} + +.el-tabs__nav-wrap::after { + background: var(--editorColor04); +} + +div.el-tabs__item { + color: var(--editorColor); +} + +div.el-tab-pane { + color: var(--editorColor); + font-size: 14px; +} + +.ag-dialog-table .dialog-title svg { + width: 1.5em; + height: 1.5em; +} + +.ag-underdevelop { + opacity: 0.3; + pointer-events: none; + cursor: not-allowed; +} + +.button, +.muya-button, +.el-button { + cursor: pointer; + text-decoration: none; + color: var(--buttonFontColor); + background: var(--buttonBgColor); + border: var(--buttonBorder); + border-radius: 5px; + box-shadow: var(--buttonShadow); + padding: 7px 14px; +} +.button:hover, +.muya-button:hover, +.el-button:hover { + color: var(--buttonFontColorHover); + background: var(--buttonBgColorHover); + border: var(--buttonBorderHover); +} +.button:active, +.muya-button:active, +.el-button:active { + color: var(--buttonFontColorActive); + background: var(--buttonBgColorActive); + border: var(--buttonBorderActive); +} +.button:focus, +.muya-button:focus, +.el-button:focus { + outline: none; + border: var(--buttonFocusBorder); +} + +.button-primary, +.muya-button-primary, +.el-button--primary { + cursor: pointer; + text-decoration: none; + color: var(--buttonPrimaryFontColor); + background: var(--buttonPrimaryBgColor); + border: var(--buttonPrimaryBorder); + border-radius: 5px; + box-shadow: var(--buttonPrimaryShadow); + padding: 7px 14px; +} +.button-primary:hover, +.muya-button-primary:hover, +.el-button--primary:hover { + color: var(--buttonPrimaryFontColorHover); + background: var(--buttonPrimaryBgColorHover); + border: var(--buttonPrimaryBorderHover); +} +.button-primary:active, +.muya-button-primary:active, +.el-button--primary:active { + color: var(--buttonPrimaryFontColorActive); + background: var(--buttonPrimaryBgColorActive); + border: var(--buttonPrimaryBorderActive); +} +button.button-primary:not(.custom):focus, +.button-primary:focus, +.muya-button-primary:focus, +.el-button--primary:focus { + outline: none; + border: var(--buttonPrimaryFocusBorder); + box-shadow: var(--buttonPrimaryFocusShadow); +} + +.button.disabled, +.button[disabled], +.muya-button.disabled, +.muya-button[disabled], +.el-button.disabled, +.el-button[disabled], +.button-primary.disabled, +.button-primary[disabled], +.el-button--primary.disabled, +.el-button--primary[disabled] { + cursor: not-allowed !important; + opacity: 0.65 !important; + box-shadow: none !important; +} + +.button.small, +.muya-button.small, +.el-button.small, +.button-primary.small, +.muya-button-primary.small, +.el-button--primary.small { + padding: 5px 8px; +} +.button.tiny, +.muya-button.tiny, +.el-button.tiny, +.button-primary.tiny, +.muya-button-primary.tiny, +.el-button--primary.tiny { + font-size: 13px; + padding: 4px 7px; +} + +.button-group { + /* Remove space between buttons */ + font-size: 0; +} +.button-group > button:first-child:not(:last-child) { + border-bottom-right-radius: 0; + border-top-right-radius: 0; + margin-left: 0; + border-right: none; +} +.button-group > button:not(:first-child):not(:last-child) { + border-radius: 0; + margin-left: 0; + margin-right: 0; + border-right: none; +} +.button-group > button:last-child:not(:first-child) { + border-bottom-left-radius: 0; + border-top-left-radius: 0; + margin-right: 0; +} + +/* Overwrite element UI button padding */ +.el-button, +.el-button--primary { + padding: 7px 14px; +} +.el-button:focus { + background-color: var(--buttonBgColor); +} +.el-button--primary:focus { + background-color: var(--buttonPrimaryBgColor); +} +.el-button.is-disabled, +.el-button.is-disabled:focus, +.el-button.is-disabled:hover { + color: var(--buttonFontColor); + background: var(--buttonBgColor); + border: var(--buttonBorder); + box-shadow: none; +} +.el-button--primary.is-disabled, +.el-button--primary.is-disabled:focus, +.el-button--primary.is-disabled:hover { + color: var(--buttonPrimaryFontColor); + background: var(--buttonPrimaryBgColor); + border: var(--buttonPrimaryBorder); + box-shadow: none; +} + +.image-viewer { + z-index: 10001; +} + +.el-tabs__active-bar { + background-color: var(--themeColor); +} diff --git a/packages/desktop/src/renderer/src/assets/styles/printService.css b/packages/desktop/src/renderer/src/assets/styles/printService.css new file mode 100644 index 0000000000..63ed0666a9 --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/styles/printService.css @@ -0,0 +1,88 @@ +body .ag-render-container { + position: fixed; + left: -10000px; + right: -10000px; + max-width: 100%; +} + +body article.print-container { + display: none; +} + +@media print { + /* Set all settings like margin with @page or Electron settings. */ + + @page { + /* + Recent versions of chromemium will treat margins as "transparent" - but we should set it to white so it isn't so ugly. + This should not affect the actual printing. + */ + background: white; + margin: 15mm 20mm 15mm 20mm; + } + + .editor-container { + display: none !important; + } + + /* General rules for printing. */ + html, + body { + position: relative !important; + width: 100% !important; + display: block !important; + height: auto !important; + background: #ffffff !important; + overflow: hidden; + } + + body { + position: relative !important; + height: auto !important; + } + + body > div, + body > svg { + display: none !important; + } + + body article.print-container { + display: block; + } + body article.print-container { + height: 100%; + } + body article.markdown-body { + box-sizing: border-box; + min-width: 200px; + max-width: 980px; + margin: 0 auto; + padding: 0; + } + body article.markdown-body table { + display: table; + } + body article.markdown-body input[type='checkbox'] ~ p { + margin-top: 0; + display: inline-block; + } + body article.markdown-body pre code.fenced-code-block { + white-space: pre-wrap; + word-break: break-word; + } + body article.markdown-body .flowchart, + body article.markdown-body .sequence, + body article.markdown-body .plantuml, + body article.markdown-body .mermaid, + body article.markdown-body .vega-embed { + text-align: center; + } + body article.markdown-body .flowchart > svg, + body article.markdown-body .sequence > svg, + body article.markdown-body .plantuml > svg, + body article.markdown-body .plantuml > img, + body article.markdown-body .mermaid > svg, + body article.markdown-body .vega-embed > svg { + max-width: 100%; + } +} diff --git a/packages/desktop/src/renderer/src/assets/symbolIcon/index.js b/packages/desktop/src/renderer/src/assets/symbolIcon/index.js new file mode 100644 index 0000000000..e562db7e2c --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/symbolIcon/index.js @@ -0,0 +1,86 @@ +;(function (window) { + var svgSprite = + '' + var script = (function () { + var scripts = document.getElementsByTagName('script') + return scripts[scripts.length - 1] + })() + var shouldInjectCss = script.getAttribute('data-injectcss') + var ready = function (fn) { + if (document.addEventListener) { + if (~['complete', 'loaded', 'interactive'].indexOf(document.readyState)) { + setTimeout(fn, 0) + } else { + var loadFn = function () { + document.removeEventListener('DOMContentLoaded', loadFn, false) + fn() + } + document.addEventListener('DOMContentLoaded', loadFn, false) + } + } else if (document.attachEvent) { + IEContentLoaded(window, fn) + } + function IEContentLoaded(w, fn) { + var d = w.document, + done = false, + init = function () { + if (!done) { + done = true + fn() + } + } + var polling = function () { + try { + d.documentElement.doScroll('left') + } catch (e) { + setTimeout(polling, 50) + return + } + init() + } + polling() + d.onreadystatechange = function () { + if (d.readyState == 'complete') { + d.onreadystatechange = null + init() + } + } + } + } + var before = function (el, target) { + target.parentNode.insertBefore(el, target) + } + var prepend = function (el, target) { + if (target.firstChild) { + before(el, target.firstChild) + } else { + target.appendChild(el) + } + } + function appendSvg() { + var div, svg + div = document.createElement('div') + div.innerHTML = svgSprite + svgSprite = null + svg = div.getElementsByTagName('svg')[0] + if (svg) { + svg.setAttribute('aria-hidden', 'true') + svg.style.position = 'absolute' + svg.style.width = 0 + svg.style.height = 0 + svg.style.overflow = 'hidden' + prepend(svg, document.body) + } + } + if (shouldInjectCss && !window.__iconfont__svg__cssinject__) { + window.__iconfont__svg__cssinject__ = true + try { + document.write( + '' + ) + } catch (e) { + console && console.log(e) + } + } + ready(appendSvg) +})(window) diff --git a/packages/desktop/src/renderer/src/assets/themes/ayu-dark.theme.css b/packages/desktop/src/renderer/src/assets/themes/ayu-dark.theme.css new file mode 100644 index 0000000000..f8a4668d6f --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/ayu-dark.theme.css @@ -0,0 +1,214 @@ +:root { + /*editor - Ayu Dark theme*/ + --themeColor: #39bae6; + --themeColor90: rgba(57, 186, 230, 0.9); + --themeColor80: rgba(57, 186, 230, 0.8); + --themeColor70: rgba(57, 186, 230, 0.7); + --themeColor60: rgba(57, 186, 230, 0.6); + --themeColor50: rgba(57, 186, 230, 0.5); + --themeColor40: rgba(57, 186, 230, 0.4); + --themeColor30: rgba(57, 186, 230, 0.3); + --themeColor20: rgba(57, 186, 230, 0.2); + --themeColor10: rgba(57, 186, 230, 0.1); + + --highlightColor: rgba(57, 186, 230, 0.6); + --selectionColor: rgba(27, 38, 51, 0.8); + --editorColor: #b3b1ad; + --editorColor80: rgba(179, 177, 173, 0.8); + --editorColor60: rgba(179, 177, 173, 0.6); + --editorColor50: rgba(179, 177, 173, 0.5); + --editorColor40: rgba(179, 177, 173, 0.4); + --editorColor30: rgba(179, 177, 173, 0.3); + --editorColor10: rgba(179, 177, 173, 0.1); + --editorColor04: rgba(179, 177, 173, 0.04); + --editorBgColor: #0a0e14; + --deleteColor: #f07178; + --iconColor: rgba(179, 177, 173, 0.56); + --codeBgColor: #131721; + --codeBlockBgColor: #131721; + --footnoteBgColor: rgba(19, 23, 33, 0.3); + --inputBgColor: #050709; + + --focusColor: var(--themeColor); + + --buttonFontColor: rgba(179, 177, 173, 0.6); + --buttonBgColor: #131721; + --buttonBorder: 1px solid rgba(0, 0, 0, 0.2); + --buttonShadow: none; + --buttonFontColorHover: var(--buttonFontColor); + --buttonBgColorHover: #1b2230; + --buttonBorderHover: 1px solid rgba(0, 0, 0, 0.3); + --buttonFontColorActive: var(--buttonFontColor); + --buttonBgColorActive: #0d1118; + --buttonBorderActive: var(--buttonBorder); + + --buttonPrimaryFontColor: #0a0e14; + --buttonPrimaryBgColor: var(--themeColor); + --buttonPrimaryBorder: none; + --buttonPrimaryShadow: none; + --buttonPrimaryFontColorHover: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorHover: #59c2ff; + --buttonPrimaryBorderHover: var(--buttonPrimaryBorder); + --buttonPrimaryFontColorActive: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorActive: #2a9ac7; + --buttonPrimaryBorderActive: var(--buttonPrimaryBorder); + --buttonPrimaryFocusBorder: none; + --buttonPrimaryFocusShadow: inset 0 0 0 1px rgba(24, 26, 31, 0.5), 0 0 0 1px var(--themeColor); + --tableBorderColor: #131721; + + /* Markdown element colors */ + --headingColor: #e6e1cf; + --h1Color: #ff8f40; + --h2Color: #39bae6; + --h3Color: #c2d94c; + --h4Color: #59c2ff; + --h5Color: #f29668; + --h6Color: #f07178; + --blockquoteTextColor: rgba(179, 177, 173, 0.6); + --blockquoteBorderColor: #4d5566; + --hrColor: #1b2230; + --linkColor: #39bae6; + --strongColor: #e6e1cf; + --emColor: #ffb454; + --listMarkerColor: #ff8f40; + + /*marktext*/ + --sideBarColor: rgba(179, 177, 173, 0.6); + --sideBarIconColor: var(--iconColor); + --sideBarTitleColor: rgba(179, 177, 173, 0.8); + --sideBarTextColor: rgba(179, 177, 173, 0.4); + --sideBarBgColor: #070a0e; + --sideBarItemHoverBgColor: rgba(179, 177, 173, 0.03); + --itemBgColor: #131721; + + --floatFontColor: rgba(179, 177, 173, 0.7); + --floatBgColor: #131721; + --floatHoverColor: rgba(179, 177, 173, 0.04); + --floatBorderColor: rgba(0, 0, 0, 0.05); + --floatShadow: rgba(0, 0, 0, 0.2); + --maskColor: rgba(0, 0, 0, 0.7); + --editorAreaWidth: 750px; + + /* @muyajs/core (kebab-case) editor variables + Additive prep for the muyajs -> @muyajs/core editor swap. These + mirror the camelCase vars above; the legacy editor ignores them. + Do NOT remove the camelCase vars — they still drive the current + editor until editor.vue switches engines. */ + --theme-color: var(--themeColor); + --highlight-color: var(--highlightColor); + --selection-color: var(--selectionColor); + --editor-color: var(--editorColor); + --editor-color-80: var(--editorColor80); + --editor-color-50: var(--editorColor50); + --editor-color-30: var(--editorColor30); + --editor-color-10: var(--editorColor10); + --editor-color-04: var(--editorColor04); + --editor-bg-color: var(--editorBgColor); + --editor-area-width: var(--editorAreaWidth); + --delete-color: var(--deleteColor); + --icon-color: var(--iconColor); + --code-block-bg-color: var(--codeBlockBgColor); + --table-border-color: var(--tableBorderColor); + --input-bg-color: var(--inputBgColor); + --button-font-color: var(--buttonFontColor); + --button-bg-color: var(--buttonBgColor); + --button-border: var(--buttonBorder); + --button-bg-color-hover: var(--buttonBgColorHover); + --button-border-hover: var(--buttonBorderHover); + --button-bg-color-active: var(--buttonBgColorActive); + --button-border-active: var(--buttonBorderActive); + --button-border-focus: var(--button-border); + --float-bg-color: var(--floatBgColor); + --float-hover-color: var(--floatHoverColor); + --float-border-color: var(--floatBorderColor); + --float-shadow: rgb(15 15 15 / 3%) 0 0 0 1px, rgb(15 15 15 / 4%) 0 3px 6px, rgb(15 15 15 / 5%) 0 9px 24px; + --link-color: var(--linkColor); + --h1-color: var(--h1Color); + --h2-color: var(--h2Color); + --h3-color: var(--h3Color); + --h4-color: var(--h4Color); + --h5-color: var(--h5Color); + --h6-color: var(--h6Color); + --blockquote-text-color: var(--blockquoteTextColor); + --blockquote-border-color: var(--blockquoteBorderColor); + --strong-color: var(--strongColor); + --em-color: var(--emColor); + --list-marker-color: var(--listMarkerColor); + --hr-color: var(--hrColor); +} + +::-webkit-scrollbar { + background: var(--editorBgColor); +} + +.mu-front-menu, +.mu-float-wrapper { + box-shadow: 0 4px 8px 0 var(--floatShadow) !important; +} + +.title-bar .frameless-titlebar-button > div > svg { + fill: #b3b1ad; +} +.title-bar .frameless-titlebar-minimize:hover, +.title-bar .frameless-titlebar-toggle:hover { + background-color: rgba(179, 177, 173, 0.05); +} + +.side-bar { + border-right: 1px solid #030406 !important; +} + +.recent-files-projects a, +.open-project a { + box-shadow: none !important; +} + +.editor-tabs { + box-shadow: none !important; +} +.editor-tabs:after { + position: absolute; + content: ''; + border-bottom: 1px solid #030406; + bottom: 0; + left: 0; + right: 0; + z-index: 1; +} +.editor-tabs ul.tabs-container:after { + position: absolute; + content: ''; + border-bottom: 1px solid #030406; + bottom: 0; + left: 0; + right: 0; + z-index: 2; +} + +.tabs-container > li, +.tabs-container > li.active { + background: var(--editorBgColor) !important; +} + +.open-project button, +.recent-files-projects button { + box-shadow: none !important; +} + +/* ------------------------------------ */ + +:not(pre) > code[class*='language-'], +pre:not(.CodeMirror-line), +pre[class*='language-'], +pre.mu-code-block { + background: var(--codeBlockBgColor) !important; + border: none !important; +} +.mu-thematic-break:not(.mu-active)::before { + border-top: 2px dashed var(--editorColor10) !important; + background: none !important; +} +figure.mu-active.mu-math-block > div.mu-math-preview, +figure.mu-active.mu-diagram-block > div.mu-diagram-preview { + box-shadow: 0 3px 8px 0 var(--floatShadow) !important; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/ayu-light.theme.css b/packages/desktop/src/renderer/src/assets/themes/ayu-light.theme.css new file mode 100644 index 0000000000..59bf0e100e --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/ayu-light.theme.css @@ -0,0 +1,214 @@ +:root { + /*editor - Ayu Light theme*/ + --themeColor: #399ee6; + --themeColor90: rgba(57, 158, 230, 0.9); + --themeColor80: rgba(57, 158, 230, 0.8); + --themeColor70: rgba(57, 158, 230, 0.7); + --themeColor60: rgba(57, 158, 230, 0.6); + --themeColor50: rgba(57, 158, 230, 0.5); + --themeColor40: rgba(57, 158, 230, 0.4); + --themeColor30: rgba(57, 158, 230, 0.3); + --themeColor20: rgba(57, 158, 230, 0.2); + --themeColor10: rgba(57, 158, 230, 0.1); + + --highlightColor: rgba(57, 158, 230, 0.3); + --selectionColor: rgba(214, 226, 235, 0.8); + --editorColor: #575f66; + --editorColor80: rgba(87, 95, 102, 0.8); + --editorColor60: rgba(87, 95, 102, 0.6); + --editorColor50: rgba(87, 95, 102, 0.5); + --editorColor40: rgba(87, 95, 102, 0.4); + --editorColor30: rgba(87, 95, 102, 0.3); + --editorColor10: rgba(87, 95, 102, 0.1); + --editorColor04: rgba(87, 95, 102, 0.04); + --editorBgColor: #fafafa; + --deleteColor: #f07171; + --iconColor: rgba(87, 95, 102, 0.56); + --codeBgColor: #f0f0f0; + --codeBlockBgColor: #f0f0f0; + --footnoteBgColor: rgba(240, 240, 240, 0.5); + --inputBgColor: #f3f3f3; + + --focusColor: var(--themeColor); + + --buttonFontColor: rgba(87, 95, 102, 0.7); + --buttonBgColor: #f0f0f0; + --buttonBorder: 1px solid rgba(0, 0, 0, 0.08); + --buttonShadow: none; + --buttonFontColorHover: var(--buttonFontColor); + --buttonBgColorHover: #e5e5e5; + --buttonBorderHover: 1px solid rgba(0, 0, 0, 0.12); + --buttonFontColorActive: var(--buttonFontColor); + --buttonBgColorActive: #dbdbdb; + --buttonBorderActive: var(--buttonBorder); + + --buttonPrimaryFontColor: #ffffff; + --buttonPrimaryBgColor: var(--themeColor); + --buttonPrimaryBorder: none; + --buttonPrimaryShadow: none; + --buttonPrimaryFontColorHover: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorHover: #55b4fc; + --buttonPrimaryBorderHover: var(--buttonPrimaryBorder); + --buttonPrimaryFontColorActive: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorActive: #2a85c7; + --buttonPrimaryBorderActive: var(--buttonPrimaryBorder); + --buttonPrimaryFocusBorder: none; + --buttonPrimaryFocusShadow: inset 0 0 0 1px rgba(255, 255, 255, 0.5), 0 0 0 1px var(--themeColor); + --tableBorderColor: #dbdbdb; + + /* Markdown element colors */ + --headingColor: #575f66; + --h1Color: #fa8d3e; + --h2Color: #399ee6; + --h3Color: #86b300; + --h4Color: #55b4d4; + --h5Color: #a37acc; + --h6Color: #f07171; + --blockquoteTextColor: rgba(87, 95, 102, 0.6); + --blockquoteBorderColor: #abb0b6; + --hrColor: #dbdbdb; + --linkColor: #399ee6; + --strongColor: #575f66; + --emColor: #e6ba7e; + --listMarkerColor: #fa8d3e; + + /*marktext*/ + --sideBarColor: rgba(87, 95, 102, 0.7); + --sideBarIconColor: var(--iconColor); + --sideBarTitleColor: rgba(87, 95, 102, 0.9); + --sideBarTextColor: rgba(87, 95, 102, 0.5); + --sideBarBgColor: #f5f5f5; + --sideBarItemHoverBgColor: rgba(87, 95, 102, 0.05); + --itemBgColor: #e5e5e5; + + --floatFontColor: rgba(87, 95, 102, 0.8); + --floatBgColor: #f0f0f0; + --floatHoverColor: rgba(87, 95, 102, 0.06); + --floatBorderColor: rgba(0, 0, 0, 0.08); + --floatShadow: rgba(0, 0, 0, 0.1); + --maskColor: rgba(0, 0, 0, 0.3); + --editorAreaWidth: 750px; + + /* @muyajs/core (kebab-case) editor variables + Additive prep for the muyajs -> @muyajs/core editor swap. These + mirror the camelCase vars above; the legacy editor ignores them. + Do NOT remove the camelCase vars — they still drive the current + editor until editor.vue switches engines. */ + --theme-color: var(--themeColor); + --highlight-color: var(--highlightColor); + --selection-color: var(--selectionColor); + --editor-color: var(--editorColor); + --editor-color-80: var(--editorColor80); + --editor-color-50: var(--editorColor50); + --editor-color-30: var(--editorColor30); + --editor-color-10: var(--editorColor10); + --editor-color-04: var(--editorColor04); + --editor-bg-color: var(--editorBgColor); + --editor-area-width: var(--editorAreaWidth); + --delete-color: var(--deleteColor); + --icon-color: var(--iconColor); + --code-block-bg-color: var(--codeBlockBgColor); + --table-border-color: var(--tableBorderColor); + --input-bg-color: var(--inputBgColor); + --button-font-color: var(--buttonFontColor); + --button-bg-color: var(--buttonBgColor); + --button-border: var(--buttonBorder); + --button-bg-color-hover: var(--buttonBgColorHover); + --button-border-hover: var(--buttonBorderHover); + --button-bg-color-active: var(--buttonBgColorActive); + --button-border-active: var(--buttonBorderActive); + --button-border-focus: var(--button-border); + --float-bg-color: var(--floatBgColor); + --float-hover-color: var(--floatHoverColor); + --float-border-color: var(--floatBorderColor); + --float-shadow: rgb(15 15 15 / 3%) 0 0 0 1px, rgb(15 15 15 / 4%) 0 3px 6px, rgb(15 15 15 / 5%) 0 9px 24px; + --link-color: var(--linkColor); + --h1-color: var(--h1Color); + --h2-color: var(--h2Color); + --h3-color: var(--h3Color); + --h4-color: var(--h4Color); + --h5-color: var(--h5Color); + --h6-color: var(--h6Color); + --blockquote-text-color: var(--blockquoteTextColor); + --blockquote-border-color: var(--blockquoteBorderColor); + --strong-color: var(--strongColor); + --em-color: var(--emColor); + --list-marker-color: var(--listMarkerColor); + --hr-color: var(--hrColor); +} + +::-webkit-scrollbar { + background: var(--editorBgColor); +} + +.mu-front-menu, +.mu-float-wrapper { + box-shadow: 0 4px 8px 0 var(--floatShadow) !important; +} + +.title-bar .frameless-titlebar-button > div > svg { + fill: #575f66; +} +.title-bar .frameless-titlebar-minimize:hover, +.title-bar .frameless-titlebar-toggle:hover { + background-color: rgba(87, 95, 102, 0.08); +} + +.side-bar { + border-right: 1px solid #dbdbdb !important; +} + +.recent-files-projects a, +.open-project a { + box-shadow: none !important; +} + +.editor-tabs { + box-shadow: none !important; +} +.editor-tabs:after { + position: absolute; + content: ''; + border-bottom: 1px solid #dbdbdb; + bottom: 0; + left: 0; + right: 0; + z-index: 1; +} +.editor-tabs ul.tabs-container:after { + position: absolute; + content: ''; + border-bottom: 1px solid #dbdbdb; + bottom: 0; + left: 0; + right: 0; + z-index: 2; +} + +.tabs-container > li, +.tabs-container > li.active { + background: var(--editorBgColor) !important; +} + +.open-project button, +.recent-files-projects button { + box-shadow: none !important; +} + +/* ------------------------------------ */ + +:not(pre) > code[class*='language-'], +pre:not(.CodeMirror-line), +pre[class*='language-'], +pre.mu-code-block { + background: var(--codeBlockBgColor) !important; + border: none !important; +} +.mu-thematic-break:not(.mu-active)::before { + border-top: 2px dashed var(--editorColor10) !important; + background: none !important; +} +figure.mu-active.mu-math-block > div.mu-math-preview, +figure.mu-active.mu-diagram-block > div.mu-diagram-preview { + box-shadow: 0 3px 8px 0 var(--floatShadow) !important; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/ayu-mirage.theme.css b/packages/desktop/src/renderer/src/assets/themes/ayu-mirage.theme.css new file mode 100644 index 0000000000..1ed1ab7c29 --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/ayu-mirage.theme.css @@ -0,0 +1,214 @@ +:root { + /*editor - Ayu Mirage theme*/ + --themeColor: #ffcc66; + --themeColor90: rgba(255, 204, 102, 0.9); + --themeColor80: rgba(255, 204, 102, 0.8); + --themeColor70: rgba(255, 204, 102, 0.7); + --themeColor60: rgba(255, 204, 102, 0.6); + --themeColor50: rgba(255, 204, 102, 0.5); + --themeColor40: rgba(255, 204, 102, 0.4); + --themeColor30: rgba(255, 204, 102, 0.3); + --themeColor20: rgba(255, 204, 102, 0.2); + --themeColor10: rgba(255, 204, 102, 0.1); + + --highlightColor: rgba(255, 204, 102, 0.6); + --selectionColor: rgba(52, 68, 88, 0.8); + --editorColor: #cbccc6; + --editorColor80: rgba(203, 204, 198, 0.8); + --editorColor60: rgba(203, 204, 198, 0.6); + --editorColor50: rgba(203, 204, 198, 0.5); + --editorColor40: rgba(203, 204, 198, 0.4); + --editorColor30: rgba(203, 204, 198, 0.3); + --editorColor10: rgba(203, 204, 198, 0.1); + --editorColor04: rgba(203, 204, 198, 0.04); + --editorBgColor: #1f2430; + --deleteColor: #f28779; + --iconColor: rgba(203, 204, 198, 0.56); + --codeBgColor: #272d38; + --codeBlockBgColor: #272d38; + --footnoteBgColor: rgba(39, 45, 56, 0.3); + --inputBgColor: #171b24; + + --focusColor: var(--themeColor); + + --buttonFontColor: rgba(203, 204, 198, 0.6); + --buttonBgColor: #272d38; + --buttonBorder: 1px solid rgba(0, 0, 0, 0.2); + --buttonShadow: none; + --buttonFontColorHover: var(--buttonFontColor); + --buttonBgColorHover: #323a48; + --buttonBorderHover: 1px solid rgba(0, 0, 0, 0.3); + --buttonFontColorActive: var(--buttonFontColor); + --buttonBgColorActive: #1f262f; + --buttonBorderActive: var(--buttonBorder); + + --buttonPrimaryFontColor: #1f2430; + --buttonPrimaryBgColor: var(--themeColor); + --buttonPrimaryBorder: none; + --buttonPrimaryShadow: none; + --buttonPrimaryFontColorHover: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorHover: #ffd580; + --buttonPrimaryBorderHover: var(--buttonPrimaryBorder); + --buttonPrimaryFontColorActive: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorActive: #e6b84d; + --buttonPrimaryBorderActive: var(--buttonPrimaryBorder); + --buttonPrimaryFocusBorder: none; + --buttonPrimaryFocusShadow: inset 0 0 0 1px rgba(24, 26, 31, 0.5), 0 0 0 1px var(--themeColor); + --tableBorderColor: #272d38; + + /* Markdown element colors */ + --headingColor: #cccac2; + --h1Color: #ffcc66; + --h2Color: #73d0ff; + --h3Color: #bae67e; + --h4Color: #5ccfe6; + --h5Color: #ffd580; + --h6Color: #f28779; + --blockquoteTextColor: rgba(203, 204, 198, 0.6); + --blockquoteBorderColor: #5c6773; + --hrColor: #323a48; + --linkColor: #73d0ff; + --strongColor: #cccac2; + --emColor: #ffd580; + --listMarkerColor: #ffcc66; + + /*marktext*/ + --sideBarColor: rgba(203, 204, 198, 0.6); + --sideBarIconColor: var(--iconColor); + --sideBarTitleColor: rgba(203, 204, 198, 0.8); + --sideBarTextColor: rgba(203, 204, 198, 0.4); + --sideBarBgColor: #1b1f2a; + --sideBarItemHoverBgColor: rgba(203, 204, 198, 0.03); + --itemBgColor: #272d38; + + --floatFontColor: rgba(203, 204, 198, 0.7); + --floatBgColor: #272d38; + --floatHoverColor: rgba(203, 204, 198, 0.04); + --floatBorderColor: rgba(0, 0, 0, 0.05); + --floatShadow: rgba(0, 0, 0, 0.2); + --maskColor: rgba(0, 0, 0, 0.7); + --editorAreaWidth: 750px; + + /* @muyajs/core (kebab-case) editor variables + Additive prep for the muyajs -> @muyajs/core editor swap. These + mirror the camelCase vars above; the legacy editor ignores them. + Do NOT remove the camelCase vars — they still drive the current + editor until editor.vue switches engines. */ + --theme-color: var(--themeColor); + --highlight-color: var(--highlightColor); + --selection-color: var(--selectionColor); + --editor-color: var(--editorColor); + --editor-color-80: var(--editorColor80); + --editor-color-50: var(--editorColor50); + --editor-color-30: var(--editorColor30); + --editor-color-10: var(--editorColor10); + --editor-color-04: var(--editorColor04); + --editor-bg-color: var(--editorBgColor); + --editor-area-width: var(--editorAreaWidth); + --delete-color: var(--deleteColor); + --icon-color: var(--iconColor); + --code-block-bg-color: var(--codeBlockBgColor); + --table-border-color: var(--tableBorderColor); + --input-bg-color: var(--inputBgColor); + --button-font-color: var(--buttonFontColor); + --button-bg-color: var(--buttonBgColor); + --button-border: var(--buttonBorder); + --button-bg-color-hover: var(--buttonBgColorHover); + --button-border-hover: var(--buttonBorderHover); + --button-bg-color-active: var(--buttonBgColorActive); + --button-border-active: var(--buttonBorderActive); + --button-border-focus: var(--button-border); + --float-bg-color: var(--floatBgColor); + --float-hover-color: var(--floatHoverColor); + --float-border-color: var(--floatBorderColor); + --float-shadow: rgb(15 15 15 / 3%) 0 0 0 1px, rgb(15 15 15 / 4%) 0 3px 6px, rgb(15 15 15 / 5%) 0 9px 24px; + --link-color: var(--linkColor); + --h1-color: var(--h1Color); + --h2-color: var(--h2Color); + --h3-color: var(--h3Color); + --h4-color: var(--h4Color); + --h5-color: var(--h5Color); + --h6-color: var(--h6Color); + --blockquote-text-color: var(--blockquoteTextColor); + --blockquote-border-color: var(--blockquoteBorderColor); + --strong-color: var(--strongColor); + --em-color: var(--emColor); + --list-marker-color: var(--listMarkerColor); + --hr-color: var(--hrColor); +} + +::-webkit-scrollbar { + background: var(--editorBgColor); +} + +.mu-front-menu, +.mu-float-wrapper { + box-shadow: 0 4px 8px 0 var(--floatShadow) !important; +} + +.title-bar .frameless-titlebar-button > div > svg { + fill: #cbccc6; +} +.title-bar .frameless-titlebar-minimize:hover, +.title-bar .frameless-titlebar-toggle:hover { + background-color: rgba(203, 204, 198, 0.05); +} + +.side-bar { + border-right: 1px solid #12151c !important; +} + +.recent-files-projects a, +.open-project a { + box-shadow: none !important; +} + +.editor-tabs { + box-shadow: none !important; +} +.editor-tabs:after { + position: absolute; + content: ''; + border-bottom: 1px solid #12151c; + bottom: 0; + left: 0; + right: 0; + z-index: 1; +} +.editor-tabs ul.tabs-container:after { + position: absolute; + content: ''; + border-bottom: 1px solid #12151c; + bottom: 0; + left: 0; + right: 0; + z-index: 2; +} + +.tabs-container > li, +.tabs-container > li.active { + background: var(--editorBgColor) !important; +} + +.open-project button, +.recent-files-projects button { + box-shadow: none !important; +} + +/* ------------------------------------ */ + +:not(pre) > code[class*='language-'], +pre:not(.CodeMirror-line), +pre[class*='language-'], +pre.mu-code-block { + background: var(--codeBlockBgColor) !important; + border: none !important; +} +.mu-thematic-break:not(.mu-active)::before { + border-top: 2px dashed var(--editorColor10) !important; + background: none !important; +} +figure.mu-active.mu-math-block > div.mu-math-preview, +figure.mu-active.mu-diagram-block > div.mu-diagram-preview { + box-shadow: 0 3px 8px 0 var(--floatShadow) !important; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/catppuccin-latte.theme.css b/packages/desktop/src/renderer/src/assets/themes/catppuccin-latte.theme.css new file mode 100644 index 0000000000..ab0952f863 --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/catppuccin-latte.theme.css @@ -0,0 +1,214 @@ +:root { + /*editor - Catppuccin Latte theme*/ + --themeColor: #1e66f5; + --themeColor90: rgba(30, 102, 245, 0.9); + --themeColor80: rgba(30, 102, 245, 0.8); + --themeColor70: rgba(30, 102, 245, 0.7); + --themeColor60: rgba(30, 102, 245, 0.6); + --themeColor50: rgba(30, 102, 245, 0.5); + --themeColor40: rgba(30, 102, 245, 0.4); + --themeColor30: rgba(30, 102, 245, 0.3); + --themeColor20: rgba(30, 102, 245, 0.2); + --themeColor10: rgba(30, 102, 245, 0.1); + + --highlightColor: rgba(30, 102, 245, 0.3); + --selectionColor: rgba(172, 176, 190, 0.5); + --editorColor: #4c4f69; + --editorColor80: rgba(76, 79, 105, 0.8); + --editorColor60: rgba(76, 79, 105, 0.6); + --editorColor50: rgba(76, 79, 105, 0.5); + --editorColor40: rgba(76, 79, 105, 0.4); + --editorColor30: rgba(76, 79, 105, 0.3); + --editorColor10: rgba(76, 79, 105, 0.1); + --editorColor04: rgba(76, 79, 105, 0.04); + --editorBgColor: #eff1f5; + --deleteColor: #d20f39; + --iconColor: rgba(76, 79, 105, 0.56); + --codeBgColor: #e6e9ef; + --codeBlockBgColor: #e6e9ef; + --footnoteBgColor: rgba(230, 233, 239, 0.5); + --inputBgColor: #dce0e8; + + --focusColor: var(--themeColor); + + --buttonFontColor: rgba(76, 79, 105, 0.7); + --buttonBgColor: #e6e9ef; + --buttonBorder: 1px solid rgba(0, 0, 0, 0.08); + --buttonShadow: none; + --buttonFontColorHover: var(--buttonFontColor); + --buttonBgColorHover: #dce0e8; + --buttonBorderHover: 1px solid rgba(0, 0, 0, 0.12); + --buttonFontColorActive: var(--buttonFontColor); + --buttonBgColorActive: #ccd0da; + --buttonBorderActive: var(--buttonBorder); + + --buttonPrimaryFontColor: #ffffff; + --buttonPrimaryBgColor: var(--themeColor); + --buttonPrimaryBorder: none; + --buttonPrimaryShadow: none; + --buttonPrimaryFontColorHover: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorHover: #7287fd; + --buttonPrimaryBorderHover: var(--buttonPrimaryBorder); + --buttonPrimaryFontColorActive: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorActive: #1852d6; + --buttonPrimaryBorderActive: var(--buttonPrimaryBorder); + --buttonPrimaryFocusBorder: none; + --buttonPrimaryFocusShadow: inset 0 0 0 1px rgba(255, 255, 255, 0.5), 0 0 0 1px var(--themeColor); + --tableBorderColor: #ccd0da; + + /* Markdown element colors */ + --headingColor: #4c4f69; + --h1Color: #d20f39; + --h2Color: #fe640b; + --h3Color: #df8e1d; + --h4Color: #40a02b; + --h5Color: #1e66f5; + --h6Color: #8839ef; + --blockquoteTextColor: rgba(76, 79, 105, 0.6); + --blockquoteBorderColor: #9ca0b0; + --hrColor: #ccd0da; + --linkColor: #1e66f5; + --strongColor: #4c4f69; + --emColor: #ea76cb; + --listMarkerColor: #7287fd; + + /*marktext*/ + --sideBarColor: rgba(76, 79, 105, 0.7); + --sideBarIconColor: var(--iconColor); + --sideBarTitleColor: rgba(76, 79, 105, 0.9); + --sideBarTextColor: rgba(76, 79, 105, 0.5); + --sideBarBgColor: #eaedf2; + --sideBarItemHoverBgColor: rgba(76, 79, 105, 0.05); + --itemBgColor: #dce0e8; + + --floatFontColor: rgba(76, 79, 105, 0.8); + --floatBgColor: #e6e9ef; + --floatHoverColor: rgba(76, 79, 105, 0.06); + --floatBorderColor: rgba(0, 0, 0, 0.08); + --floatShadow: rgba(0, 0, 0, 0.1); + --maskColor: rgba(0, 0, 0, 0.3); + --editorAreaWidth: 750px; + + /* @muyajs/core (kebab-case) editor variables + Additive prep for the muyajs -> @muyajs/core editor swap. These + mirror the camelCase vars above; the legacy editor ignores them. + Do NOT remove the camelCase vars — they still drive the current + editor until editor.vue switches engines. */ + --theme-color: var(--themeColor); + --highlight-color: var(--highlightColor); + --selection-color: var(--selectionColor); + --editor-color: var(--editorColor); + --editor-color-80: var(--editorColor80); + --editor-color-50: var(--editorColor50); + --editor-color-30: var(--editorColor30); + --editor-color-10: var(--editorColor10); + --editor-color-04: var(--editorColor04); + --editor-bg-color: var(--editorBgColor); + --editor-area-width: var(--editorAreaWidth); + --delete-color: var(--deleteColor); + --icon-color: var(--iconColor); + --code-block-bg-color: var(--codeBlockBgColor); + --table-border-color: var(--tableBorderColor); + --input-bg-color: var(--inputBgColor); + --button-font-color: var(--buttonFontColor); + --button-bg-color: var(--buttonBgColor); + --button-border: var(--buttonBorder); + --button-bg-color-hover: var(--buttonBgColorHover); + --button-border-hover: var(--buttonBorderHover); + --button-bg-color-active: var(--buttonBgColorActive); + --button-border-active: var(--buttonBorderActive); + --button-border-focus: var(--button-border); + --float-bg-color: var(--floatBgColor); + --float-hover-color: var(--floatHoverColor); + --float-border-color: var(--floatBorderColor); + --float-shadow: rgb(15 15 15 / 3%) 0 0 0 1px, rgb(15 15 15 / 4%) 0 3px 6px, rgb(15 15 15 / 5%) 0 9px 24px; + --link-color: var(--linkColor); + --h1-color: var(--h1Color); + --h2-color: var(--h2Color); + --h3-color: var(--h3Color); + --h4-color: var(--h4Color); + --h5-color: var(--h5Color); + --h6-color: var(--h6Color); + --blockquote-text-color: var(--blockquoteTextColor); + --blockquote-border-color: var(--blockquoteBorderColor); + --strong-color: var(--strongColor); + --em-color: var(--emColor); + --list-marker-color: var(--listMarkerColor); + --hr-color: var(--hrColor); +} + +::-webkit-scrollbar { + background: var(--editorBgColor); +} + +.mu-front-menu, +.mu-float-wrapper { + box-shadow: 0 4px 8px 0 var(--floatShadow) !important; +} + +.title-bar .frameless-titlebar-button > div > svg { + fill: #4c4f69; +} +.title-bar .frameless-titlebar-minimize:hover, +.title-bar .frameless-titlebar-toggle:hover { + background-color: rgba(76, 79, 105, 0.08); +} + +.side-bar { + border-right: 1px solid #ccd0da !important; +} + +.recent-files-projects a, +.open-project a { + box-shadow: none !important; +} + +.editor-tabs { + box-shadow: none !important; +} +.editor-tabs:after { + position: absolute; + content: ''; + border-bottom: 1px solid #ccd0da; + bottom: 0; + left: 0; + right: 0; + z-index: 1; +} +.editor-tabs ul.tabs-container:after { + position: absolute; + content: ''; + border-bottom: 1px solid #ccd0da; + bottom: 0; + left: 0; + right: 0; + z-index: 2; +} + +.tabs-container > li, +.tabs-container > li.active { + background: var(--editorBgColor) !important; +} + +.open-project button, +.recent-files-projects button { + box-shadow: none !important; +} + +/* ------------------------------------ */ + +:not(pre) > code[class*='language-'], +pre:not(.CodeMirror-line), +pre[class*='language-'], +pre.mu-code-block { + background: var(--codeBlockBgColor) !important; + border: none !important; +} +.mu-thematic-break:not(.mu-active)::before { + border-top: 2px dashed var(--editorColor10) !important; + background: none !important; +} +figure.mu-active.mu-math-block > div.mu-math-preview, +figure.mu-active.mu-diagram-block > div.mu-diagram-preview { + box-shadow: 0 3px 8px 0 var(--floatShadow) !important; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/catppuccin-mocha.theme.css b/packages/desktop/src/renderer/src/assets/themes/catppuccin-mocha.theme.css new file mode 100644 index 0000000000..ef5fb26086 --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/catppuccin-mocha.theme.css @@ -0,0 +1,214 @@ +:root { + /*editor - Catppuccin Mocha theme*/ + --themeColor: #89b4fa; + --themeColor90: rgba(137, 180, 250, 0.9); + --themeColor80: rgba(137, 180, 250, 0.8); + --themeColor70: rgba(137, 180, 250, 0.7); + --themeColor60: rgba(137, 180, 250, 0.6); + --themeColor50: rgba(137, 180, 250, 0.5); + --themeColor40: rgba(137, 180, 250, 0.4); + --themeColor30: rgba(137, 180, 250, 0.3); + --themeColor20: rgba(137, 180, 250, 0.2); + --themeColor10: rgba(137, 180, 250, 0.1); + + --highlightColor: rgba(137, 180, 250, 0.6); + --selectionColor: rgba(88, 91, 112, 0.6); + --editorColor: #cdd6f4; + --editorColor80: rgba(205, 214, 244, 0.8); + --editorColor60: rgba(205, 214, 244, 0.6); + --editorColor50: rgba(205, 214, 244, 0.5); + --editorColor40: rgba(205, 214, 244, 0.4); + --editorColor30: rgba(205, 214, 244, 0.3); + --editorColor10: rgba(205, 214, 244, 0.1); + --editorColor04: rgba(205, 214, 244, 0.04); + --editorBgColor: #1e1e2e; + --deleteColor: #f38ba8; + --iconColor: rgba(205, 214, 244, 0.56); + --codeBgColor: #313244; + --codeBlockBgColor: #313244; + --footnoteBgColor: rgba(49, 50, 68, 0.3); + --inputBgColor: #181825; + + --focusColor: var(--themeColor); + + --buttonFontColor: rgba(205, 214, 244, 0.6); + --buttonBgColor: #313244; + --buttonBorder: 1px solid rgba(0, 0, 0, 0.2); + --buttonShadow: none; + --buttonFontColorHover: var(--buttonFontColor); + --buttonBgColorHover: #45475a; + --buttonBorderHover: 1px solid rgba(0, 0, 0, 0.3); + --buttonFontColorActive: var(--buttonFontColor); + --buttonBgColorActive: #2a2a3c; + --buttonBorderActive: var(--buttonBorder); + + --buttonPrimaryFontColor: #1e1e2e; + --buttonPrimaryBgColor: var(--themeColor); + --buttonPrimaryBorder: none; + --buttonPrimaryShadow: none; + --buttonPrimaryFontColorHover: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorHover: #b4befe; + --buttonPrimaryBorderHover: var(--buttonPrimaryBorder); + --buttonPrimaryFontColorActive: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorActive: #74c7ec; + --buttonPrimaryBorderActive: var(--buttonPrimaryBorder); + --buttonPrimaryFocusBorder: none; + --buttonPrimaryFocusShadow: inset 0 0 0 1px rgba(24, 26, 31, 0.5), 0 0 0 1px var(--themeColor); + --tableBorderColor: #313244; + + /* Markdown element colors */ + --headingColor: #cdd6f4; + --h1Color: #cba6f7; + --h2Color: #89b4fa; + --h3Color: #94e2d5; + --h4Color: #a6e3a1; + --h5Color: #f9e2af; + --h6Color: #fab387; + --blockquoteTextColor: rgba(205, 214, 244, 0.6); + --blockquoteBorderColor: #585b70; + --hrColor: #45475a; + --linkColor: #89dceb; + --strongColor: #cdd6f4; + --emColor: #f5c2e7; + --listMarkerColor: #cba6f7; + + /*marktext*/ + --sideBarColor: rgba(205, 214, 244, 0.6); + --sideBarIconColor: var(--iconColor); + --sideBarTitleColor: rgba(205, 214, 244, 0.8); + --sideBarTextColor: rgba(205, 214, 244, 0.4); + --sideBarBgColor: #1b1b29; + --sideBarItemHoverBgColor: rgba(205, 214, 244, 0.03); + --itemBgColor: #313244; + + --floatFontColor: rgba(205, 214, 244, 0.7); + --floatBgColor: #313244; + --floatHoverColor: rgba(205, 214, 244, 0.04); + --floatBorderColor: rgba(0, 0, 0, 0.05); + --floatShadow: rgba(0, 0, 0, 0.2); + --maskColor: rgba(0, 0, 0, 0.7); + --editorAreaWidth: 750px; + + /* @muyajs/core (kebab-case) editor variables + Additive prep for the muyajs -> @muyajs/core editor swap. These + mirror the camelCase vars above; the legacy editor ignores them. + Do NOT remove the camelCase vars — they still drive the current + editor until editor.vue switches engines. */ + --theme-color: var(--themeColor); + --highlight-color: var(--highlightColor); + --selection-color: var(--selectionColor); + --editor-color: var(--editorColor); + --editor-color-80: var(--editorColor80); + --editor-color-50: var(--editorColor50); + --editor-color-30: var(--editorColor30); + --editor-color-10: var(--editorColor10); + --editor-color-04: var(--editorColor04); + --editor-bg-color: var(--editorBgColor); + --editor-area-width: var(--editorAreaWidth); + --delete-color: var(--deleteColor); + --icon-color: var(--iconColor); + --code-block-bg-color: var(--codeBlockBgColor); + --table-border-color: var(--tableBorderColor); + --input-bg-color: var(--inputBgColor); + --button-font-color: var(--buttonFontColor); + --button-bg-color: var(--buttonBgColor); + --button-border: var(--buttonBorder); + --button-bg-color-hover: var(--buttonBgColorHover); + --button-border-hover: var(--buttonBorderHover); + --button-bg-color-active: var(--buttonBgColorActive); + --button-border-active: var(--buttonBorderActive); + --button-border-focus: var(--button-border); + --float-bg-color: var(--floatBgColor); + --float-hover-color: var(--floatHoverColor); + --float-border-color: var(--floatBorderColor); + --float-shadow: rgb(15 15 15 / 3%) 0 0 0 1px, rgb(15 15 15 / 4%) 0 3px 6px, rgb(15 15 15 / 5%) 0 9px 24px; + --link-color: var(--linkColor); + --h1-color: var(--h1Color); + --h2-color: var(--h2Color); + --h3-color: var(--h3Color); + --h4-color: var(--h4Color); + --h5-color: var(--h5Color); + --h6-color: var(--h6Color); + --blockquote-text-color: var(--blockquoteTextColor); + --blockquote-border-color: var(--blockquoteBorderColor); + --strong-color: var(--strongColor); + --em-color: var(--emColor); + --list-marker-color: var(--listMarkerColor); + --hr-color: var(--hrColor); +} + +::-webkit-scrollbar { + background: var(--editorBgColor); +} + +.mu-front-menu, +.mu-float-wrapper { + box-shadow: 0 4px 8px 0 var(--floatShadow) !important; +} + +.title-bar .frameless-titlebar-button > div > svg { + fill: #cdd6f4; +} +.title-bar .frameless-titlebar-minimize:hover, +.title-bar .frameless-titlebar-toggle:hover { + background-color: rgba(205, 214, 244, 0.05); +} + +.side-bar { + border-right: 1px solid #11111b !important; +} + +.recent-files-projects a, +.open-project a { + box-shadow: none !important; +} + +.editor-tabs { + box-shadow: none !important; +} +.editor-tabs:after { + position: absolute; + content: ''; + border-bottom: 1px solid #11111b; + bottom: 0; + left: 0; + right: 0; + z-index: 1; +} +.editor-tabs ul.tabs-container:after { + position: absolute; + content: ''; + border-bottom: 1px solid #11111b; + bottom: 0; + left: 0; + right: 0; + z-index: 2; +} + +.tabs-container > li, +.tabs-container > li.active { + background: var(--editorBgColor) !important; +} + +.open-project button, +.recent-files-projects button { + box-shadow: none !important; +} + +/* ------------------------------------ */ + +:not(pre) > code[class*='language-'], +pre:not(.CodeMirror-line), +pre[class*='language-'], +pre.mu-code-block { + background: var(--codeBlockBgColor) !important; + border: none !important; +} +.mu-thematic-break:not(.mu-active)::before { + border-top: 2px dashed var(--editorColor10) !important; + background: none !important; +} +figure.mu-active.mu-math-block > div.mu-math-preview, +figure.mu-active.mu-diagram-block > div.mu-diagram-preview { + box-shadow: 0 3px 8px 0 var(--floatShadow) !important; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/codemirror/one-dark.css b/packages/desktop/src/renderer/src/assets/themes/codemirror/one-dark.css new file mode 100644 index 0000000000..36d7059d06 --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/codemirror/one-dark.css @@ -0,0 +1,185 @@ +/* + Name: one-dark 1.1.1 + Author: Török Ádám (http://github.com/Aerobird98) + Original Atom One Dark Theme (https://github.com/atom/one-dark-ui & https://github.com/atom/one-dark-syntax) + +MIT License + +Copyright (c) 2016-2017 Aerobird98 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +/* basic */ +.cm-s-one-dark { + font-family: Menlo, Consolas, 'DejaVu Sans Mono', monospace; + font-weight: 350; + font-size: 18px; + color: #abb2bf; + background-color: #282c34; +} +.cm-s-one-dark .CodeMirror-selected { + background-color: #3e4451; +} +.cm-s-one-dark .CodeMirror-gutter, +.cm-s-one-dark .CodeMirror-gutters { + border: none; + background-color: #282c34; +} +.cm-s-one-dark .CodeMirror-linenumber, +.cm-s-one-dark .CodeMirror-linenumbers { + color: #5c6370 !important; + background-color: transparent; +} +.cm-s-one-dark .CodeMirror-lines { + color: #abb2bf !important; + background-color: transparent; +} +.cm-s-one-dark .CodeMirror-cursor { + border-left: 2px solid #56b6c2 !important; +} +/* addon: edit/machingbrackets.js & addon: edit/matchtags.js */ +.cm-s-one-dark .CodeMirror-matchingbracket, +.cm-s-one-dark .CodeMirror-matchingtag { + border-bottom: 2px solid #56b6c2; + color: #abb2bf !important; + background-color: transparent; +} +.cm-s-one-dark .CodeMirror-nonmatchingbracket { + border-bottom: 2px solid #e06c75; + color: #abb2bf !important; + background-color: transparent; +} +/* addon: fold/foldgutter.js */ +.cm-s-one-dark .CodeMirror-foldmarker, +.cm-s-one-dark .CodeMirror-foldgutter, +.cm-s-one-dark .CodeMirror-foldgutter-open, +.cm-s-one-dark .CodeMirror-foldgutter-folded { + border: none; + text-shadow: none; + color: #5c6370 !important; + background-color: transparent; +} +/* addon: selection/active-line.js */ +.cm-s-one-dark .CodeMirror-activeline-background { + background-color: rgba(153, 187, 255, 0.04); +} +/* basic syntax */ +.cm-s-one-dark .cm-header { + color: #e06c75; +} +.cm-s-one-dark .cm-quote { + color: #5c6370; + font-style: italic; +} +.cm-s-one-dark .cm-negative { + color: #e06c75; +} +.cm-s-one-dark .cm-positive { + color: #e06c75; +} +.cm-s-one-dark .cm-strong { + color: #d19a66; + font-weight: bold; +} +.cm-s-one-dark .cm-header .cm-strong { + color: #d19a66; + font-weight: bold; +} +.cm-s-one-dark .cm-em { + color: #c678dd; + font-style: italic; +} +.cm-s-one-dark .cm-header .cm-em { + color: #c678dd; + font-style: italic; +} +.cm-s-one-dark .cm-tag { + color: #e06c75; +} +.cm-s-one-dark .cm-attribute { + color: #d19a66; +} +.cm-s-one-dark .cm-link { + color: #98c379; + border-bottom: solid 1px #98c379; +} +.cm-s-one-dark .cm-builtin { + color: #e06c75; +} +.cm-s-one-dark .cm-keyword { + color: #c678dd; +} +.cm-s-one-dark .cm-def { + color: #e5c07b; +} /* original: #d19a66; */ +.cm-s-one-dark .cm-atom { + color: #d19a66; +} +.cm-s-one-dark .cm-number { + color: #d19a66; +} +.cm-s-one-dark .cm-property { + color: #56b6c2; +} /* original: #abb2bf */ +.cm-s-one-dark .cm-qualifier { + color: #d19a66; +} +.cm-s-one-dark .cm-variable { + color: #e06c75; +} +.cm-s-one-dark .cm-string { + color: #98c379; +} +.cm-s-one-dark .cm-punctuation { + color: #abb2bf; +} +.cm-s-one-dark .cm-operator { + color: #56b6c2; +} /* original: #abb2bf */ +.cm-s-one-dark .cm-meta { + color: #abb2bf; +} +.cm-s-one-dark .cm-bracket { + color: #abb2bf; +} +.cm-s-one-dark .cm-comment { + color: #5c6370; + font-style: italic; +} +.cm-s-one-dark .cm-error { + color: #e06c75; +} +/* css syntax corrections */ +.cm-s-one-dark .cm-m-css.cm-variable { + color: #828997; +} +.cm-s-one-dark .cm-m-css.cm-property { + color: #abb2bf; +} +.cm-s-one-dark .cm-m-css.cm-atom { + color: #56b6c2; +} +.cm-s-one-dark .cm-m-css.cm-builtin { + color: #56b6c2; +} +/* lua syntax corrections */ +.cm-s-one-dark .cm-m-lua.cm-variable { + color: #56b6c2; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/cyberdream.theme.css b/packages/desktop/src/renderer/src/assets/themes/cyberdream.theme.css new file mode 100644 index 0000000000..3ccf93f85d --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/cyberdream.theme.css @@ -0,0 +1,214 @@ +:root { + /*editor - Cyberdream theme*/ + --themeColor: #5ea1ff; + --themeColor90: rgba(94, 161, 255, 0.9); + --themeColor80: rgba(94, 161, 255, 0.8); + --themeColor70: rgba(94, 161, 255, 0.7); + --themeColor60: rgba(94, 161, 255, 0.6); + --themeColor50: rgba(94, 161, 255, 0.5); + --themeColor40: rgba(94, 161, 255, 0.4); + --themeColor30: rgba(94, 161, 255, 0.3); + --themeColor20: rgba(94, 161, 255, 0.2); + --themeColor10: rgba(94, 161, 255, 0.1); + + --highlightColor: rgba(94, 161, 255, 0.6); + --selectionColor: rgba(45, 48, 53, 0.8); + --editorColor: #ffffff; + --editorColor80: rgba(255, 255, 255, 0.8); + --editorColor60: rgba(255, 255, 255, 0.6); + --editorColor50: rgba(255, 255, 255, 0.5); + --editorColor40: rgba(255, 255, 255, 0.4); + --editorColor30: rgba(255, 255, 255, 0.3); + --editorColor10: rgba(255, 255, 255, 0.1); + --editorColor04: rgba(255, 255, 255, 0.04); + --editorBgColor: #16181a; + --deleteColor: #ff6e5e; + --iconColor: rgba(255, 255, 255, 0.56); + --codeBgColor: #1e2124; + --codeBlockBgColor: #1e2124; + --footnoteBgColor: rgba(30, 33, 36, 0.3); + --inputBgColor: #0e1012; + + --focusColor: var(--themeColor); + + --buttonFontColor: rgba(255, 255, 255, 0.6); + --buttonBgColor: #1e2124; + --buttonBorder: 1px solid rgba(0, 0, 0, 0.2); + --buttonShadow: none; + --buttonFontColorHover: var(--buttonFontColor); + --buttonBgColorHover: #2a2e32; + --buttonBorderHover: 1px solid rgba(0, 0, 0, 0.3); + --buttonFontColorActive: var(--buttonFontColor); + --buttonBgColorActive: #181b1d; + --buttonBorderActive: var(--buttonBorder); + + --buttonPrimaryFontColor: #16181a; + --buttonPrimaryBgColor: var(--themeColor); + --buttonPrimaryBorder: none; + --buttonPrimaryShadow: none; + --buttonPrimaryFontColorHover: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorHover: #5eff6c; + --buttonPrimaryBorderHover: var(--buttonPrimaryBorder); + --buttonPrimaryFontColorActive: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorActive: #4585e6; + --buttonPrimaryBorderActive: var(--buttonPrimaryBorder); + --buttonPrimaryFocusBorder: none; + --buttonPrimaryFocusShadow: inset 0 0 0 1px rgba(24, 26, 31, 0.5), 0 0 0 1px var(--themeColor); + --tableBorderColor: #1e2124; + + /* Markdown element colors */ + --headingColor: #ffffff; + --h1Color: #ff6e5e; + --h2Color: #5ea1ff; + --h3Color: #5eff6c; + --h4Color: #5ef1ff; + --h5Color: #bd5eff; + --h6Color: #ffbd5e; + --blockquoteTextColor: rgba(255, 255, 255, 0.6); + --blockquoteBorderColor: #3c4048; + --hrColor: #2a2e32; + --linkColor: #5ea1ff; + --strongColor: #ffffff; + --emColor: #bd5eff; + --listMarkerColor: #5eff6c; + + /*marktext*/ + --sideBarColor: rgba(255, 255, 255, 0.6); + --sideBarIconColor: var(--iconColor); + --sideBarTitleColor: rgba(255, 255, 255, 0.8); + --sideBarTextColor: rgba(255, 255, 255, 0.4); + --sideBarBgColor: #121416; + --sideBarItemHoverBgColor: rgba(255, 255, 255, 0.03); + --itemBgColor: #1e2124; + + --floatFontColor: rgba(255, 255, 255, 0.7); + --floatBgColor: #1e2124; + --floatHoverColor: rgba(255, 255, 255, 0.04); + --floatBorderColor: rgba(0, 0, 0, 0.05); + --floatShadow: rgba(0, 0, 0, 0.2); + --maskColor: rgba(0, 0, 0, 0.7); + --editorAreaWidth: 750px; + + /* @muyajs/core (kebab-case) editor variables + Additive prep for the muyajs -> @muyajs/core editor swap. These + mirror the camelCase vars above; the legacy editor ignores them. + Do NOT remove the camelCase vars — they still drive the current + editor until editor.vue switches engines. */ + --theme-color: var(--themeColor); + --highlight-color: var(--highlightColor); + --selection-color: var(--selectionColor); + --editor-color: var(--editorColor); + --editor-color-80: var(--editorColor80); + --editor-color-50: var(--editorColor50); + --editor-color-30: var(--editorColor30); + --editor-color-10: var(--editorColor10); + --editor-color-04: var(--editorColor04); + --editor-bg-color: var(--editorBgColor); + --editor-area-width: var(--editorAreaWidth); + --delete-color: var(--deleteColor); + --icon-color: var(--iconColor); + --code-block-bg-color: var(--codeBlockBgColor); + --table-border-color: var(--tableBorderColor); + --input-bg-color: var(--inputBgColor); + --button-font-color: var(--buttonFontColor); + --button-bg-color: var(--buttonBgColor); + --button-border: var(--buttonBorder); + --button-bg-color-hover: var(--buttonBgColorHover); + --button-border-hover: var(--buttonBorderHover); + --button-bg-color-active: var(--buttonBgColorActive); + --button-border-active: var(--buttonBorderActive); + --button-border-focus: var(--button-border); + --float-bg-color: var(--floatBgColor); + --float-hover-color: var(--floatHoverColor); + --float-border-color: var(--floatBorderColor); + --float-shadow: rgb(15 15 15 / 3%) 0 0 0 1px, rgb(15 15 15 / 4%) 0 3px 6px, rgb(15 15 15 / 5%) 0 9px 24px; + --link-color: var(--linkColor); + --h1-color: var(--h1Color); + --h2-color: var(--h2Color); + --h3-color: var(--h3Color); + --h4-color: var(--h4Color); + --h5-color: var(--h5Color); + --h6-color: var(--h6Color); + --blockquote-text-color: var(--blockquoteTextColor); + --blockquote-border-color: var(--blockquoteBorderColor); + --strong-color: var(--strongColor); + --em-color: var(--emColor); + --list-marker-color: var(--listMarkerColor); + --hr-color: var(--hrColor); +} + +::-webkit-scrollbar { + background: var(--editorBgColor); +} + +.mu-front-menu, +.mu-float-wrapper { + box-shadow: 0 4px 8px 0 var(--floatShadow) !important; +} + +.title-bar .frameless-titlebar-button > div > svg { + fill: #ffffff; +} +.title-bar .frameless-titlebar-minimize:hover, +.title-bar .frameless-titlebar-toggle:hover { + background-color: rgba(255, 255, 255, 0.05); +} + +.side-bar { + border-right: 1px solid #0a0b0c !important; +} + +.recent-files-projects a, +.open-project a { + box-shadow: none !important; +} + +.editor-tabs { + box-shadow: none !important; +} +.editor-tabs:after { + position: absolute; + content: ''; + border-bottom: 1px solid #0a0b0c; + bottom: 0; + left: 0; + right: 0; + z-index: 1; +} +.editor-tabs ul.tabs-container:after { + position: absolute; + content: ''; + border-bottom: 1px solid #0a0b0c; + bottom: 0; + left: 0; + right: 0; + z-index: 2; +} + +.tabs-container > li, +.tabs-container > li.active { + background: var(--editorBgColor) !important; +} + +.open-project button, +.recent-files-projects button { + box-shadow: none !important; +} + +/* ------------------------------------ */ + +:not(pre) > code[class*='language-'], +pre:not(.CodeMirror-line), +pre[class*='language-'], +pre.mu-code-block { + background: var(--codeBlockBgColor) !important; + border: none !important; +} +.mu-thematic-break:not(.mu-active)::before { + border-top: 2px dashed var(--editorColor10) !important; + background: none !important; +} +figure.mu-active.mu-math-block > div.mu-math-preview, +figure.mu-active.mu-diagram-block > div.mu-diagram-preview { + box-shadow: 0 3px 8px 0 var(--floatShadow) !important; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/dark.theme.css b/packages/desktop/src/renderer/src/assets/themes/dark.theme.css new file mode 100644 index 0000000000..a40a00bbd6 --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/dark.theme.css @@ -0,0 +1,220 @@ +:root { + /*editor*/ + --themeColor: #409eff; + --themeColor90: rgba(64, 158, 255, 0.9); + --themeColor80: rgba(64, 158, 255, 0.8); + --themeColor70: rgba(64, 158, 255, 0.7); + --themeColor60: rgba(64, 158, 255, 0.6); + --themeColor50: rgba(64, 158, 255, 0.5); + --themeColor40: rgba(64, 158, 255, 0.4); + --themeColor30: rgba(64, 158, 255, 0.3); + --themeColor20: rgba(64, 158, 255, 0.2); + --themeColor10: rgba(64, 158, 255, 0.1); + + --highlightColor: rgba(102, 177, 255, 0.6); + --selectionColor: rgba(102, 177, 255, 0.3); + --editorColor: rgba(255, 255, 255, 0.7); + --editorColor80: rgba(255, 255, 255, 0.8); + --editorColor60: rgba(255, 255, 255, 0.6); + --editorColor50: rgba(255, 255, 255, 0.5); + --editorColor40: rgba(255, 255, 255, 0.4); + --editorColor30: rgba(255, 255, 255, 0.3); + --editorColor10: rgba(255, 255, 255, 0.1); + --editorColor04: rgba(255, 255, 255, 0.04); + --editorBgColor: #282828; + --deleteColor: #409eff; + --iconColor: rgba(255, 255, 255, 0.56); + --codeBgColor: #424344; + --codeBlockBgColor: #424344; + --footnoteBgColor: rgba(66, 67, 68, 0.3); + --inputBgColor: #2f3336; + + --focusColor: var(--themeColor); + + --buttonFontColor: rgba(255, 255, 255, 0.6); + --buttonBgColor: #424344; + --buttonBorder: 1px solid rgba(0, 0, 0, 0.2); + --buttonShadow: none; + --buttonFontColorHover: var(--buttonFontColor); + --buttonBgColorHover: #4f5051; + --buttonBorderHover: 1px solid rgba(0, 0, 0, 0.3); + --buttonFontColorActive: var(--buttonFontColor); + --buttonBgColorActive: #333434; + --buttonBorderActive: var(--buttonBorder); + + --buttonPrimaryFontColor: #ffffff; + --buttonPrimaryBgColor: var(--themeColor); + --buttonPrimaryBorder: none; + --buttonPrimaryShadow: none; + --buttonPrimaryFontColorHover: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorHover: #5aabff; + --buttonPrimaryBorderHover: var(--buttonPrimaryBorder); + --buttonPrimaryFontColorActive: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorActive: #2791ff; + --buttonPrimaryBorderActive: var(--buttonPrimaryBorder); + --buttonPrimaryFocusBorder: none; + --buttonPrimaryFocusShadow: inset 0 0 0 1px rgba(24, 26, 31, 0.5), 0 0 0 1px var(--themeColor); + --tableBorderColor: #363839; + + /* Markdown element colors */ + --headingColor: rgba(255, 255, 255, 0.8); + --h1Color: var(--editorColor80); + --h2Color: var(--editorColor80); + --h3Color: var(--editorColor80); + --h4Color: var(--editorColor80); + --h5Color: var(--editorColor80); + --h6Color: var(--editorColor80); + --blockquoteTextColor: rgba(255, 255, 255, 0.5); + --blockquoteBorderColor: #409eff; + --hrColor: rgba(255, 255, 255, 0.1); + --linkColor: #409eff; + --strongColor: rgba(255, 255, 255, 0.8); + --emColor: #66b1ff; + --listMarkerColor: #409eff; + + /*marktext*/ + --sideBarColor: rgba(255, 255, 255, 0.6); + --sideBarIconColor: var(--iconColor); + --sideBarTitleColor: rgba(255, 255, 255, 0.8); + --sideBarTextColor: rgba(255, 255, 255, 0.4); + --sideBarBgColor: #232323; + --sideBarItemHoverBgColor: rgba(255, 255, 255, 0.03); + --itemBgColor: #3f3f3f; + + --floatFontColor: rgba(255, 255, 255, 0.7); + --floatBgColor: #3f3f3f; + --floatHoverColor: rgba(255, 255, 255, 0.04); + --floatBorderColor: rgba(0, 0, 0, 0.05); + --floatShadow: rgba(0, 0, 0, 0.2); + --maskColor: rgba(0, 0, 0, 0.7); + --editorAreaWidth: 750px; + + /* @muyajs/core (kebab-case) editor variables + Additive prep for the muyajs -> @muyajs/core editor swap. These + mirror the camelCase vars above; the legacy editor ignores them. + Do NOT remove the camelCase vars — they still drive the current + editor until editor.vue switches engines. */ + --theme-color: var(--themeColor); + --highlight-color: var(--highlightColor); + --selection-color: var(--selectionColor); + --editor-color: var(--editorColor); + --editor-color-80: var(--editorColor80); + --editor-color-50: var(--editorColor50); + --editor-color-30: var(--editorColor30); + --editor-color-10: var(--editorColor10); + --editor-color-04: var(--editorColor04); + --editor-bg-color: var(--editorBgColor); + --editor-area-width: var(--editorAreaWidth); + --delete-color: var(--deleteColor); + --icon-color: var(--iconColor); + --code-block-bg-color: var(--codeBlockBgColor); + --table-border-color: var(--tableBorderColor); + --input-bg-color: var(--inputBgColor); + --button-font-color: var(--buttonFontColor); + --button-bg-color: var(--buttonBgColor); + --button-border: var(--buttonBorder); + --button-bg-color-hover: var(--buttonBgColorHover); + --button-border-hover: var(--buttonBorderHover); + --button-bg-color-active: var(--buttonBgColorActive); + --button-border-active: var(--buttonBorderActive); + --button-border-focus: var(--button-border); + --float-bg-color: var(--floatBgColor); + --float-hover-color: var(--floatHoverColor); + --float-border-color: var(--floatBorderColor); + --float-shadow: rgb(15 15 15 / 3%) 0 0 0 1px, rgb(15 15 15 / 4%) 0 3px 6px, rgb(15 15 15 / 5%) 0 9px 24px; + --link-color: var(--linkColor); + --h1-color: var(--h1Color); + --h2-color: var(--h2Color); + --h3-color: var(--h3Color); + --h4-color: var(--h4Color); + --h5-color: var(--h5Color); + --h6-color: var(--h6Color); + --blockquote-text-color: var(--blockquoteTextColor); + --blockquote-border-color: var(--blockquoteBorderColor); + --strong-color: var(--strongColor); + --em-color: var(--emColor); + --list-marker-color: var(--listMarkerColor); + --hr-color: var(--hrColor); +} + +::-webkit-scrollbar { + background: var(--editorBgColor); +} + +.mu-front-menu, +.mu-float-wrapper { + box-shadow: 0 4px 8px 0 var(--floatShadow) !important; +} + +.title-bar .frameless-titlebar-button > div > svg { + fill: #ffffff; +} +.title-bar .frameless-titlebar-minimize:hover, +.title-bar .frameless-titlebar-toggle:hover { + background-color: rgb(255, 255, 255, 0.05); +} + +.side-bar { + border-right: 1px solid #1d1d1d !important; +} + +.recent-files-projects a, +.open-project a { + box-shadow: none !important; +} + +.editor-tabs { + box-shadow: none !important; +} +.editor-tabs:after { + position: absolute; + content: ''; + border-bottom: 1px solid #1d1d1d; + bottom: 0; + left: 0; + right: 0; + z-index: 1; +} +.editor-tabs ul.tabs-container:after { + position: absolute; + content: ''; + border-bottom: 1px solid #1d1d1d; + bottom: 0; + left: 0; + right: 0; + z-index: 2; +} + +.tabs-container > li, +.tabs-container > li.active { + background: var(--editorBgColor) !important; +} + +.open-project button, +.recent-files-projects button { + box-shadow: none !important; +} + +/* ------------------------------------ */ + +:not(pre) > code[class*='language-'], +pre:not(.CodeMirror-line), +pre[class*='language-'], +pre.mu-code-block { + background: var(--codeBlockBgColor) !important; + border: none !important; +} +.mu-thematic-break:not(.mu-active)::before { + border-top: 2px dashed var(--editorColor10) !important; + background: none !important; +} +figure.mu-active.mu-math-block > div.mu-math-preview, +figure.mu-active.mu-diagram-block > div.mu-diagram-preview { + box-shadow: 0 3px 8px 0 var(--floatShadow) !important; +} + +/* + * Prism.js theme (override light theme) + */ + +/* @import url("prismjs/dark.theme.css"); */ diff --git a/packages/desktop/src/renderer/src/assets/themes/dracula.theme.css b/packages/desktop/src/renderer/src/assets/themes/dracula.theme.css new file mode 100644 index 0000000000..026b731ef9 --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/dracula.theme.css @@ -0,0 +1,214 @@ +:root { + /*editor - Dracula theme*/ + --themeColor: #bd93f9; + --themeColor90: rgba(189, 147, 249, 0.9); + --themeColor80: rgba(189, 147, 249, 0.8); + --themeColor70: rgba(189, 147, 249, 0.7); + --themeColor60: rgba(189, 147, 249, 0.6); + --themeColor50: rgba(189, 147, 249, 0.5); + --themeColor40: rgba(189, 147, 249, 0.4); + --themeColor30: rgba(189, 147, 249, 0.3); + --themeColor20: rgba(189, 147, 249, 0.2); + --themeColor10: rgba(189, 147, 249, 0.1); + + --highlightColor: rgba(189, 147, 249, 0.6); + --selectionColor: rgba(68, 71, 90, 0.8); + --editorColor: #f8f8f2; + --editorColor80: rgba(248, 248, 242, 0.8); + --editorColor60: rgba(248, 248, 242, 0.6); + --editorColor50: rgba(248, 248, 242, 0.5); + --editorColor40: rgba(248, 248, 242, 0.4); + --editorColor30: rgba(248, 248, 242, 0.3); + --editorColor10: rgba(248, 248, 242, 0.1); + --editorColor04: rgba(248, 248, 242, 0.04); + --editorBgColor: #282a36; + --deleteColor: #ff5555; + --iconColor: rgba(248, 248, 242, 0.56); + --codeBgColor: #44475a; + --codeBlockBgColor: #44475a; + --footnoteBgColor: rgba(68, 71, 90, 0.3); + --inputBgColor: #21222c; + + --focusColor: var(--themeColor); + + --buttonFontColor: rgba(248, 248, 242, 0.6); + --buttonBgColor: #44475a; + --buttonBorder: 1px solid rgba(0, 0, 0, 0.2); + --buttonShadow: none; + --buttonFontColorHover: var(--buttonFontColor); + --buttonBgColorHover: #6272a4; + --buttonBorderHover: 1px solid rgba(0, 0, 0, 0.3); + --buttonFontColorActive: var(--buttonFontColor); + --buttonBgColorActive: #383a46; + --buttonBorderActive: var(--buttonBorder); + + --buttonPrimaryFontColor: #ffffff; + --buttonPrimaryBgColor: var(--themeColor); + --buttonPrimaryBorder: none; + --buttonPrimaryShadow: none; + --buttonPrimaryFontColorHover: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorHover: #caa8fa; + --buttonPrimaryBorderHover: var(--buttonPrimaryBorder); + --buttonPrimaryFontColorActive: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorActive: #a87ef8; + --buttonPrimaryBorderActive: var(--buttonPrimaryBorder); + --buttonPrimaryFocusBorder: none; + --buttonPrimaryFocusShadow: inset 0 0 0 1px rgba(24, 26, 31, 0.5), 0 0 0 1px var(--themeColor); + --tableBorderColor: #44475a; + + /* Markdown element colors */ + --headingColor: #f8f8f2; + --h1Color: #ff79c6; + --h2Color: #bd93f9; + --h3Color: #8be9fd; + --h4Color: #50fa7b; + --h5Color: #ffb86c; + --h6Color: #f1fa8c; + --blockquoteTextColor: rgba(248, 248, 242, 0.6); + --blockquoteBorderColor: #6272a4; + --hrColor: #44475a; + --linkColor: #8be9fd; + --strongColor: #f8f8f2; + --emColor: #f1fa8c; + --listMarkerColor: #bd93f9; + + /*marktext*/ + --sideBarColor: rgba(248, 248, 242, 0.6); + --sideBarIconColor: var(--iconColor); + --sideBarTitleColor: rgba(248, 248, 242, 0.8); + --sideBarTextColor: rgba(248, 248, 242, 0.4); + --sideBarBgColor: #242631; + --sideBarItemHoverBgColor: rgba(248, 248, 242, 0.03); + --itemBgColor: #44475a; + + --floatFontColor: rgba(248, 248, 242, 0.7); + --floatBgColor: #44475a; + --floatHoverColor: rgba(248, 248, 242, 0.04); + --floatBorderColor: rgba(0, 0, 0, 0.05); + --floatShadow: rgba(0, 0, 0, 0.2); + --maskColor: rgba(0, 0, 0, 0.7); + --editorAreaWidth: 750px; + + /* @muyajs/core (kebab-case) editor variables + Additive prep for the muyajs -> @muyajs/core editor swap. These + mirror the camelCase vars above; the legacy editor ignores them. + Do NOT remove the camelCase vars — they still drive the current + editor until editor.vue switches engines. */ + --theme-color: var(--themeColor); + --highlight-color: var(--highlightColor); + --selection-color: var(--selectionColor); + --editor-color: var(--editorColor); + --editor-color-80: var(--editorColor80); + --editor-color-50: var(--editorColor50); + --editor-color-30: var(--editorColor30); + --editor-color-10: var(--editorColor10); + --editor-color-04: var(--editorColor04); + --editor-bg-color: var(--editorBgColor); + --editor-area-width: var(--editorAreaWidth); + --delete-color: var(--deleteColor); + --icon-color: var(--iconColor); + --code-block-bg-color: var(--codeBlockBgColor); + --table-border-color: var(--tableBorderColor); + --input-bg-color: var(--inputBgColor); + --button-font-color: var(--buttonFontColor); + --button-bg-color: var(--buttonBgColor); + --button-border: var(--buttonBorder); + --button-bg-color-hover: var(--buttonBgColorHover); + --button-border-hover: var(--buttonBorderHover); + --button-bg-color-active: var(--buttonBgColorActive); + --button-border-active: var(--buttonBorderActive); + --button-border-focus: var(--button-border); + --float-bg-color: var(--floatBgColor); + --float-hover-color: var(--floatHoverColor); + --float-border-color: var(--floatBorderColor); + --float-shadow: rgb(15 15 15 / 3%) 0 0 0 1px, rgb(15 15 15 / 4%) 0 3px 6px, rgb(15 15 15 / 5%) 0 9px 24px; + --link-color: var(--linkColor); + --h1-color: var(--h1Color); + --h2-color: var(--h2Color); + --h3-color: var(--h3Color); + --h4-color: var(--h4Color); + --h5-color: var(--h5Color); + --h6-color: var(--h6Color); + --blockquote-text-color: var(--blockquoteTextColor); + --blockquote-border-color: var(--blockquoteBorderColor); + --strong-color: var(--strongColor); + --em-color: var(--emColor); + --list-marker-color: var(--listMarkerColor); + --hr-color: var(--hrColor); +} + +::-webkit-scrollbar { + background: var(--editorBgColor); +} + +.mu-front-menu, +.mu-float-wrapper { + box-shadow: 0 4px 8px 0 var(--floatShadow) !important; +} + +.title-bar .frameless-titlebar-button > div > svg { + fill: #f8f8f2; +} +.title-bar .frameless-titlebar-minimize:hover, +.title-bar .frameless-titlebar-toggle:hover { + background-color: rgb(248, 248, 242, 0.05); +} + +.side-bar { + border-right: 1px solid #191a21 !important; +} + +.recent-files-projects a, +.open-project a { + box-shadow: none !important; +} + +.editor-tabs { + box-shadow: none !important; +} +.editor-tabs:after { + position: absolute; + content: ''; + border-bottom: 1px solid #191a21; + bottom: 0; + left: 0; + right: 0; + z-index: 1; +} +.editor-tabs ul.tabs-container:after { + position: absolute; + content: ''; + border-bottom: 1px solid #191a21; + bottom: 0; + left: 0; + right: 0; + z-index: 2; +} + +.tabs-container > li, +.tabs-container > li.active { + background: var(--editorBgColor) !important; +} + +.open-project button, +.recent-files-projects button { + box-shadow: none !important; +} + +/* ------------------------------------ */ + +:not(pre) > code[class*='language-'], +pre:not(.CodeMirror-line), +pre[class*='language-'], +pre.mu-code-block { + background: var(--codeBlockBgColor) !important; + border: none !important; +} +.mu-thematic-break:not(.mu-active)::before { + border-top: 2px dashed var(--editorColor10) !important; + background: none !important; +} +figure.mu-active.mu-math-block > div.mu-math-preview, +figure.mu-active.mu-diagram-block > div.mu-diagram-preview { + box-shadow: 0 3px 8px 0 var(--floatShadow) !important; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/everforest-dark.theme.css b/packages/desktop/src/renderer/src/assets/themes/everforest-dark.theme.css new file mode 100644 index 0000000000..6ef62b0de5 --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/everforest-dark.theme.css @@ -0,0 +1,214 @@ +:root { + /*editor - Everforest Dark theme*/ + --themeColor: #a7c080; + --themeColor90: rgba(167, 192, 128, 0.9); + --themeColor80: rgba(167, 192, 128, 0.8); + --themeColor70: rgba(167, 192, 128, 0.7); + --themeColor60: rgba(167, 192, 128, 0.6); + --themeColor50: rgba(167, 192, 128, 0.5); + --themeColor40: rgba(167, 192, 128, 0.4); + --themeColor30: rgba(167, 192, 128, 0.3); + --themeColor20: rgba(167, 192, 128, 0.2); + --themeColor10: rgba(167, 192, 128, 0.1); + + --highlightColor: rgba(167, 192, 128, 0.6); + --selectionColor: rgba(67, 76, 64, 0.8); + --editorColor: #d3c6aa; + --editorColor80: rgba(211, 198, 170, 0.8); + --editorColor60: rgba(211, 198, 170, 0.6); + --editorColor50: rgba(211, 198, 170, 0.5); + --editorColor40: rgba(211, 198, 170, 0.4); + --editorColor30: rgba(211, 198, 170, 0.3); + --editorColor10: rgba(211, 198, 170, 0.1); + --editorColor04: rgba(211, 198, 170, 0.04); + --editorBgColor: #2d353b; + --deleteColor: #e67e80; + --iconColor: rgba(211, 198, 170, 0.56); + --codeBgColor: #343f44; + --codeBlockBgColor: #343f44; + --footnoteBgColor: rgba(52, 63, 68, 0.3); + --inputBgColor: #272e33; + + --focusColor: var(--themeColor); + + --buttonFontColor: rgba(211, 198, 170, 0.6); + --buttonBgColor: #343f44; + --buttonBorder: 1px solid rgba(0, 0, 0, 0.2); + --buttonShadow: none; + --buttonFontColorHover: var(--buttonFontColor); + --buttonBgColorHover: #3d4a50; + --buttonBorderHover: 1px solid rgba(0, 0, 0, 0.3); + --buttonFontColorActive: var(--buttonFontColor); + --buttonBgColorActive: #2c363b; + --buttonBorderActive: var(--buttonBorder); + + --buttonPrimaryFontColor: #2d353b; + --buttonPrimaryBgColor: var(--themeColor); + --buttonPrimaryBorder: none; + --buttonPrimaryShadow: none; + --buttonPrimaryFontColorHover: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorHover: #83c092; + --buttonPrimaryBorderHover: var(--buttonPrimaryBorder); + --buttonPrimaryFontColorActive: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorActive: #8da060; + --buttonPrimaryBorderActive: var(--buttonPrimaryBorder); + --buttonPrimaryFocusBorder: none; + --buttonPrimaryFocusShadow: inset 0 0 0 1px rgba(24, 26, 31, 0.5), 0 0 0 1px var(--themeColor); + --tableBorderColor: #343f44; + + /* Markdown element colors */ + --headingColor: #d3c6aa; + --h1Color: #e67e80; + --h2Color: #dbbc7f; + --h3Color: #a7c080; + --h4Color: #7fbbb3; + --h5Color: #d699b6; + --h6Color: #83c092; + --blockquoteTextColor: rgba(211, 198, 170, 0.6); + --blockquoteBorderColor: #475258; + --hrColor: #3d4a50; + --linkColor: #7fbbb3; + --strongColor: #d3c6aa; + --emColor: #dbbc7f; + --listMarkerColor: #a7c080; + + /*marktext*/ + --sideBarColor: rgba(211, 198, 170, 0.6); + --sideBarIconColor: var(--iconColor); + --sideBarTitleColor: rgba(211, 198, 170, 0.8); + --sideBarTextColor: rgba(211, 198, 170, 0.4); + --sideBarBgColor: #2a3137; + --sideBarItemHoverBgColor: rgba(211, 198, 170, 0.03); + --itemBgColor: #343f44; + + --floatFontColor: rgba(211, 198, 170, 0.7); + --floatBgColor: #343f44; + --floatHoverColor: rgba(211, 198, 170, 0.04); + --floatBorderColor: rgba(0, 0, 0, 0.05); + --floatShadow: rgba(0, 0, 0, 0.2); + --maskColor: rgba(0, 0, 0, 0.7); + --editorAreaWidth: 750px; + + /* @muyajs/core (kebab-case) editor variables + Additive prep for the muyajs -> @muyajs/core editor swap. These + mirror the camelCase vars above; the legacy editor ignores them. + Do NOT remove the camelCase vars — they still drive the current + editor until editor.vue switches engines. */ + --theme-color: var(--themeColor); + --highlight-color: var(--highlightColor); + --selection-color: var(--selectionColor); + --editor-color: var(--editorColor); + --editor-color-80: var(--editorColor80); + --editor-color-50: var(--editorColor50); + --editor-color-30: var(--editorColor30); + --editor-color-10: var(--editorColor10); + --editor-color-04: var(--editorColor04); + --editor-bg-color: var(--editorBgColor); + --editor-area-width: var(--editorAreaWidth); + --delete-color: var(--deleteColor); + --icon-color: var(--iconColor); + --code-block-bg-color: var(--codeBlockBgColor); + --table-border-color: var(--tableBorderColor); + --input-bg-color: var(--inputBgColor); + --button-font-color: var(--buttonFontColor); + --button-bg-color: var(--buttonBgColor); + --button-border: var(--buttonBorder); + --button-bg-color-hover: var(--buttonBgColorHover); + --button-border-hover: var(--buttonBorderHover); + --button-bg-color-active: var(--buttonBgColorActive); + --button-border-active: var(--buttonBorderActive); + --button-border-focus: var(--button-border); + --float-bg-color: var(--floatBgColor); + --float-hover-color: var(--floatHoverColor); + --float-border-color: var(--floatBorderColor); + --float-shadow: rgb(15 15 15 / 3%) 0 0 0 1px, rgb(15 15 15 / 4%) 0 3px 6px, rgb(15 15 15 / 5%) 0 9px 24px; + --link-color: var(--linkColor); + --h1-color: var(--h1Color); + --h2-color: var(--h2Color); + --h3-color: var(--h3Color); + --h4-color: var(--h4Color); + --h5-color: var(--h5Color); + --h6-color: var(--h6Color); + --blockquote-text-color: var(--blockquoteTextColor); + --blockquote-border-color: var(--blockquoteBorderColor); + --strong-color: var(--strongColor); + --em-color: var(--emColor); + --list-marker-color: var(--listMarkerColor); + --hr-color: var(--hrColor); +} + +::-webkit-scrollbar { + background: var(--editorBgColor); +} + +.mu-front-menu, +.mu-float-wrapper { + box-shadow: 0 4px 8px 0 var(--floatShadow) !important; +} + +.title-bar .frameless-titlebar-button > div > svg { + fill: #d3c6aa; +} +.title-bar .frameless-titlebar-minimize:hover, +.title-bar .frameless-titlebar-toggle:hover { + background-color: rgba(211, 198, 170, 0.05); +} + +.side-bar { + border-right: 1px solid #232a2e !important; +} + +.recent-files-projects a, +.open-project a { + box-shadow: none !important; +} + +.editor-tabs { + box-shadow: none !important; +} +.editor-tabs:after { + position: absolute; + content: ''; + border-bottom: 1px solid #232a2e; + bottom: 0; + left: 0; + right: 0; + z-index: 1; +} +.editor-tabs ul.tabs-container:after { + position: absolute; + content: ''; + border-bottom: 1px solid #232a2e; + bottom: 0; + left: 0; + right: 0; + z-index: 2; +} + +.tabs-container > li, +.tabs-container > li.active { + background: var(--editorBgColor) !important; +} + +.open-project button, +.recent-files-projects button { + box-shadow: none !important; +} + +/* ------------------------------------ */ + +:not(pre) > code[class*='language-'], +pre:not(.CodeMirror-line), +pre[class*='language-'], +pre.mu-code-block { + background: var(--codeBlockBgColor) !important; + border: none !important; +} +.mu-thematic-break:not(.mu-active)::before { + border-top: 2px dashed var(--editorColor10) !important; + background: none !important; +} +figure.mu-active.mu-math-block > div.mu-math-preview, +figure.mu-active.mu-diagram-block > div.mu-diagram-preview { + box-shadow: 0 3px 8px 0 var(--floatShadow) !important; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/everforest-light.theme.css b/packages/desktop/src/renderer/src/assets/themes/everforest-light.theme.css new file mode 100644 index 0000000000..5c0fa18a5e --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/everforest-light.theme.css @@ -0,0 +1,214 @@ +:root { + /*editor - Everforest Light theme*/ + --themeColor: #8da101; + --themeColor90: rgba(141, 161, 1, 0.9); + --themeColor80: rgba(141, 161, 1, 0.8); + --themeColor70: rgba(141, 161, 1, 0.7); + --themeColor60: rgba(141, 161, 1, 0.6); + --themeColor50: rgba(141, 161, 1, 0.5); + --themeColor40: rgba(141, 161, 1, 0.4); + --themeColor30: rgba(141, 161, 1, 0.3); + --themeColor20: rgba(141, 161, 1, 0.2); + --themeColor10: rgba(141, 161, 1, 0.1); + + --highlightColor: rgba(141, 161, 1, 0.3); + --selectionColor: rgba(230, 226, 204, 0.8); + --editorColor: #5c6a72; + --editorColor80: rgba(92, 106, 114, 0.8); + --editorColor60: rgba(92, 106, 114, 0.6); + --editorColor50: rgba(92, 106, 114, 0.5); + --editorColor40: rgba(92, 106, 114, 0.4); + --editorColor30: rgba(92, 106, 114, 0.3); + --editorColor10: rgba(92, 106, 114, 0.1); + --editorColor04: rgba(92, 106, 114, 0.04); + --editorBgColor: #fdf6e3; + --deleteColor: #f85552; + --iconColor: rgba(92, 106, 114, 0.56); + --codeBgColor: #f4f0d9; + --codeBlockBgColor: #f4f0d9; + --footnoteBgColor: rgba(244, 240, 217, 0.5); + --inputBgColor: #f8f4dd; + + --focusColor: var(--themeColor); + + --buttonFontColor: rgba(92, 106, 114, 0.7); + --buttonBgColor: #f4f0d9; + --buttonBorder: 1px solid rgba(0, 0, 0, 0.08); + --buttonShadow: none; + --buttonFontColorHover: var(--buttonFontColor); + --buttonBgColorHover: #ebe7cf; + --buttonBorderHover: 1px solid rgba(0, 0, 0, 0.12); + --buttonFontColorActive: var(--buttonFontColor); + --buttonBgColorActive: #e2ddc5; + --buttonBorderActive: var(--buttonBorder); + + --buttonPrimaryFontColor: #ffffff; + --buttonPrimaryBgColor: var(--themeColor); + --buttonPrimaryBorder: none; + --buttonPrimaryShadow: none; + --buttonPrimaryFontColorHover: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorHover: #35a77c; + --buttonPrimaryBorderHover: var(--buttonPrimaryBorder); + --buttonPrimaryFontColorActive: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorActive: #718800; + --buttonPrimaryBorderActive: var(--buttonPrimaryBorder); + --buttonPrimaryFocusBorder: none; + --buttonPrimaryFocusShadow: inset 0 0 0 1px rgba(255, 255, 255, 0.5), 0 0 0 1px var(--themeColor); + --tableBorderColor: #e2ddc5; + + /* Markdown element colors */ + --headingColor: #5c6a72; + --h1Color: #f85552; + --h2Color: #dfa000; + --h3Color: #8da101; + --h4Color: #35a77c; + --h5Color: #df69ba; + --h6Color: #3a94c5; + --blockquoteTextColor: rgba(92, 106, 114, 0.6); + --blockquoteBorderColor: #939f91; + --hrColor: #e2ddc5; + --linkColor: #3a94c5; + --strongColor: #5c6a72; + --emColor: #dfa000; + --listMarkerColor: #8da101; + + /*marktext*/ + --sideBarColor: rgba(92, 106, 114, 0.7); + --sideBarIconColor: var(--iconColor); + --sideBarTitleColor: rgba(92, 106, 114, 0.9); + --sideBarTextColor: rgba(92, 106, 114, 0.5); + --sideBarBgColor: #f8f3de; + --sideBarItemHoverBgColor: rgba(92, 106, 114, 0.05); + --itemBgColor: #ebe7cf; + + --floatFontColor: rgba(92, 106, 114, 0.8); + --floatBgColor: #f4f0d9; + --floatHoverColor: rgba(92, 106, 114, 0.06); + --floatBorderColor: rgba(0, 0, 0, 0.08); + --floatShadow: rgba(0, 0, 0, 0.1); + --maskColor: rgba(0, 0, 0, 0.3); + --editorAreaWidth: 750px; + + /* @muyajs/core (kebab-case) editor variables + Additive prep for the muyajs -> @muyajs/core editor swap. These + mirror the camelCase vars above; the legacy editor ignores them. + Do NOT remove the camelCase vars — they still drive the current + editor until editor.vue switches engines. */ + --theme-color: var(--themeColor); + --highlight-color: var(--highlightColor); + --selection-color: var(--selectionColor); + --editor-color: var(--editorColor); + --editor-color-80: var(--editorColor80); + --editor-color-50: var(--editorColor50); + --editor-color-30: var(--editorColor30); + --editor-color-10: var(--editorColor10); + --editor-color-04: var(--editorColor04); + --editor-bg-color: var(--editorBgColor); + --editor-area-width: var(--editorAreaWidth); + --delete-color: var(--deleteColor); + --icon-color: var(--iconColor); + --code-block-bg-color: var(--codeBlockBgColor); + --table-border-color: var(--tableBorderColor); + --input-bg-color: var(--inputBgColor); + --button-font-color: var(--buttonFontColor); + --button-bg-color: var(--buttonBgColor); + --button-border: var(--buttonBorder); + --button-bg-color-hover: var(--buttonBgColorHover); + --button-border-hover: var(--buttonBorderHover); + --button-bg-color-active: var(--buttonBgColorActive); + --button-border-active: var(--buttonBorderActive); + --button-border-focus: var(--button-border); + --float-bg-color: var(--floatBgColor); + --float-hover-color: var(--floatHoverColor); + --float-border-color: var(--floatBorderColor); + --float-shadow: rgb(15 15 15 / 3%) 0 0 0 1px, rgb(15 15 15 / 4%) 0 3px 6px, rgb(15 15 15 / 5%) 0 9px 24px; + --link-color: var(--linkColor); + --h1-color: var(--h1Color); + --h2-color: var(--h2Color); + --h3-color: var(--h3Color); + --h4-color: var(--h4Color); + --h5-color: var(--h5Color); + --h6-color: var(--h6Color); + --blockquote-text-color: var(--blockquoteTextColor); + --blockquote-border-color: var(--blockquoteBorderColor); + --strong-color: var(--strongColor); + --em-color: var(--emColor); + --list-marker-color: var(--listMarkerColor); + --hr-color: var(--hrColor); +} + +::-webkit-scrollbar { + background: var(--editorBgColor); +} + +.mu-front-menu, +.mu-float-wrapper { + box-shadow: 0 4px 8px 0 var(--floatShadow) !important; +} + +.title-bar .frameless-titlebar-button > div > svg { + fill: #5c6a72; +} +.title-bar .frameless-titlebar-minimize:hover, +.title-bar .frameless-titlebar-toggle:hover { + background-color: rgba(92, 106, 114, 0.08); +} + +.side-bar { + border-right: 1px solid #e2ddc5 !important; +} + +.recent-files-projects a, +.open-project a { + box-shadow: none !important; +} + +.editor-tabs { + box-shadow: none !important; +} +.editor-tabs:after { + position: absolute; + content: ''; + border-bottom: 1px solid #e2ddc5; + bottom: 0; + left: 0; + right: 0; + z-index: 1; +} +.editor-tabs ul.tabs-container:after { + position: absolute; + content: ''; + border-bottom: 1px solid #e2ddc5; + bottom: 0; + left: 0; + right: 0; + z-index: 2; +} + +.tabs-container > li, +.tabs-container > li.active { + background: var(--editorBgColor) !important; +} + +.open-project button, +.recent-files-projects button { + box-shadow: none !important; +} + +/* ------------------------------------ */ + +:not(pre) > code[class*='language-'], +pre:not(.CodeMirror-line), +pre[class*='language-'], +pre.mu-code-block { + background: var(--codeBlockBgColor) !important; + border: none !important; +} +.mu-thematic-break:not(.mu-active)::before { + border-top: 2px dashed var(--editorColor10) !important; + background: none !important; +} +figure.mu-active.mu-math-block > div.mu-math-preview, +figure.mu-active.mu-diagram-block > div.mu-diagram-preview { + box-shadow: 0 3px 8px 0 var(--floatShadow) !important; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/export/academic.theme.css b/packages/desktop/src/renderer/src/assets/themes/export/academic.theme.css new file mode 100644 index 0000000000..0d9325759a --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/export/academic.theme.css @@ -0,0 +1,112 @@ +/** Academic **/ + +/* DejaVu Serif should use 11pt */ +.hf-container, +.markdown-body { + font-family: + Georgia, Palatino, 'Palatino Linotype', 'Times New Roman', 'DejaVu Serif', serif, + 'Apple Color Emoji', 'Segoe UI Emoji'; + font-size: 12pt; + line-height: 1.3; + color: #212121; +} + +:root { + --footerHeaderBorderColor: #1c1c1c; +} +.hf-container { + font-size: 0.75em; +} +.page-header:not(.simple) .hf-container { + padding-bottom: 2px; + border-bottom: 1px solid var(--footerHeaderBorderColor); +} +.page-footer:not(.simple) .hf-container { + padding-top: 2px; + border-top: 1px solid var(--footerHeaderBorderColor); +} + +.footnotes { + font-size: 0.75em; + opacity: 0.9; +} +.footnotes > hr:first-of-type { + width: 250px; + height: 1px; + background: var(--footerHeaderBorderColor); +} +.footnotes > ol > li > p { + margin-top: 4px; + margin-bottom: 4px; +} + +.markdown-body h1, +.markdown-body h2, +.markdown-body h3, +.markdown-body h4, +.markdown-body h5, +.markdown-body h6 { + font-weight: 600; + color: #282828; +} + +.markdown-body h1 { + font-size: 1.8em; +} +.markdown-body h2 { + font-size: 1.6em; +} +.markdown-body h3 { + font-size: 1.4em; +} +.markdown-body h4, +.markdown-body h5 { + font-size: 1.2em; +} +.markdown-body h6 { + font-size: 1em; +} + +.markdown-body h1, +.markdown-body h2 { + border-bottom: none; + padding-bottom: 0; +} + +.markdown-body a { + color: #212121; + text-decoration: underline; +} + +.markdown-body hr { + background-color: #b0b0b0; + height: 1px; +} + +.markdown-body blockquote { + border: none; + color: #282828; + padding: 0 1em; + margin-left: 20px; +} + +.markdown-body .highlight pre, +.markdown-body pre { + background-color: #f6f8fa; + border: 1px solid #e5e5e5; + border-radius: 0; +} + +.markdown-body table td, +.markdown-body table th { + border: 1px solid #1a1a1a; + padding: 2px 6px; +} +.markdown-body table tr { + background: none; + border-top: 1px solid #1a1a1a; + text-align: left; +} +.markdown-body table tr:nth-child(2n) { + background: none; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/export/liber.theme.css b/packages/desktop/src/renderer/src/assets/themes/export/liber.theme.css new file mode 100644 index 0000000000..220a90486d --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/export/liber.theme.css @@ -0,0 +1,92 @@ +/** Liber **/ + +.hf-container, +.markdown-body { + font-family: + 'Source Sans Pro', 'Segoe UI', 'Helvetica Neue', 'Open Sans', sans-serif, 'Apple Color Emoji', + 'Segoe UI Emoji'; + font-size: 16px; + line-height: 1.3; + color: #212121; +} + +:root { + --footerHeaderBorderColor: #1c1c1c; +} +.hf-container { + font-size: 0.75em; +} +.page-header:not(.simple) .hf-container { + padding-bottom: 2px; + border-bottom: 1px solid var(--footerHeaderBorderColor); +} +.page-footer:not(.simple) .hf-container { + padding-top: 2px; + border-top: 1px solid var(--footerHeaderBorderColor); +} + +.footnotes { + font-size: 0.75em; + opacity: 0.9; +} +.footnotes > hr:first-of-type { + width: 250px; + height: 1px; + background: var(--footerHeaderBorderColor); +} +.footnotes > ol > li > p { + margin-top: 4px; + margin-bottom: 4px; +} +.footnotes .footnote-back { + color: #212121; +} +.markdown-body a.footnote-ref { + color: #212121; +} + +.markdown-body h1, +.markdown-body h2, +.markdown-body h3, +.markdown-body h4, +.markdown-body h5, +.markdown-body h6 { + font-weight: 600; + color: #282828; +} + +.markdown-body h1 { + font-size: 1.8em; +} +.markdown-body h2 { + font-size: 1.6em; +} +.markdown-body h3 { + font-size: 1.4em; +} +.markdown-body h4, +.markdown-body h5 { + font-size: 1.2em; +} +.markdown-body h6 { + font-size: 1em; + font-weight: 400; +} + +.markdown-body h1, +.markdown-body h2 { + border-bottom: none; + padding-bottom: 0; +} + +.markdown-body hr { + background-color: #d7d8d9; + height: 1px; +} + +.markdown-body .highlight pre, +.markdown-body pre { + background-color: #f6f8fa; + border: 1px solid #e5e5e5; + border-radius: 0; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/graphite.theme.css b/packages/desktop/src/renderer/src/assets/themes/graphite.theme.css new file mode 100644 index 0000000000..47acdcda15 --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/graphite.theme.css @@ -0,0 +1,254 @@ +:root { + --themeColor: rgb(104, 134, 170); + --themeColor90: rgba(104, 134, 170, 0.9); + --themeColor80: rgba(104, 134, 170, 0.8); + --themeColor70: rgba(104, 134, 170, 0.7); + --themeColor60: rgba(104, 134, 170, 0.6); + --themeColor50: rgba(104, 134, 170, 0.5); + --themeColor40: rgba(104, 134, 170, 0.4); + --themeColor30: rgba(104, 134, 170, 0.3); + --themeColor20: rgba(104, 134, 170, 0.2); + --themeColor10: rgba(104, 134, 170, 0.1); + + --highlightColor: rgba(104, 134, 170, 0.4); + --selectionColor: rgba(0, 0, 0, 0.1); + --editorColor: rgba(43, 48, 50, 0.7); + --editorColor80: rgba(43, 48, 50, 0.8); + --editorColor60: rgba(43, 48, 50, 0.6); + --editorColor50: rgba(43, 48, 50, 0.5); + --editorColor40: rgba(43, 48, 50, 0.4); + --editorColor30: rgba(150, 150, 150, 0.8); + --editorColor10: rgba(43, 48, 50, 0.1); + --editorColor04: rgba(43, 48, 50, 0.04); + --editorBgColor: #f7f7f7; + --deleteColor: #ff6969; + --iconColor: rgba(150, 150, 150, 0.8); + --codeBgColor: #d8d8d869; + --codeBlockBgColor: rgba(104, 134, 170, 0.05); + --footnoteBgColor: rgba(0, 0, 0, 0.03); + --inputBgColor: rgba(0, 0, 0, 0.06); + --focusColor: var(--themeColor); + + --buttonFontColor: var(--editorColor); + --buttonBgColor: #ffffff; + --buttonBorder: 1px solid #dcdfe6; + --buttonShadow: none; + --buttonFontColorHover: var(--buttonFontColor); + --buttonBgColorHover: linear-gradient(#fafafa, #f5f5f5); + --buttonBorderHover: var(--buttonBorder); + --buttonFontColorActive: var(--buttonFontColor); + --buttonBgColorActive: #f5f5f5; + --buttonBorderActive: var(--buttonBorder); + --buttonFocusBorder: 1px solid var(--themeColor); + + --buttonPrimaryFontColor: #ffffff; + --buttonPrimaryBgColor: var(--themeColor); + --buttonPrimaryBorder: none; + --buttonPrimaryShadow: 0 0 8px 0 rgba(0, 0, 0, 0.1); + --buttonPrimaryFontColorHover: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorHover: var(--buttonPrimaryBgColor); + --buttonPrimaryBorderHover: var(--buttonPrimaryBorder); + --buttonPrimaryFontColorActive: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorActive: var(--buttonPrimaryBgColor); + --buttonPrimaryBorderActive: var(--buttonPrimaryBorder); + --buttonPrimaryFocusBorder: none; + --buttonPrimaryFocusShadow: inset 0 0 0 1px rgba(53, 74, 99, 0.5), 0 0 0 1px var(--themeColor); + --tableBorderColor: #e5e5e5; + + /* Markdown element colors */ + --headingColor: rgba(43, 48, 50, 0.8); + --h1Color: #6886aa; + --h2Color: #5f9ea0; + --h3Color: #b8860b; + --h4Color: #7b68ee; + --h5Color: #cd5c5c; + --h6Color: #708090; + --blockquoteTextColor: rgba(43, 48, 50, 0.5); + --blockquoteBorderColor: rgb(104, 134, 170); + --hrColor: rgba(43, 48, 50, 0.1); + --linkColor: rgb(104, 134, 170); + --strongColor: rgba(43, 48, 50, 0.8); + --emColor: #5f9ea0; + --listMarkerColor: #6886aa; + + --sideBarColor: rgba(43, 48, 50, 0.8); + --sideBarIconColor: rgba(43, 48, 50, 0.6); + --sideBarTitleColor: rgba(43, 48, 50, 1); + --sideBarTextColor: rgba(43, 48, 50, 0.6); + --sideBarBgColor: #ececec; + --sideBarItemHoverBgColor: rgba(255, 255, 255, 0.03); + --itemBgColor: #e5e5e5; + + --floatFontColor: rgba(43, 48, 50, 0.7); + --floatBgColor: rgb(237, 237, 238); + --floatHoverColor: rgba(43, 48, 50, 0.04); + --floatBorderColor: rgba(0, 0, 0, 0.03); + --maskColor: rgba(232, 232, 232, 0.8); + --editorAreaWidth: 750px; + + /* @muyajs/core (kebab-case) editor variables + Additive prep for the muyajs -> @muyajs/core editor swap. These + mirror the camelCase vars above; the legacy editor ignores them. + Do NOT remove the camelCase vars — they still drive the current + editor until editor.vue switches engines. */ + --theme-color: var(--themeColor); + --highlight-color: var(--highlightColor); + --selection-color: var(--selectionColor); + --editor-color: var(--editorColor); + --editor-color-80: var(--editorColor80); + --editor-color-50: var(--editorColor50); + --editor-color-30: var(--editorColor30); + --editor-color-10: var(--editorColor10); + --editor-color-04: var(--editorColor04); + --editor-bg-color: var(--editorBgColor); + --editor-area-width: var(--editorAreaWidth); + --delete-color: var(--deleteColor); + --icon-color: var(--iconColor); + --code-block-bg-color: var(--codeBlockBgColor); + --table-border-color: var(--tableBorderColor); + --input-bg-color: var(--inputBgColor); + --button-font-color: var(--buttonFontColor); + --button-bg-color: var(--buttonBgColor); + --button-border: var(--buttonBorder); + --button-bg-color-hover: var(--buttonBgColorHover); + --button-border-hover: var(--buttonBorderHover); + --button-bg-color-active: var(--buttonBgColorActive); + --button-border-active: var(--buttonBorderActive); + --button-border-focus: var(--button-border); + --float-bg-color: var(--floatBgColor); + --float-hover-color: var(--floatHoverColor); + --float-border-color: var(--floatBorderColor); + --float-shadow: rgb(15 15 15 / 3%) 0 0 0 1px, rgb(15 15 15 / 4%) 0 3px 6px, rgb(15 15 15 / 5%) 0 9px 24px; + --link-color: var(--linkColor); + --h1-color: var(--h1Color); + --h2-color: var(--h2Color); + --h3-color: var(--h3Color); + --h4-color: var(--h4Color); + --h5-color: var(--h5Color); + --h6-color: var(--h6Color); + --blockquote-text-color: var(--blockquoteTextColor); + --blockquote-border-color: var(--blockquoteBorderColor); + --strong-color: var(--strongColor); + --em-color: var(--emColor); + --list-marker-color: var(--listMarkerColor); + --hr-color: var(--hrColor); +} + +.editor-tabs { + background: #f3f3f3 !important; + box-shadow: none !important; +} +.editor-tabs:after { + position: absolute; + content: ''; + border-bottom: 1px solid #dddddd; + bottom: 0; + left: 0; + right: 0; + z-index: 1; +} +.editor-tabs ul.tabs-container:after { + position: absolute; + content: ''; + border-bottom: 1px solid #dddddd; + bottom: 0; + left: 0; + right: 0; + z-index: 2; +} +.title-bar-editor-bg.tabs-visible { + background: #f3f3f3 !important; +} + +.tabs-container > li { + background: none !important; +} +.tabs-container > li.active { + border: 1px solid #dddddd; + border-top: none; + border-bottom: none; + background: var(--floatBgColor) !important; +} +.tabs-container > li.active:after { + top: 0 !important; + bottom: auto !important; + background: var(--themeColor) !important; +} + +/* ------------------------------------ */ + +.mu-container li.mu-list-item { + color: var(--editorColor); +} + +/*task list*/ +li.mu-task-list-item { + list-style-type: none; + position: relative; +} +li.mu-task-list-item > input[type='checkbox'] { + position: absolute; + cursor: pointer; + width: 16px; + height: 16px; + top: 0.1em; + transform: rotate(-90deg); + margin: 0; + left: -24px; + transform-origin: center; + transition: all 0.2s ease; +} +li.mu-task-list-item > input.mu-checkbox-checked { + transform: rotate(0); + opacity: 0.5; +} +li.mu-task-list-item > input[type='checkbox']::before { + content: ''; + width: 16px; + height: 16px; + box-sizing: border-box; + display: inline-block; + border: 2px solid var(--editorColor); + border-radius: 2px; + background-color: var(--editorBgColor); + position: absolute; + top: 0; + left: 0; + transition: all 0.2s ease; +} +li.mu-task-list-item > input.mu-checkbox-checked::before { + border: transparent; + background-color: var(--editorColor); +} +li.mu-task-list-item > input::after { + content: ''; + transform: rotate(-45deg) scale(0); + width: 9px; + height: 5px; + border: 2px solid #fff; + border-top: none; + border-right: none; + position: absolute; + display: inline-block; + top: 1px; + left: 5px; + transition: all 0.2s ease; +} +li.mu-task-list-item > input.mu-checkbox-checked::after { + transform: rotate(-45deg) scale(1); +} + +/*horizontal line*/ +.mu-thematic-break:not(.mu-active)::before { + content: ''; + position: absolute; + width: 100%; + display: block; + left: 50%; + top: 50%; + height: 2px; + box-sizing: border-box; + border: none; + border-bottom: 2px dashed var(--editorColor50); + transform: translateX(-50%) translateY(-50%); +} diff --git a/packages/desktop/src/renderer/src/assets/themes/gruvbox-dark.theme.css b/packages/desktop/src/renderer/src/assets/themes/gruvbox-dark.theme.css new file mode 100644 index 0000000000..41a4b95085 --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/gruvbox-dark.theme.css @@ -0,0 +1,214 @@ +:root { + /*editor - Gruvbox Dark theme*/ + --themeColor: #83a598; + --themeColor90: rgba(131, 165, 152, 0.9); + --themeColor80: rgba(131, 165, 152, 0.8); + --themeColor70: rgba(131, 165, 152, 0.7); + --themeColor60: rgba(131, 165, 152, 0.6); + --themeColor50: rgba(131, 165, 152, 0.5); + --themeColor40: rgba(131, 165, 152, 0.4); + --themeColor30: rgba(131, 165, 152, 0.3); + --themeColor20: rgba(131, 165, 152, 0.2); + --themeColor10: rgba(131, 165, 152, 0.1); + + --highlightColor: rgba(250, 189, 47, 0.6); + --selectionColor: rgba(146, 131, 116, 0.4); + --editorColor: #ebdbb2; + --editorColor80: rgba(235, 219, 178, 0.8); + --editorColor60: rgba(235, 219, 178, 0.6); + --editorColor50: rgba(235, 219, 178, 0.5); + --editorColor40: rgba(235, 219, 178, 0.4); + --editorColor30: rgba(235, 219, 178, 0.3); + --editorColor10: rgba(235, 219, 178, 0.1); + --editorColor04: rgba(235, 219, 178, 0.04); + --editorBgColor: #282828; + --deleteColor: #fb4934; + --iconColor: rgba(235, 219, 178, 0.56); + --codeBgColor: #3c3836; + --codeBlockBgColor: #3c3836; + --footnoteBgColor: rgba(60, 56, 54, 0.3); + --inputBgColor: #1d2021; + + --focusColor: var(--themeColor); + + --buttonFontColor: rgba(235, 219, 178, 0.6); + --buttonBgColor: #3c3836; + --buttonBorder: 1px solid rgba(0, 0, 0, 0.2); + --buttonShadow: none; + --buttonFontColorHover: var(--buttonFontColor); + --buttonBgColorHover: #504945; + --buttonBorderHover: 1px solid rgba(0, 0, 0, 0.3); + --buttonFontColorActive: var(--buttonFontColor); + --buttonBgColorActive: #32302f; + --buttonBorderActive: var(--buttonBorder); + + --buttonPrimaryFontColor: #282828; + --buttonPrimaryBgColor: var(--themeColor); + --buttonPrimaryBorder: none; + --buttonPrimaryShadow: none; + --buttonPrimaryFontColorHover: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorHover: #8ec07c; + --buttonPrimaryBorderHover: var(--buttonPrimaryBorder); + --buttonPrimaryFontColorActive: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorActive: #689d6a; + --buttonPrimaryBorderActive: var(--buttonPrimaryBorder); + --buttonPrimaryFocusBorder: none; + --buttonPrimaryFocusShadow: inset 0 0 0 1px rgba(24, 26, 31, 0.5), 0 0 0 1px var(--themeColor); + --tableBorderColor: #3c3836; + + /* Markdown element colors */ + --headingColor: #ebdbb2; + --h1Color: #fb4934; + --h2Color: #fabd2f; + --h3Color: #b8bb26; + --h4Color: #83a598; + --h5Color: #d3869b; + --h6Color: #8ec07c; + --blockquoteTextColor: rgba(235, 219, 178, 0.6); + --blockquoteBorderColor: #665c54; + --hrColor: #504945; + --linkColor: #83a598; + --strongColor: #ebdbb2; + --emColor: #fabd2f; + --listMarkerColor: #fe8019; + + /*marktext*/ + --sideBarColor: rgba(235, 219, 178, 0.6); + --sideBarIconColor: var(--iconColor); + --sideBarTitleColor: rgba(235, 219, 178, 0.8); + --sideBarTextColor: rgba(235, 219, 178, 0.4); + --sideBarBgColor: #222424; + --sideBarItemHoverBgColor: rgba(235, 219, 178, 0.03); + --itemBgColor: #3c3836; + + --floatFontColor: rgba(235, 219, 178, 0.7); + --floatBgColor: #3c3836; + --floatHoverColor: rgba(235, 219, 178, 0.04); + --floatBorderColor: rgba(0, 0, 0, 0.05); + --floatShadow: rgba(0, 0, 0, 0.2); + --maskColor: rgba(0, 0, 0, 0.7); + --editorAreaWidth: 750px; + + /* @muyajs/core (kebab-case) editor variables + Additive prep for the muyajs -> @muyajs/core editor swap. These + mirror the camelCase vars above; the legacy editor ignores them. + Do NOT remove the camelCase vars — they still drive the current + editor until editor.vue switches engines. */ + --theme-color: var(--themeColor); + --highlight-color: var(--highlightColor); + --selection-color: var(--selectionColor); + --editor-color: var(--editorColor); + --editor-color-80: var(--editorColor80); + --editor-color-50: var(--editorColor50); + --editor-color-30: var(--editorColor30); + --editor-color-10: var(--editorColor10); + --editor-color-04: var(--editorColor04); + --editor-bg-color: var(--editorBgColor); + --editor-area-width: var(--editorAreaWidth); + --delete-color: var(--deleteColor); + --icon-color: var(--iconColor); + --code-block-bg-color: var(--codeBlockBgColor); + --table-border-color: var(--tableBorderColor); + --input-bg-color: var(--inputBgColor); + --button-font-color: var(--buttonFontColor); + --button-bg-color: var(--buttonBgColor); + --button-border: var(--buttonBorder); + --button-bg-color-hover: var(--buttonBgColorHover); + --button-border-hover: var(--buttonBorderHover); + --button-bg-color-active: var(--buttonBgColorActive); + --button-border-active: var(--buttonBorderActive); + --button-border-focus: var(--button-border); + --float-bg-color: var(--floatBgColor); + --float-hover-color: var(--floatHoverColor); + --float-border-color: var(--floatBorderColor); + --float-shadow: rgb(15 15 15 / 3%) 0 0 0 1px, rgb(15 15 15 / 4%) 0 3px 6px, rgb(15 15 15 / 5%) 0 9px 24px; + --link-color: var(--linkColor); + --h1-color: var(--h1Color); + --h2-color: var(--h2Color); + --h3-color: var(--h3Color); + --h4-color: var(--h4Color); + --h5-color: var(--h5Color); + --h6-color: var(--h6Color); + --blockquote-text-color: var(--blockquoteTextColor); + --blockquote-border-color: var(--blockquoteBorderColor); + --strong-color: var(--strongColor); + --em-color: var(--emColor); + --list-marker-color: var(--listMarkerColor); + --hr-color: var(--hrColor); +} + +::-webkit-scrollbar { + background: var(--editorBgColor); +} + +.mu-front-menu, +.mu-float-wrapper { + box-shadow: 0 4px 8px 0 var(--floatShadow) !important; +} + +.title-bar .frameless-titlebar-button > div > svg { + fill: #ebdbb2; +} +.title-bar .frameless-titlebar-minimize:hover, +.title-bar .frameless-titlebar-toggle:hover { + background-color: rgba(235, 219, 178, 0.05); +} + +.side-bar { + border-right: 1px solid #1d2021 !important; +} + +.recent-files-projects a, +.open-project a { + box-shadow: none !important; +} + +.editor-tabs { + box-shadow: none !important; +} +.editor-tabs:after { + position: absolute; + content: ''; + border-bottom: 1px solid #1d2021; + bottom: 0; + left: 0; + right: 0; + z-index: 1; +} +.editor-tabs ul.tabs-container:after { + position: absolute; + content: ''; + border-bottom: 1px solid #1d2021; + bottom: 0; + left: 0; + right: 0; + z-index: 2; +} + +.tabs-container > li, +.tabs-container > li.active { + background: var(--editorBgColor) !important; +} + +.open-project button, +.recent-files-projects button { + box-shadow: none !important; +} + +/* ------------------------------------ */ + +:not(pre) > code[class*='language-'], +pre:not(.CodeMirror-line), +pre[class*='language-'], +pre.mu-code-block { + background: var(--codeBlockBgColor) !important; + border: none !important; +} +.mu-thematic-break:not(.mu-active)::before { + border-top: 2px dashed var(--editorColor10) !important; + background: none !important; +} +figure.mu-active.mu-math-block > div.mu-math-preview, +figure.mu-active.mu-diagram-block > div.mu-diagram-preview { + box-shadow: 0 3px 8px 0 var(--floatShadow) !important; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/gruvbox-light.theme.css b/packages/desktop/src/renderer/src/assets/themes/gruvbox-light.theme.css new file mode 100644 index 0000000000..f78b65dd6c --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/gruvbox-light.theme.css @@ -0,0 +1,214 @@ +:root { + /*editor - Gruvbox Light theme*/ + --themeColor: #458588; + --themeColor90: rgba(69, 133, 136, 0.9); + --themeColor80: rgba(69, 133, 136, 0.8); + --themeColor70: rgba(69, 133, 136, 0.7); + --themeColor60: rgba(69, 133, 136, 0.6); + --themeColor50: rgba(69, 133, 136, 0.5); + --themeColor40: rgba(69, 133, 136, 0.4); + --themeColor30: rgba(69, 133, 136, 0.3); + --themeColor20: rgba(69, 133, 136, 0.2); + --themeColor10: rgba(69, 133, 136, 0.1); + + --highlightColor: rgba(69, 133, 136, 0.3); + --selectionColor: rgba(235, 219, 178, 0.6); + --editorColor: #3c3836; + --editorColor80: rgba(60, 56, 54, 0.8); + --editorColor60: rgba(60, 56, 54, 0.6); + --editorColor50: rgba(60, 56, 54, 0.5); + --editorColor40: rgba(60, 56, 54, 0.4); + --editorColor30: rgba(60, 56, 54, 0.3); + --editorColor10: rgba(60, 56, 54, 0.1); + --editorColor04: rgba(60, 56, 54, 0.04); + --editorBgColor: #fbf1c7; + --deleteColor: #cc241d; + --iconColor: rgba(60, 56, 54, 0.56); + --codeBgColor: #ebdbb2; + --codeBlockBgColor: #ebdbb2; + --footnoteBgColor: rgba(235, 219, 178, 0.5); + --inputBgColor: #f2e5bc; + + --focusColor: var(--themeColor); + + --buttonFontColor: rgba(60, 56, 54, 0.7); + --buttonBgColor: #ebdbb2; + --buttonBorder: 1px solid rgba(0, 0, 0, 0.08); + --buttonShadow: none; + --buttonFontColorHover: var(--buttonFontColor); + --buttonBgColorHover: #e2d1a7; + --buttonBorderHover: 1px solid rgba(0, 0, 0, 0.12); + --buttonFontColorActive: var(--buttonFontColor); + --buttonBgColorActive: #d9c79c; + --buttonBorderActive: var(--buttonBorder); + + --buttonPrimaryFontColor: #fbf1c7; + --buttonPrimaryBgColor: var(--themeColor); + --buttonPrimaryBorder: none; + --buttonPrimaryShadow: none; + --buttonPrimaryFontColorHover: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorHover: #689d6a; + --buttonPrimaryBorderHover: var(--buttonPrimaryBorder); + --buttonPrimaryFontColorActive: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorActive: #366c6e; + --buttonPrimaryBorderActive: var(--buttonPrimaryBorder); + --buttonPrimaryFocusBorder: none; + --buttonPrimaryFocusShadow: inset 0 0 0 1px rgba(255, 255, 255, 0.5), 0 0 0 1px var(--themeColor); + --tableBorderColor: #d9c79c; + + /* Markdown element colors */ + --headingColor: #3c3836; + --h1Color: #cc241d; + --h2Color: #d79921; + --h3Color: #98971a; + --h4Color: #458588; + --h5Color: #b16286; + --h6Color: #689d6a; + --blockquoteTextColor: rgba(60, 56, 54, 0.6); + --blockquoteBorderColor: #928374; + --hrColor: #d9c79c; + --linkColor: #458588; + --strongColor: #3c3836; + --emColor: #d79921; + --listMarkerColor: #d65d0e; + + /*marktext*/ + --sideBarColor: rgba(60, 56, 54, 0.7); + --sideBarIconColor: var(--iconColor); + --sideBarTitleColor: rgba(60, 56, 54, 0.9); + --sideBarTextColor: rgba(60, 56, 54, 0.5); + --sideBarBgColor: #f3e6bc; + --sideBarItemHoverBgColor: rgba(60, 56, 54, 0.05); + --itemBgColor: #e2d1a7; + + --floatFontColor: rgba(60, 56, 54, 0.8); + --floatBgColor: #ebdbb2; + --floatHoverColor: rgba(60, 56, 54, 0.06); + --floatBorderColor: rgba(0, 0, 0, 0.08); + --floatShadow: rgba(0, 0, 0, 0.1); + --maskColor: rgba(0, 0, 0, 0.3); + --editorAreaWidth: 750px; + + /* @muyajs/core (kebab-case) editor variables + Additive prep for the muyajs -> @muyajs/core editor swap. These + mirror the camelCase vars above; the legacy editor ignores them. + Do NOT remove the camelCase vars — they still drive the current + editor until editor.vue switches engines. */ + --theme-color: var(--themeColor); + --highlight-color: var(--highlightColor); + --selection-color: var(--selectionColor); + --editor-color: var(--editorColor); + --editor-color-80: var(--editorColor80); + --editor-color-50: var(--editorColor50); + --editor-color-30: var(--editorColor30); + --editor-color-10: var(--editorColor10); + --editor-color-04: var(--editorColor04); + --editor-bg-color: var(--editorBgColor); + --editor-area-width: var(--editorAreaWidth); + --delete-color: var(--deleteColor); + --icon-color: var(--iconColor); + --code-block-bg-color: var(--codeBlockBgColor); + --table-border-color: var(--tableBorderColor); + --input-bg-color: var(--inputBgColor); + --button-font-color: var(--buttonFontColor); + --button-bg-color: var(--buttonBgColor); + --button-border: var(--buttonBorder); + --button-bg-color-hover: var(--buttonBgColorHover); + --button-border-hover: var(--buttonBorderHover); + --button-bg-color-active: var(--buttonBgColorActive); + --button-border-active: var(--buttonBorderActive); + --button-border-focus: var(--button-border); + --float-bg-color: var(--floatBgColor); + --float-hover-color: var(--floatHoverColor); + --float-border-color: var(--floatBorderColor); + --float-shadow: rgb(15 15 15 / 3%) 0 0 0 1px, rgb(15 15 15 / 4%) 0 3px 6px, rgb(15 15 15 / 5%) 0 9px 24px; + --link-color: var(--linkColor); + --h1-color: var(--h1Color); + --h2-color: var(--h2Color); + --h3-color: var(--h3Color); + --h4-color: var(--h4Color); + --h5-color: var(--h5Color); + --h6-color: var(--h6Color); + --blockquote-text-color: var(--blockquoteTextColor); + --blockquote-border-color: var(--blockquoteBorderColor); + --strong-color: var(--strongColor); + --em-color: var(--emColor); + --list-marker-color: var(--listMarkerColor); + --hr-color: var(--hrColor); +} + +::-webkit-scrollbar { + background: var(--editorBgColor); +} + +.mu-front-menu, +.mu-float-wrapper { + box-shadow: 0 4px 8px 0 var(--floatShadow) !important; +} + +.title-bar .frameless-titlebar-button > div > svg { + fill: #3c3836; +} +.title-bar .frameless-titlebar-minimize:hover, +.title-bar .frameless-titlebar-toggle:hover { + background-color: rgba(60, 56, 54, 0.08); +} + +.side-bar { + border-right: 1px solid #d9c79c !important; +} + +.recent-files-projects a, +.open-project a { + box-shadow: none !important; +} + +.editor-tabs { + box-shadow: none !important; +} +.editor-tabs:after { + position: absolute; + content: ''; + border-bottom: 1px solid #d9c79c; + bottom: 0; + left: 0; + right: 0; + z-index: 1; +} +.editor-tabs ul.tabs-container:after { + position: absolute; + content: ''; + border-bottom: 1px solid #d9c79c; + bottom: 0; + left: 0; + right: 0; + z-index: 2; +} + +.tabs-container > li, +.tabs-container > li.active { + background: var(--editorBgColor) !important; +} + +.open-project button, +.recent-files-projects button { + box-shadow: none !important; +} + +/* ------------------------------------ */ + +:not(pre) > code[class*='language-'], +pre:not(.CodeMirror-line), +pre[class*='language-'], +pre.mu-code-block { + background: var(--codeBlockBgColor) !important; + border: none !important; +} +.mu-thematic-break:not(.mu-active)::before { + border-top: 2px dashed var(--editorColor10) !important; + background: none !important; +} +figure.mu-active.mu-math-block > div.mu-math-preview, +figure.mu-active.mu-diagram-block > div.mu-diagram-preview { + box-shadow: 0 3px 8px 0 var(--floatShadow) !important; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/horizon-dark.theme.css b/packages/desktop/src/renderer/src/assets/themes/horizon-dark.theme.css new file mode 100644 index 0000000000..ba9548def2 --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/horizon-dark.theme.css @@ -0,0 +1,214 @@ +:root { + /*editor - Horizon Dark theme*/ + --themeColor: #e95678; + --themeColor90: rgba(233, 86, 120, 0.9); + --themeColor80: rgba(233, 86, 120, 0.8); + --themeColor70: rgba(233, 86, 120, 0.7); + --themeColor60: rgba(233, 86, 120, 0.6); + --themeColor50: rgba(233, 86, 120, 0.5); + --themeColor40: rgba(233, 86, 120, 0.4); + --themeColor30: rgba(233, 86, 120, 0.3); + --themeColor20: rgba(233, 86, 120, 0.2); + --themeColor10: rgba(233, 86, 120, 0.1); + + --highlightColor: rgba(233, 86, 120, 0.6); + --selectionColor: rgba(45, 48, 59, 0.8); + --editorColor: #d5d8da; + --editorColor80: rgba(213, 216, 218, 0.8); + --editorColor60: rgba(213, 216, 218, 0.6); + --editorColor50: rgba(213, 216, 218, 0.5); + --editorColor40: rgba(213, 216, 218, 0.4); + --editorColor30: rgba(213, 216, 218, 0.3); + --editorColor10: rgba(213, 216, 218, 0.1); + --editorColor04: rgba(213, 216, 218, 0.04); + --editorBgColor: #1c1e26; + --deleteColor: #e95678; + --iconColor: rgba(213, 216, 218, 0.56); + --codeBgColor: #232530; + --codeBlockBgColor: #232530; + --footnoteBgColor: rgba(35, 37, 48, 0.3); + --inputBgColor: #16181f; + + --focusColor: var(--themeColor); + + --buttonFontColor: rgba(213, 216, 218, 0.6); + --buttonBgColor: #232530; + --buttonBorder: 1px solid rgba(0, 0, 0, 0.2); + --buttonShadow: none; + --buttonFontColorHover: var(--buttonFontColor); + --buttonBgColorHover: #2e303d; + --buttonBorderHover: 1px solid rgba(0, 0, 0, 0.3); + --buttonFontColorActive: var(--buttonFontColor); + --buttonBgColorActive: #1d1f27; + --buttonBorderActive: var(--buttonBorder); + + --buttonPrimaryFontColor: #ffffff; + --buttonPrimaryBgColor: var(--themeColor); + --buttonPrimaryBorder: none; + --buttonPrimaryShadow: none; + --buttonPrimaryFontColorHover: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorHover: #fab795; + --buttonPrimaryBorderHover: var(--buttonPrimaryBorder); + --buttonPrimaryFontColorActive: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorActive: #d04565; + --buttonPrimaryBorderActive: var(--buttonPrimaryBorder); + --buttonPrimaryFocusBorder: none; + --buttonPrimaryFocusShadow: inset 0 0 0 1px rgba(24, 26, 31, 0.5), 0 0 0 1px var(--themeColor); + --tableBorderColor: #232530; + + /* Markdown element colors */ + --headingColor: #d5d8da; + --h1Color: #e95678; + --h2Color: #fab795; + --h3Color: #fac29a; + --h4Color: #29d398; + --h5Color: #26bbd9; + --h6Color: #ee64ae; + --blockquoteTextColor: rgba(213, 216, 218, 0.6); + --blockquoteBorderColor: #3b3e4a; + --hrColor: #2e303d; + --linkColor: #26bbd9; + --strongColor: #d5d8da; + --emColor: #fab795; + --listMarkerColor: #e95678; + + /*marktext*/ + --sideBarColor: rgba(213, 216, 218, 0.6); + --sideBarIconColor: var(--iconColor); + --sideBarTitleColor: rgba(213, 216, 218, 0.8); + --sideBarTextColor: rgba(213, 216, 218, 0.4); + --sideBarBgColor: #191b22; + --sideBarItemHoverBgColor: rgba(213, 216, 218, 0.03); + --itemBgColor: #232530; + + --floatFontColor: rgba(213, 216, 218, 0.7); + --floatBgColor: #232530; + --floatHoverColor: rgba(213, 216, 218, 0.04); + --floatBorderColor: rgba(0, 0, 0, 0.05); + --floatShadow: rgba(0, 0, 0, 0.2); + --maskColor: rgba(0, 0, 0, 0.7); + --editorAreaWidth: 750px; + + /* @muyajs/core (kebab-case) editor variables + Additive prep for the muyajs -> @muyajs/core editor swap. These + mirror the camelCase vars above; the legacy editor ignores them. + Do NOT remove the camelCase vars — they still drive the current + editor until editor.vue switches engines. */ + --theme-color: var(--themeColor); + --highlight-color: var(--highlightColor); + --selection-color: var(--selectionColor); + --editor-color: var(--editorColor); + --editor-color-80: var(--editorColor80); + --editor-color-50: var(--editorColor50); + --editor-color-30: var(--editorColor30); + --editor-color-10: var(--editorColor10); + --editor-color-04: var(--editorColor04); + --editor-bg-color: var(--editorBgColor); + --editor-area-width: var(--editorAreaWidth); + --delete-color: var(--deleteColor); + --icon-color: var(--iconColor); + --code-block-bg-color: var(--codeBlockBgColor); + --table-border-color: var(--tableBorderColor); + --input-bg-color: var(--inputBgColor); + --button-font-color: var(--buttonFontColor); + --button-bg-color: var(--buttonBgColor); + --button-border: var(--buttonBorder); + --button-bg-color-hover: var(--buttonBgColorHover); + --button-border-hover: var(--buttonBorderHover); + --button-bg-color-active: var(--buttonBgColorActive); + --button-border-active: var(--buttonBorderActive); + --button-border-focus: var(--button-border); + --float-bg-color: var(--floatBgColor); + --float-hover-color: var(--floatHoverColor); + --float-border-color: var(--floatBorderColor); + --float-shadow: rgb(15 15 15 / 3%) 0 0 0 1px, rgb(15 15 15 / 4%) 0 3px 6px, rgb(15 15 15 / 5%) 0 9px 24px; + --link-color: var(--linkColor); + --h1-color: var(--h1Color); + --h2-color: var(--h2Color); + --h3-color: var(--h3Color); + --h4-color: var(--h4Color); + --h5-color: var(--h5Color); + --h6-color: var(--h6Color); + --blockquote-text-color: var(--blockquoteTextColor); + --blockquote-border-color: var(--blockquoteBorderColor); + --strong-color: var(--strongColor); + --em-color: var(--emColor); + --list-marker-color: var(--listMarkerColor); + --hr-color: var(--hrColor); +} + +::-webkit-scrollbar { + background: var(--editorBgColor); +} + +.mu-front-menu, +.mu-float-wrapper { + box-shadow: 0 4px 8px 0 var(--floatShadow) !important; +} + +.title-bar .frameless-titlebar-button > div > svg { + fill: #d5d8da; +} +.title-bar .frameless-titlebar-minimize:hover, +.title-bar .frameless-titlebar-toggle:hover { + background-color: rgba(213, 216, 218, 0.05); +} + +.side-bar { + border-right: 1px solid #121419 !important; +} + +.recent-files-projects a, +.open-project a { + box-shadow: none !important; +} + +.editor-tabs { + box-shadow: none !important; +} +.editor-tabs:after { + position: absolute; + content: ''; + border-bottom: 1px solid #121419; + bottom: 0; + left: 0; + right: 0; + z-index: 1; +} +.editor-tabs ul.tabs-container:after { + position: absolute; + content: ''; + border-bottom: 1px solid #121419; + bottom: 0; + left: 0; + right: 0; + z-index: 2; +} + +.tabs-container > li, +.tabs-container > li.active { + background: var(--editorBgColor) !important; +} + +.open-project button, +.recent-files-projects button { + box-shadow: none !important; +} + +/* ------------------------------------ */ + +:not(pre) > code[class*='language-'], +pre:not(.CodeMirror-line), +pre[class*='language-'], +pre.mu-code-block { + background: var(--codeBlockBgColor) !important; + border: none !important; +} +.mu-thematic-break:not(.mu-active)::before { + border-top: 2px dashed var(--editorColor10) !important; + background: none !important; +} +figure.mu-active.mu-math-block > div.mu-math-preview, +figure.mu-active.mu-diagram-block > div.mu-diagram-preview { + box-shadow: 0 3px 8px 0 var(--floatShadow) !important; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/kanagawa.theme.css b/packages/desktop/src/renderer/src/assets/themes/kanagawa.theme.css new file mode 100644 index 0000000000..2e9fa17b72 --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/kanagawa.theme.css @@ -0,0 +1,214 @@ +:root { + /*editor - Kanagawa theme*/ + --themeColor: #7e9cd8; + --themeColor90: rgba(126, 156, 216, 0.9); + --themeColor80: rgba(126, 156, 216, 0.8); + --themeColor70: rgba(126, 156, 216, 0.7); + --themeColor60: rgba(126, 156, 216, 0.6); + --themeColor50: rgba(126, 156, 216, 0.5); + --themeColor40: rgba(126, 156, 216, 0.4); + --themeColor30: rgba(126, 156, 216, 0.3); + --themeColor20: rgba(126, 156, 216, 0.2); + --themeColor10: rgba(126, 156, 216, 0.1); + + --highlightColor: rgba(126, 156, 216, 0.6); + --selectionColor: rgba(43, 51, 68, 0.8); + --editorColor: #dcd7ba; + --editorColor80: rgba(220, 215, 186, 0.8); + --editorColor60: rgba(220, 215, 186, 0.6); + --editorColor50: rgba(220, 215, 186, 0.5); + --editorColor40: rgba(220, 215, 186, 0.4); + --editorColor30: rgba(220, 215, 186, 0.3); + --editorColor10: rgba(220, 215, 186, 0.1); + --editorColor04: rgba(220, 215, 186, 0.04); + --editorBgColor: #1f1f28; + --deleteColor: #c34043; + --iconColor: rgba(220, 215, 186, 0.56); + --codeBgColor: #2a2a37; + --codeBlockBgColor: #2a2a37; + --footnoteBgColor: rgba(42, 42, 55, 0.3); + --inputBgColor: #16161d; + + --focusColor: var(--themeColor); + + --buttonFontColor: rgba(220, 215, 186, 0.6); + --buttonBgColor: #2a2a37; + --buttonBorder: 1px solid rgba(0, 0, 0, 0.2); + --buttonShadow: none; + --buttonFontColorHover: var(--buttonFontColor); + --buttonBgColorHover: #363646; + --buttonBorderHover: 1px solid rgba(0, 0, 0, 0.3); + --buttonFontColorActive: var(--buttonFontColor); + --buttonBgColorActive: #23232e; + --buttonBorderActive: var(--buttonBorder); + + --buttonPrimaryFontColor: #1f1f28; + --buttonPrimaryBgColor: var(--themeColor); + --buttonPrimaryBorder: none; + --buttonPrimaryShadow: none; + --buttonPrimaryFontColorHover: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorHover: #7fb4ca; + --buttonPrimaryBorderHover: var(--buttonPrimaryBorder); + --buttonPrimaryFontColorActive: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorActive: #6182b8; + --buttonPrimaryBorderActive: var(--buttonPrimaryBorder); + --buttonPrimaryFocusBorder: none; + --buttonPrimaryFocusShadow: inset 0 0 0 1px rgba(24, 26, 31, 0.5), 0 0 0 1px var(--themeColor); + --tableBorderColor: #2a2a37; + + /* Markdown element colors */ + --headingColor: #dcd7ba; + --h1Color: #c34043; + --h2Color: #dca561; + --h3Color: #98bb6c; + --h4Color: #7fb4ca; + --h5Color: #d27e99; + --h6Color: #957fb8; + --blockquoteTextColor: rgba(220, 215, 186, 0.6); + --blockquoteBorderColor: #54546d; + --hrColor: #363646; + --linkColor: #7e9cd8; + --strongColor: #dcd7ba; + --emColor: #ff5d62; + --listMarkerColor: #e6c384; + + /*marktext*/ + --sideBarColor: rgba(220, 215, 186, 0.6); + --sideBarIconColor: var(--iconColor); + --sideBarTitleColor: rgba(220, 215, 186, 0.8); + --sideBarTextColor: rgba(220, 215, 186, 0.4); + --sideBarBgColor: #1a1a22; + --sideBarItemHoverBgColor: rgba(220, 215, 186, 0.03); + --itemBgColor: #2a2a37; + + --floatFontColor: rgba(220, 215, 186, 0.7); + --floatBgColor: #2a2a37; + --floatHoverColor: rgba(220, 215, 186, 0.04); + --floatBorderColor: rgba(0, 0, 0, 0.05); + --floatShadow: rgba(0, 0, 0, 0.2); + --maskColor: rgba(0, 0, 0, 0.7); + --editorAreaWidth: 750px; + + /* @muyajs/core (kebab-case) editor variables + Additive prep for the muyajs -> @muyajs/core editor swap. These + mirror the camelCase vars above; the legacy editor ignores them. + Do NOT remove the camelCase vars — they still drive the current + editor until editor.vue switches engines. */ + --theme-color: var(--themeColor); + --highlight-color: var(--highlightColor); + --selection-color: var(--selectionColor); + --editor-color: var(--editorColor); + --editor-color-80: var(--editorColor80); + --editor-color-50: var(--editorColor50); + --editor-color-30: var(--editorColor30); + --editor-color-10: var(--editorColor10); + --editor-color-04: var(--editorColor04); + --editor-bg-color: var(--editorBgColor); + --editor-area-width: var(--editorAreaWidth); + --delete-color: var(--deleteColor); + --icon-color: var(--iconColor); + --code-block-bg-color: var(--codeBlockBgColor); + --table-border-color: var(--tableBorderColor); + --input-bg-color: var(--inputBgColor); + --button-font-color: var(--buttonFontColor); + --button-bg-color: var(--buttonBgColor); + --button-border: var(--buttonBorder); + --button-bg-color-hover: var(--buttonBgColorHover); + --button-border-hover: var(--buttonBorderHover); + --button-bg-color-active: var(--buttonBgColorActive); + --button-border-active: var(--buttonBorderActive); + --button-border-focus: var(--button-border); + --float-bg-color: var(--floatBgColor); + --float-hover-color: var(--floatHoverColor); + --float-border-color: var(--floatBorderColor); + --float-shadow: rgb(15 15 15 / 3%) 0 0 0 1px, rgb(15 15 15 / 4%) 0 3px 6px, rgb(15 15 15 / 5%) 0 9px 24px; + --link-color: var(--linkColor); + --h1-color: var(--h1Color); + --h2-color: var(--h2Color); + --h3-color: var(--h3Color); + --h4-color: var(--h4Color); + --h5-color: var(--h5Color); + --h6-color: var(--h6Color); + --blockquote-text-color: var(--blockquoteTextColor); + --blockquote-border-color: var(--blockquoteBorderColor); + --strong-color: var(--strongColor); + --em-color: var(--emColor); + --list-marker-color: var(--listMarkerColor); + --hr-color: var(--hrColor); +} + +::-webkit-scrollbar { + background: var(--editorBgColor); +} + +.mu-front-menu, +.mu-float-wrapper { + box-shadow: 0 4px 8px 0 var(--floatShadow) !important; +} + +.title-bar .frameless-titlebar-button > div > svg { + fill: #dcd7ba; +} +.title-bar .frameless-titlebar-minimize:hover, +.title-bar .frameless-titlebar-toggle:hover { + background-color: rgba(220, 215, 186, 0.05); +} + +.side-bar { + border-right: 1px solid #111116 !important; +} + +.recent-files-projects a, +.open-project a { + box-shadow: none !important; +} + +.editor-tabs { + box-shadow: none !important; +} +.editor-tabs:after { + position: absolute; + content: ''; + border-bottom: 1px solid #111116; + bottom: 0; + left: 0; + right: 0; + z-index: 1; +} +.editor-tabs ul.tabs-container:after { + position: absolute; + content: ''; + border-bottom: 1px solid #111116; + bottom: 0; + left: 0; + right: 0; + z-index: 2; +} + +.tabs-container > li, +.tabs-container > li.active { + background: var(--editorBgColor) !important; +} + +.open-project button, +.recent-files-projects button { + box-shadow: none !important; +} + +/* ------------------------------------ */ + +:not(pre) > code[class*='language-'], +pre:not(.CodeMirror-line), +pre[class*='language-'], +pre.mu-code-block { + background: var(--codeBlockBgColor) !important; + border: none !important; +} +.mu-thematic-break:not(.mu-active)::before { + border-top: 2px dashed var(--editorColor10) !important; + background: none !important; +} +figure.mu-active.mu-math-block > div.mu-math-preview, +figure.mu-active.mu-diagram-block > div.mu-diagram-preview { + box-shadow: 0 3px 8px 0 var(--floatShadow) !important; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/material-dark.theme.css b/packages/desktop/src/renderer/src/assets/themes/material-dark.theme.css new file mode 100644 index 0000000000..1c35c651ca --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/material-dark.theme.css @@ -0,0 +1,189 @@ +:root { + /*editor*/ + --themeColor: #f48237; + --themeColor90: rgba(244, 130, 55, 0.9); + --themeColor80: rgba(244, 130, 55, 0.8); + --themeColor70: rgba(244, 130, 55, 0.7); + --themeColor60: rgba(244, 130, 55, 0.6); + --themeColor50: rgba(244, 130, 55, 0.5); + --themeColor40: rgba(244, 130, 55, 0.4); + --themeColor30: rgba(244, 130, 55, 0.3); + --themeColor20: rgba(244, 130, 55, 0.2); + --themeColor10: rgba(244, 130, 55, 0.1); + + --highlightColor: rgba(244, 130, 55, 0.4); + --selectionColor: rgba(255, 255, 255, 0.2); + --editorColor: rgba(171, 178, 191, 0.8); + --editorColor80: rgba(171, 178, 191, 1); + --editorColor60: rgba(171, 178, 191, 0.9); + --editorColor50: rgba(171, 178, 191, 0.9); + --editorColor40: rgba(171, 178, 191, 0.9); + --editorColor30: rgba(171, 178, 191, 0.8); + --editorColor10: rgba(171, 178, 191, 0.5); + --editorColor04: rgba(171, 178, 191, 0.04); + --editorBgColor: #34393f; + --deleteColor: #ff6969; + --iconColor: rgba(255, 255, 255, 0.56); + --codeBgColor: #d8d8d869; + --codeBlockBgColor: #3f454c; + --footnoteBgColor: rgba(66, 67, 68, 0.5); + --inputBgColor: rgba(0, 0, 0, 0.1); + --focusColor: var(--themeColor); + + --buttonFontColor: rgba(255, 255, 255, 0.6); + --buttonBgColor: #58606a; + --buttonBorder: 1px solid #383d44; + --buttonShadow: none; + --buttonFontColorHover: var(--buttonFontColor); + --buttonBgColorHover: #646d78; + --buttonBorderHover: var(--buttonBorder); + --buttonFontColorActive: var(--buttonFontColor); + --buttonBgColorActive: #4a5159; + --buttonBorderActive: var(--buttonBorder); + + --buttonPrimaryFontColor: #ffffff; + --buttonPrimaryBgColor: var(--themeColor); + --buttonPrimaryBorder: none; + --buttonPrimaryShadow: none; + --buttonPrimaryFontColorHover: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorHover: #f5914f; + --buttonPrimaryBorderHover: var(--buttonPrimaryBorder); + --buttonPrimaryFontColorActive: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorActive: #f3731f; + --buttonPrimaryBorderActive: var(--buttonPrimaryBorder); + --buttonPrimaryFocusBorder: none; + --buttonPrimaryFocusShadow: inset 0 0 0 1px rgba(145, 75, 29, 0.6), 0 0 0 1px var(--themeColor); + --tableBorderColor: #4f585e; + + /* Markdown element colors */ + --headingColor: rgba(171, 178, 191, 1); + --h1Color: #f48237; + --h2Color: #56b6c2; + --h3Color: #c678dd; + --h4Color: #98c379; + --h5Color: #e5c07b; + --h6Color: #61afef; + --blockquoteTextColor: rgba(171, 178, 191, 0.7); + --blockquoteBorderColor: #f48237; + --hrColor: rgba(171, 178, 191, 0.3); + --linkColor: #61afef; + --strongColor: rgba(171, 178, 191, 1); + --emColor: #c678dd; + --listMarkerColor: #f48237; + + /*marktext*/ + --sideBarColor: rgba(255, 255, 255, 0.6); + --sideBarIconColor: var(--iconColor); + --sideBarTitleColor: rgba(255, 255, 255, 1); + --sideBarTextColor: rgba(255, 255, 255, 0.4); + --sideBarBgColor: rgba(39, 45, 52, 0.95); + --sideBarItemHoverBgColor: rgba(255, 255, 255, 0.03); + --itemBgColor: rgba(71, 78, 86, 0.6); + + --floatFontColor: rgba(171, 178, 191, 0.8); + --floatBgColor: #3c4650; + --floatHoverColor: rgba(255, 255, 255, 0.04); + --floatBorderColor: rgba(0, 0, 0, 0.03); + --floatShadow: rgba(0, 0, 0, 0.2); + --maskColor: rgba(0, 0, 0, 0.7); + --editorAreaWidth: 750px; + + /* @muyajs/core (kebab-case) editor variables + Additive prep for the muyajs -> @muyajs/core editor swap. These + mirror the camelCase vars above; the legacy editor ignores them. + Do NOT remove the camelCase vars — they still drive the current + editor until editor.vue switches engines. */ + --theme-color: var(--themeColor); + --highlight-color: var(--highlightColor); + --selection-color: var(--selectionColor); + --editor-color: var(--editorColor); + --editor-color-80: var(--editorColor80); + --editor-color-50: var(--editorColor50); + --editor-color-30: var(--editorColor30); + --editor-color-10: var(--editorColor10); + --editor-color-04: var(--editorColor04); + --editor-bg-color: var(--editorBgColor); + --editor-area-width: var(--editorAreaWidth); + --delete-color: var(--deleteColor); + --icon-color: var(--iconColor); + --code-block-bg-color: var(--codeBlockBgColor); + --table-border-color: var(--tableBorderColor); + --input-bg-color: var(--inputBgColor); + --button-font-color: var(--buttonFontColor); + --button-bg-color: var(--buttonBgColor); + --button-border: var(--buttonBorder); + --button-bg-color-hover: var(--buttonBgColorHover); + --button-border-hover: var(--buttonBorderHover); + --button-bg-color-active: var(--buttonBgColorActive); + --button-border-active: var(--buttonBorderActive); + --button-border-focus: var(--button-border); + --float-bg-color: var(--floatBgColor); + --float-hover-color: var(--floatHoverColor); + --float-border-color: var(--floatBorderColor); + --float-shadow: rgb(15 15 15 / 3%) 0 0 0 1px, rgb(15 15 15 / 4%) 0 3px 6px, rgb(15 15 15 / 5%) 0 9px 24px; + --link-color: var(--linkColor); + --h1-color: var(--h1Color); + --h2-color: var(--h2Color); + --h3-color: var(--h3Color); + --h4-color: var(--h4Color); + --h5-color: var(--h5Color); + --h6-color: var(--h6Color); + --blockquote-text-color: var(--blockquoteTextColor); + --blockquote-border-color: var(--blockquoteBorderColor); + --strong-color: var(--strongColor); + --em-color: var(--emColor); + --list-marker-color: var(--listMarkerColor); + --hr-color: var(--hrColor); +} + +.mu-front-menu, +.mu-float-wrapper { + box-shadow: 0 4px 8px 0 var(--floatShadow) !important; +} + +.title-bar .frameless-titlebar-button > div > svg { + fill: #ffffff; +} +.title-bar .frameless-titlebar-minimize:hover, +.title-bar .frameless-titlebar-toggle:hover { + background-color: rgb(255, 255, 255, 0.05); +} + +.recent-files-projects a, +.open-project a { + box-shadow: none !important; +} + +.tabs-container > li { + background: none !important; +} +.tabs-container > li.active { + background: var(--floatBgColor) !important; +} + +.open-project button, +.recent-files-projects button { + box-shadow: none !important; +} + +/* ------------------------------------ */ + +:not(pre) > code[class*='language-'], +pre:not(.CodeMirror-line), +pre[class*='language-'], +pre.mu-code-block { + background: var(--codeBlockBgColor) !important; + border: none !important; +} +figure.mu-active.mu-math-block > div.mu-math-preview, +figure.mu-active.mu-diagram-block > div.mu-diagram-preview { + box-shadow: 0 3px 8px 0 var(--floatShadow) !important; +} + +/* + * Prism.js theme (override light theme) + */ + +/* +@import url("prismjs/dark.theme.css"); +*/ diff --git a/packages/desktop/src/renderer/src/assets/themes/monokai-pro.theme.css b/packages/desktop/src/renderer/src/assets/themes/monokai-pro.theme.css new file mode 100644 index 0000000000..50bcf261d5 --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/monokai-pro.theme.css @@ -0,0 +1,214 @@ +:root { + /*editor - Monokai Pro theme*/ + --themeColor: #ffd866; + --themeColor90: rgba(255, 216, 102, 0.9); + --themeColor80: rgba(255, 216, 102, 0.8); + --themeColor70: rgba(255, 216, 102, 0.7); + --themeColor60: rgba(255, 216, 102, 0.6); + --themeColor50: rgba(255, 216, 102, 0.5); + --themeColor40: rgba(255, 216, 102, 0.4); + --themeColor30: rgba(255, 216, 102, 0.3); + --themeColor20: rgba(255, 216, 102, 0.2); + --themeColor10: rgba(255, 216, 102, 0.1); + + --highlightColor: rgba(255, 216, 102, 0.6); + --selectionColor: rgba(73, 72, 62, 0.8); + --editorColor: #fcfcfa; + --editorColor80: rgba(252, 252, 250, 0.8); + --editorColor60: rgba(252, 252, 250, 0.6); + --editorColor50: rgba(252, 252, 250, 0.5); + --editorColor40: rgba(252, 252, 250, 0.4); + --editorColor30: rgba(252, 252, 250, 0.3); + --editorColor10: rgba(252, 252, 250, 0.1); + --editorColor04: rgba(252, 252, 250, 0.04); + --editorBgColor: #2d2a2e; + --deleteColor: #ff6188; + --iconColor: rgba(252, 252, 250, 0.56); + --codeBgColor: #403e41; + --codeBlockBgColor: #403e41; + --footnoteBgColor: rgba(64, 62, 65, 0.3); + --inputBgColor: #221f22; + + --focusColor: var(--themeColor); + + --buttonFontColor: rgba(252, 252, 250, 0.6); + --buttonBgColor: #403e41; + --buttonBorder: 1px solid rgba(0, 0, 0, 0.2); + --buttonShadow: none; + --buttonFontColorHover: var(--buttonFontColor); + --buttonBgColorHover: #5b595c; + --buttonBorderHover: 1px solid rgba(0, 0, 0, 0.3); + --buttonFontColorActive: var(--buttonFontColor); + --buttonBgColorActive: #363337; + --buttonBorderActive: var(--buttonBorder); + + --buttonPrimaryFontColor: #2d2a2e; + --buttonPrimaryBgColor: var(--themeColor); + --buttonPrimaryBorder: none; + --buttonPrimaryShadow: none; + --buttonPrimaryFontColorHover: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorHover: #78dce8; + --buttonPrimaryBorderHover: var(--buttonPrimaryBorder); + --buttonPrimaryFontColorActive: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorActive: #e6c24d; + --buttonPrimaryBorderActive: var(--buttonPrimaryBorder); + --buttonPrimaryFocusBorder: none; + --buttonPrimaryFocusShadow: inset 0 0 0 1px rgba(24, 26, 31, 0.5), 0 0 0 1px var(--themeColor); + --tableBorderColor: #403e41; + + /* Markdown element colors */ + --headingColor: #fcfcfa; + --h1Color: #ff6188; + --h2Color: #ffd866; + --h3Color: #a9dc76; + --h4Color: #78dce8; + --h5Color: #ab9df2; + --h6Color: #fc9867; + --blockquoteTextColor: rgba(252, 252, 250, 0.6); + --blockquoteBorderColor: #727072; + --hrColor: #5b595c; + --linkColor: #78dce8; + --strongColor: #fcfcfa; + --emColor: #ffd866; + --listMarkerColor: #ff6188; + + /*marktext*/ + --sideBarColor: rgba(252, 252, 250, 0.6); + --sideBarIconColor: var(--iconColor); + --sideBarTitleColor: rgba(252, 252, 250, 0.8); + --sideBarTextColor: rgba(252, 252, 250, 0.4); + --sideBarBgColor: #272428; + --sideBarItemHoverBgColor: rgba(252, 252, 250, 0.03); + --itemBgColor: #403e41; + + --floatFontColor: rgba(252, 252, 250, 0.7); + --floatBgColor: #403e41; + --floatHoverColor: rgba(252, 252, 250, 0.04); + --floatBorderColor: rgba(0, 0, 0, 0.05); + --floatShadow: rgba(0, 0, 0, 0.2); + --maskColor: rgba(0, 0, 0, 0.7); + --editorAreaWidth: 750px; + + /* @muyajs/core (kebab-case) editor variables + Additive prep for the muyajs -> @muyajs/core editor swap. These + mirror the camelCase vars above; the legacy editor ignores them. + Do NOT remove the camelCase vars — they still drive the current + editor until editor.vue switches engines. */ + --theme-color: var(--themeColor); + --highlight-color: var(--highlightColor); + --selection-color: var(--selectionColor); + --editor-color: var(--editorColor); + --editor-color-80: var(--editorColor80); + --editor-color-50: var(--editorColor50); + --editor-color-30: var(--editorColor30); + --editor-color-10: var(--editorColor10); + --editor-color-04: var(--editorColor04); + --editor-bg-color: var(--editorBgColor); + --editor-area-width: var(--editorAreaWidth); + --delete-color: var(--deleteColor); + --icon-color: var(--iconColor); + --code-block-bg-color: var(--codeBlockBgColor); + --table-border-color: var(--tableBorderColor); + --input-bg-color: var(--inputBgColor); + --button-font-color: var(--buttonFontColor); + --button-bg-color: var(--buttonBgColor); + --button-border: var(--buttonBorder); + --button-bg-color-hover: var(--buttonBgColorHover); + --button-border-hover: var(--buttonBorderHover); + --button-bg-color-active: var(--buttonBgColorActive); + --button-border-active: var(--buttonBorderActive); + --button-border-focus: var(--button-border); + --float-bg-color: var(--floatBgColor); + --float-hover-color: var(--floatHoverColor); + --float-border-color: var(--floatBorderColor); + --float-shadow: rgb(15 15 15 / 3%) 0 0 0 1px, rgb(15 15 15 / 4%) 0 3px 6px, rgb(15 15 15 / 5%) 0 9px 24px; + --link-color: var(--linkColor); + --h1-color: var(--h1Color); + --h2-color: var(--h2Color); + --h3-color: var(--h3Color); + --h4-color: var(--h4Color); + --h5-color: var(--h5Color); + --h6-color: var(--h6Color); + --blockquote-text-color: var(--blockquoteTextColor); + --blockquote-border-color: var(--blockquoteBorderColor); + --strong-color: var(--strongColor); + --em-color: var(--emColor); + --list-marker-color: var(--listMarkerColor); + --hr-color: var(--hrColor); +} + +::-webkit-scrollbar { + background: var(--editorBgColor); +} + +.mu-front-menu, +.mu-float-wrapper { + box-shadow: 0 4px 8px 0 var(--floatShadow) !important; +} + +.title-bar .frameless-titlebar-button > div > svg { + fill: #fcfcfa; +} +.title-bar .frameless-titlebar-minimize:hover, +.title-bar .frameless-titlebar-toggle:hover { + background-color: rgba(252, 252, 250, 0.05); +} + +.side-bar { + border-right: 1px solid #1b191b !important; +} + +.recent-files-projects a, +.open-project a { + box-shadow: none !important; +} + +.editor-tabs { + box-shadow: none !important; +} +.editor-tabs:after { + position: absolute; + content: ''; + border-bottom: 1px solid #1b191b; + bottom: 0; + left: 0; + right: 0; + z-index: 1; +} +.editor-tabs ul.tabs-container:after { + position: absolute; + content: ''; + border-bottom: 1px solid #1b191b; + bottom: 0; + left: 0; + right: 0; + z-index: 2; +} + +.tabs-container > li, +.tabs-container > li.active { + background: var(--editorBgColor) !important; +} + +.open-project button, +.recent-files-projects button { + box-shadow: none !important; +} + +/* ------------------------------------ */ + +:not(pre) > code[class*='language-'], +pre:not(.CodeMirror-line), +pre[class*='language-'], +pre.mu-code-block { + background: var(--codeBlockBgColor) !important; + border: none !important; +} +.mu-thematic-break:not(.mu-active)::before { + border-top: 2px dashed var(--editorColor10) !important; + background: none !important; +} +figure.mu-active.mu-math-block > div.mu-math-preview, +figure.mu-active.mu-diagram-block > div.mu-diagram-preview { + box-shadow: 0 3px 8px 0 var(--floatShadow) !important; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/nightfox.theme.css b/packages/desktop/src/renderer/src/assets/themes/nightfox.theme.css new file mode 100644 index 0000000000..27ef8e3b99 --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/nightfox.theme.css @@ -0,0 +1,214 @@ +:root { + /*editor - Nightfox theme*/ + --themeColor: #719cd6; + --themeColor90: rgba(113, 156, 214, 0.9); + --themeColor80: rgba(113, 156, 214, 0.8); + --themeColor70: rgba(113, 156, 214, 0.7); + --themeColor60: rgba(113, 156, 214, 0.6); + --themeColor50: rgba(113, 156, 214, 0.5); + --themeColor40: rgba(113, 156, 214, 0.4); + --themeColor30: rgba(113, 156, 214, 0.3); + --themeColor20: rgba(113, 156, 214, 0.2); + --themeColor10: rgba(113, 156, 214, 0.1); + + --highlightColor: rgba(113, 156, 214, 0.6); + --selectionColor: rgba(42, 55, 72, 0.8); + --editorColor: #cdcecf; + --editorColor80: rgba(205, 206, 207, 0.8); + --editorColor60: rgba(205, 206, 207, 0.6); + --editorColor50: rgba(205, 206, 207, 0.5); + --editorColor40: rgba(205, 206, 207, 0.4); + --editorColor30: rgba(205, 206, 207, 0.3); + --editorColor10: rgba(205, 206, 207, 0.1); + --editorColor04: rgba(205, 206, 207, 0.04); + --editorBgColor: #192330; + --deleteColor: #c94f6d; + --iconColor: rgba(205, 206, 207, 0.56); + --codeBgColor: #212e3f; + --codeBlockBgColor: #212e3f; + --footnoteBgColor: rgba(33, 46, 63, 0.3); + --inputBgColor: #131a24; + + --focusColor: var(--themeColor); + + --buttonFontColor: rgba(205, 206, 207, 0.6); + --buttonBgColor: #212e3f; + --buttonBorder: 1px solid rgba(0, 0, 0, 0.2); + --buttonShadow: none; + --buttonFontColorHover: var(--buttonFontColor); + --buttonBgColorHover: #29394d; + --buttonBorderHover: 1px solid rgba(0, 0, 0, 0.3); + --buttonFontColorActive: var(--buttonFontColor); + --buttonBgColorActive: #1a2736; + --buttonBorderActive: var(--buttonBorder); + + --buttonPrimaryFontColor: #192330; + --buttonPrimaryBgColor: var(--themeColor); + --buttonPrimaryBorder: none; + --buttonPrimaryShadow: none; + --buttonPrimaryFontColorHover: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorHover: #9d79d6; + --buttonPrimaryBorderHover: var(--buttonPrimaryBorder); + --buttonPrimaryFontColorActive: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorActive: #5580b8; + --buttonPrimaryBorderActive: var(--buttonPrimaryBorder); + --buttonPrimaryFocusBorder: none; + --buttonPrimaryFocusShadow: inset 0 0 0 1px rgba(24, 26, 31, 0.5), 0 0 0 1px var(--themeColor); + --tableBorderColor: #212e3f; + + /* Markdown element colors */ + --headingColor: #cdcecf; + --h1Color: #c94f6d; + --h2Color: #dbc074; + --h3Color: #81b29a; + --h4Color: #63cdcf; + --h5Color: #9d79d6; + --h6Color: #719cd6; + --blockquoteTextColor: rgba(205, 206, 207, 0.6); + --blockquoteBorderColor: #39506d; + --hrColor: #29394d; + --linkColor: #719cd6; + --strongColor: #cdcecf; + --emColor: #dbc074; + --listMarkerColor: #c94f6d; + + /*marktext*/ + --sideBarColor: rgba(205, 206, 207, 0.6); + --sideBarIconColor: var(--iconColor); + --sideBarTitleColor: rgba(205, 206, 207, 0.8); + --sideBarTextColor: rgba(205, 206, 207, 0.4); + --sideBarBgColor: #161e2a; + --sideBarItemHoverBgColor: rgba(205, 206, 207, 0.03); + --itemBgColor: #212e3f; + + --floatFontColor: rgba(205, 206, 207, 0.7); + --floatBgColor: #212e3f; + --floatHoverColor: rgba(205, 206, 207, 0.04); + --floatBorderColor: rgba(0, 0, 0, 0.05); + --floatShadow: rgba(0, 0, 0, 0.2); + --maskColor: rgba(0, 0, 0, 0.7); + --editorAreaWidth: 750px; + + /* @muyajs/core (kebab-case) editor variables + Additive prep for the muyajs -> @muyajs/core editor swap. These + mirror the camelCase vars above; the legacy editor ignores them. + Do NOT remove the camelCase vars — they still drive the current + editor until editor.vue switches engines. */ + --theme-color: var(--themeColor); + --highlight-color: var(--highlightColor); + --selection-color: var(--selectionColor); + --editor-color: var(--editorColor); + --editor-color-80: var(--editorColor80); + --editor-color-50: var(--editorColor50); + --editor-color-30: var(--editorColor30); + --editor-color-10: var(--editorColor10); + --editor-color-04: var(--editorColor04); + --editor-bg-color: var(--editorBgColor); + --editor-area-width: var(--editorAreaWidth); + --delete-color: var(--deleteColor); + --icon-color: var(--iconColor); + --code-block-bg-color: var(--codeBlockBgColor); + --table-border-color: var(--tableBorderColor); + --input-bg-color: var(--inputBgColor); + --button-font-color: var(--buttonFontColor); + --button-bg-color: var(--buttonBgColor); + --button-border: var(--buttonBorder); + --button-bg-color-hover: var(--buttonBgColorHover); + --button-border-hover: var(--buttonBorderHover); + --button-bg-color-active: var(--buttonBgColorActive); + --button-border-active: var(--buttonBorderActive); + --button-border-focus: var(--button-border); + --float-bg-color: var(--floatBgColor); + --float-hover-color: var(--floatHoverColor); + --float-border-color: var(--floatBorderColor); + --float-shadow: rgb(15 15 15 / 3%) 0 0 0 1px, rgb(15 15 15 / 4%) 0 3px 6px, rgb(15 15 15 / 5%) 0 9px 24px; + --link-color: var(--linkColor); + --h1-color: var(--h1Color); + --h2-color: var(--h2Color); + --h3-color: var(--h3Color); + --h4-color: var(--h4Color); + --h5-color: var(--h5Color); + --h6-color: var(--h6Color); + --blockquote-text-color: var(--blockquoteTextColor); + --blockquote-border-color: var(--blockquoteBorderColor); + --strong-color: var(--strongColor); + --em-color: var(--emColor); + --list-marker-color: var(--listMarkerColor); + --hr-color: var(--hrColor); +} + +::-webkit-scrollbar { + background: var(--editorBgColor); +} + +.mu-front-menu, +.mu-float-wrapper { + box-shadow: 0 4px 8px 0 var(--floatShadow) !important; +} + +.title-bar .frameless-titlebar-button > div > svg { + fill: #cdcecf; +} +.title-bar .frameless-titlebar-minimize:hover, +.title-bar .frameless-titlebar-toggle:hover { + background-color: rgba(205, 206, 207, 0.05); +} + +.side-bar { + border-right: 1px solid #0e141b !important; +} + +.recent-files-projects a, +.open-project a { + box-shadow: none !important; +} + +.editor-tabs { + box-shadow: none !important; +} +.editor-tabs:after { + position: absolute; + content: ''; + border-bottom: 1px solid #0e141b; + bottom: 0; + left: 0; + right: 0; + z-index: 1; +} +.editor-tabs ul.tabs-container:after { + position: absolute; + content: ''; + border-bottom: 1px solid #0e141b; + bottom: 0; + left: 0; + right: 0; + z-index: 2; +} + +.tabs-container > li, +.tabs-container > li.active { + background: var(--editorBgColor) !important; +} + +.open-project button, +.recent-files-projects button { + box-shadow: none !important; +} + +/* ------------------------------------ */ + +:not(pre) > code[class*='language-'], +pre:not(.CodeMirror-line), +pre[class*='language-'], +pre.mu-code-block { + background: var(--codeBlockBgColor) !important; + border: none !important; +} +.mu-thematic-break:not(.mu-active)::before { + border-top: 2px dashed var(--editorColor10) !important; + background: none !important; +} +figure.mu-active.mu-math-block > div.mu-math-preview, +figure.mu-active.mu-diagram-block > div.mu-diagram-preview { + box-shadow: 0 3px 8px 0 var(--floatShadow) !important; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/nord.theme.css b/packages/desktop/src/renderer/src/assets/themes/nord.theme.css new file mode 100644 index 0000000000..c62c0a4ec0 --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/nord.theme.css @@ -0,0 +1,214 @@ +:root { + /*editor - Nord theme*/ + --themeColor: #81a1c1; + --themeColor90: rgba(129, 161, 193, 0.9); + --themeColor80: rgba(129, 161, 193, 0.8); + --themeColor70: rgba(129, 161, 193, 0.7); + --themeColor60: rgba(129, 161, 193, 0.6); + --themeColor50: rgba(129, 161, 193, 0.5); + --themeColor40: rgba(129, 161, 193, 0.4); + --themeColor30: rgba(129, 161, 193, 0.3); + --themeColor20: rgba(129, 161, 193, 0.2); + --themeColor10: rgba(129, 161, 193, 0.1); + + --highlightColor: rgba(129, 161, 193, 0.6); + --selectionColor: rgba(76, 86, 106, 0.6); + --editorColor: #d8dee9; + --editorColor80: rgba(216, 222, 233, 0.8); + --editorColor60: rgba(216, 222, 233, 0.6); + --editorColor50: rgba(216, 222, 233, 0.5); + --editorColor40: rgba(216, 222, 233, 0.4); + --editorColor30: rgba(216, 222, 233, 0.3); + --editorColor10: rgba(216, 222, 233, 0.1); + --editorColor04: rgba(216, 222, 233, 0.04); + --editorBgColor: #2e3440; + --deleteColor: #bf616a; + --iconColor: rgba(216, 222, 233, 0.56); + --codeBgColor: #3b4252; + --codeBlockBgColor: #3b4252; + --footnoteBgColor: rgba(59, 66, 82, 0.3); + --inputBgColor: #292e39; + + --focusColor: var(--themeColor); + + --buttonFontColor: rgba(216, 222, 233, 0.6); + --buttonBgColor: #3b4252; + --buttonBorder: 1px solid rgba(0, 0, 0, 0.2); + --buttonShadow: none; + --buttonFontColorHover: var(--buttonFontColor); + --buttonBgColorHover: #434c5e; + --buttonBorderHover: 1px solid rgba(0, 0, 0, 0.3); + --buttonFontColorActive: var(--buttonFontColor); + --buttonBgColorActive: #353b47; + --buttonBorderActive: var(--buttonBorder); + + --buttonPrimaryFontColor: #2e3440; + --buttonPrimaryBgColor: var(--themeColor); + --buttonPrimaryBorder: none; + --buttonPrimaryShadow: none; + --buttonPrimaryFontColorHover: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorHover: #8fbcbb; + --buttonPrimaryBorderHover: var(--buttonPrimaryBorder); + --buttonPrimaryFontColorActive: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorActive: #5e81ac; + --buttonPrimaryBorderActive: var(--buttonPrimaryBorder); + --buttonPrimaryFocusBorder: none; + --buttonPrimaryFocusShadow: inset 0 0 0 1px rgba(24, 26, 31, 0.5), 0 0 0 1px var(--themeColor); + --tableBorderColor: #3b4252; + + /* Markdown element colors */ + --headingColor: #eceff4; + --h1Color: #88c0d0; + --h2Color: #81a1c1; + --h3Color: #5e81ac; + --h4Color: #b48ead; + --h5Color: #a3be8c; + --h6Color: #ebcb8b; + --blockquoteTextColor: rgba(216, 222, 233, 0.6); + --blockquoteBorderColor: #5e81ac; + --hrColor: #4c566a; + --linkColor: #88c0d0; + --strongColor: #eceff4; + --emColor: #ebcb8b; + --listMarkerColor: #81a1c1; + + /*marktext*/ + --sideBarColor: rgba(216, 222, 233, 0.6); + --sideBarIconColor: var(--iconColor); + --sideBarTitleColor: rgba(216, 222, 233, 0.8); + --sideBarTextColor: rgba(216, 222, 233, 0.4); + --sideBarBgColor: #2b313c; + --sideBarItemHoverBgColor: rgba(216, 222, 233, 0.03); + --itemBgColor: #3b4252; + + --floatFontColor: rgba(216, 222, 233, 0.7); + --floatBgColor: #3b4252; + --floatHoverColor: rgba(216, 222, 233, 0.04); + --floatBorderColor: rgba(0, 0, 0, 0.05); + --floatShadow: rgba(0, 0, 0, 0.2); + --maskColor: rgba(0, 0, 0, 0.7); + --editorAreaWidth: 750px; + + /* @muyajs/core (kebab-case) editor variables + Additive prep for the muyajs -> @muyajs/core editor swap. These + mirror the camelCase vars above; the legacy editor ignores them. + Do NOT remove the camelCase vars — they still drive the current + editor until editor.vue switches engines. */ + --theme-color: var(--themeColor); + --highlight-color: var(--highlightColor); + --selection-color: var(--selectionColor); + --editor-color: var(--editorColor); + --editor-color-80: var(--editorColor80); + --editor-color-50: var(--editorColor50); + --editor-color-30: var(--editorColor30); + --editor-color-10: var(--editorColor10); + --editor-color-04: var(--editorColor04); + --editor-bg-color: var(--editorBgColor); + --editor-area-width: var(--editorAreaWidth); + --delete-color: var(--deleteColor); + --icon-color: var(--iconColor); + --code-block-bg-color: var(--codeBlockBgColor); + --table-border-color: var(--tableBorderColor); + --input-bg-color: var(--inputBgColor); + --button-font-color: var(--buttonFontColor); + --button-bg-color: var(--buttonBgColor); + --button-border: var(--buttonBorder); + --button-bg-color-hover: var(--buttonBgColorHover); + --button-border-hover: var(--buttonBorderHover); + --button-bg-color-active: var(--buttonBgColorActive); + --button-border-active: var(--buttonBorderActive); + --button-border-focus: var(--button-border); + --float-bg-color: var(--floatBgColor); + --float-hover-color: var(--floatHoverColor); + --float-border-color: var(--floatBorderColor); + --float-shadow: rgb(15 15 15 / 3%) 0 0 0 1px, rgb(15 15 15 / 4%) 0 3px 6px, rgb(15 15 15 / 5%) 0 9px 24px; + --link-color: var(--linkColor); + --h1-color: var(--h1Color); + --h2-color: var(--h2Color); + --h3-color: var(--h3Color); + --h4-color: var(--h4Color); + --h5-color: var(--h5Color); + --h6-color: var(--h6Color); + --blockquote-text-color: var(--blockquoteTextColor); + --blockquote-border-color: var(--blockquoteBorderColor); + --strong-color: var(--strongColor); + --em-color: var(--emColor); + --list-marker-color: var(--listMarkerColor); + --hr-color: var(--hrColor); +} + +::-webkit-scrollbar { + background: var(--editorBgColor); +} + +.mu-front-menu, +.mu-float-wrapper { + box-shadow: 0 4px 8px 0 var(--floatShadow) !important; +} + +.title-bar .frameless-titlebar-button > div > svg { + fill: #d8dee9; +} +.title-bar .frameless-titlebar-minimize:hover, +.title-bar .frameless-titlebar-toggle:hover { + background-color: rgba(216, 222, 233, 0.05); +} + +.side-bar { + border-right: 1px solid #252a34 !important; +} + +.recent-files-projects a, +.open-project a { + box-shadow: none !important; +} + +.editor-tabs { + box-shadow: none !important; +} +.editor-tabs:after { + position: absolute; + content: ''; + border-bottom: 1px solid #252a34; + bottom: 0; + left: 0; + right: 0; + z-index: 1; +} +.editor-tabs ul.tabs-container:after { + position: absolute; + content: ''; + border-bottom: 1px solid #252a34; + bottom: 0; + left: 0; + right: 0; + z-index: 2; +} + +.tabs-container > li, +.tabs-container > li.active { + background: var(--editorBgColor) !important; +} + +.open-project button, +.recent-files-projects button { + box-shadow: none !important; +} + +/* ------------------------------------ */ + +:not(pre) > code[class*='language-'], +pre:not(.CodeMirror-line), +pre[class*='language-'], +pre.mu-code-block { + background: var(--codeBlockBgColor) !important; + border: none !important; +} +.mu-thematic-break:not(.mu-active)::before { + border-top: 2px dashed var(--editorColor10) !important; + background: none !important; +} +figure.mu-active.mu-math-block > div.mu-math-preview, +figure.mu-active.mu-diagram-block > div.mu-diagram-preview { + box-shadow: 0 3px 8px 0 var(--floatShadow) !important; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/one-dark.theme.css b/packages/desktop/src/renderer/src/assets/themes/one-dark.theme.css new file mode 100644 index 0000000000..959fe64740 --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/one-dark.theme.css @@ -0,0 +1,350 @@ +:root { + /*editor*/ + --themeColor: rgba(77, 120, 204, 1); + --themeColor90: rgba(77, 120, 204, 0.9); + --themeColor80: rgba(77, 120, 204, 0.8); + --themeColor70: rgba(77, 120, 204, 0.7); + --themeColor60: rgba(77, 120, 204, 0.6); + --themeColor50: rgba(77, 120, 204, 0.5); + --themeColor40: rgba(77, 120, 204, 0.4); + --themeColor30: rgba(77, 120, 204, 0.3); + --themeColor20: rgba(77, 120, 204, 0.2); + --themeColor10: rgba(77, 120, 204, 0.1); + --highlightThemeColor: #e2c08d; + + --highlightColor: #ffffff10; + --selectionColor: #67769660; + --editorColor: #9da5b4; + --editorColor80: #9da5b4; + --editorColor60: rgba(157, 165, 180, 0.6); + --editorColor50: rgba(157, 165, 180, 0.5); + --editorColor40: rgba(157, 165, 180, 0.4); + --editorColor30: rgba(157, 165, 180, 0.3); + --editorColor10: rgba(157, 165, 180, 0.1); + --editorColor04: rgba(157, 165, 180, 0.04); + --editorBgColor: #282c34; + --deleteColor: #ff6969; + --iconColor: rgba(255, 255, 255, 0.56); + --codeBgColor: #3a3f4b; + --codeBlockBgColor: #3a3f4b; + --footnoteBgColor: rgba(66, 67, 68, 0.5); + --inputBgColor: rgba(0, 0, 0, 0.1); + --focusColor: #568af2; + + --buttonFontColor: #9da5b4; + --buttonBgColor: linear-gradient(#3a3f4b, #353b45); + --buttonBorder: 1px solid #181a1f; + --buttonShadow: none; + --buttonFontColorHover: #d7dae0; + --buttonBgColorHover: linear-gradient(#3e4451, #3a3f4b); + --buttonBorderHover: var(--buttonBorder); + --buttonFontColorActive: var(--buttonFontColor); + --buttonBgColorActive: #2c313a; + --buttonBorderActive: var(--buttonBorder); + --buttonFocusBorder: 1px solid #568af2; + + --buttonPrimaryFontColor: #ffffff; + --buttonPrimaryBgColor: linear-gradient(#557ecf, #4d78cc); + --buttonPrimaryBorder: 1px solid #181a1f; + --buttonPrimaryShadow: none; + --buttonPrimaryFontColorHover: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorHover: linear-gradient(#6187d2, #5981d0); + --buttonPrimaryBorderHover: var(--buttonPrimaryBorder); + --buttonPrimaryFontColorActive: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorActive: #3d6bc8; + --buttonPrimaryBorderActive: var(--buttonPrimaryBorder); + --buttonPrimaryFocusBorder: none; + --buttonPrimaryFocusShadow: inset 0 0 0 1px rgba(24, 26, 31, 0.5), 0 0 0 1px #568af2; + --tableBorderColor: #363839; + + /* Markdown element colors */ + --headingColor: #e2c08d; + --h1Color: #e06c75; + --h2Color: #61afef; + --h3Color: #98c379; + --h4Color: #56b6c2; + --h5Color: #c678dd; + --h6Color: #e5c07b; + --blockquoteTextColor: rgba(157, 165, 180, 0.6); + --blockquoteBorderColor: #e2c08d; + --hrColor: #4b5362; + --linkColor: #61afef; + --strongColor: #e2c08d; + --emColor: #c678dd; + --listMarkerColor: #e06c75; + + /*marktext*/ + --sideBarColor: #9da5b4; + --sideBarIconColor: var(--iconColor); + --sideBarTitleColor: #9da5b4; + --sideBarTextColor: #9da5b4; + --sideBarBgColor: #24282f; + --sideBarItemHoverBgColor: #3a3f4b; + --itemBgColor: #21252b; + + --floatFontColor: #9da5b4; + --floatBgColor: #21252b; + --floatHoverColor: #3a3f4b; + --floatBorderColor: #181a1f; + --floatShadow: rgba(0, 0, 0, 0.3); + --maskColor: rgba(0, 0, 0, 0.7); + --editorAreaWidth: 750px; + + /* @muyajs/core (kebab-case) editor variables + Additive prep for the muyajs -> @muyajs/core editor swap. These + mirror the camelCase vars above; the legacy editor ignores them. + Do NOT remove the camelCase vars — they still drive the current + editor until editor.vue switches engines. */ + --theme-color: var(--themeColor); + --highlight-color: var(--highlightColor); + --selection-color: var(--selectionColor); + --editor-color: var(--editorColor); + --editor-color-80: var(--editorColor80); + --editor-color-50: var(--editorColor50); + --editor-color-30: var(--editorColor30); + --editor-color-10: var(--editorColor10); + --editor-color-04: var(--editorColor04); + --editor-bg-color: var(--editorBgColor); + --editor-area-width: var(--editorAreaWidth); + --delete-color: var(--deleteColor); + --icon-color: var(--iconColor); + --code-block-bg-color: var(--codeBlockBgColor); + --table-border-color: var(--tableBorderColor); + --input-bg-color: var(--inputBgColor); + --button-font-color: var(--buttonFontColor); + --button-bg-color: var(--buttonBgColor); + --button-border: var(--buttonBorder); + --button-bg-color-hover: var(--buttonBgColorHover); + --button-border-hover: var(--buttonBorderHover); + --button-bg-color-active: var(--buttonBgColorActive); + --button-border-active: var(--buttonBorderActive); + --button-border-focus: var(--button-border); + --float-bg-color: var(--floatBgColor); + --float-hover-color: var(--floatHoverColor); + --float-border-color: var(--floatBorderColor); + --float-shadow: rgb(15 15 15 / 3%) 0 0 0 1px, rgb(15 15 15 / 4%) 0 3px 6px, rgb(15 15 15 / 5%) 0 9px 24px; + --link-color: var(--linkColor); + --h1-color: var(--h1Color); + --h2-color: var(--h2Color); + --h3-color: var(--h3Color); + --h4-color: var(--h4Color); + --h5-color: var(--h5Color); + --h6-color: var(--h6Color); + --blockquote-text-color: var(--blockquoteTextColor); + --blockquote-border-color: var(--blockquoteBorderColor); + --strong-color: var(--strongColor); + --em-color: var(--emColor); + --list-marker-color: var(--listMarkerColor); + --hr-color: var(--hrColor); +} + +::-webkit-scrollbar, +::-webkit-scrollbar-corner { + background: var(--editorBgColor); +} +::-webkit-scrollbar:vertical { + width: 10px; +} +::-webkit-scrollbar:vertical:hover { + width: 12px; +} +::-webkit-scrollbar-thumb { + background: #414956; +} +::-webkit-scrollbar-thumb:hover { + background: #4b5362; +} + +/* --- Element UI ------------------------------------------------ */ + +.v-modal { + background: #08090a !important; + opacity: 0.65 !important; +} +.el-dialog, +.el-dialog.ag-dialog-table { + border-radius: 6px !important; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3) !important; + border: 1px solid #181a1f !important; + background: #252830 !important; +} + +.el-button:focus { + color: var(--buttonFontColor); +} +.el-button--primary:focus { + color: var(--buttonPrimaryFontColor); + background: var(--buttonPrimaryBgColor); +} + +/* --- Core elements --------------------------------------------- */ + +.mu-front-menu, +.mu-float-wrapper { + box-shadow: 0 4px 8px 0 var(--floatShadow) !important; +} + +.title-bar .frameless-titlebar-button > div > svg { + fill: #ffffff; +} +.title-bar .frameless-titlebar-minimize:hover, +.title-bar .frameless-titlebar-toggle:hover { + background-color: rgba(215, 218, 224, 0.05) !important; +} + +.input-wrapper { + border-radius: 3px !important; + border: 1px solid #181a1f !important; + background: #1b1d23 !important; +} + +/* --- UI elements ----------------------------------------------- */ + +.side-bar { + border-right: 1px solid #181a1f !important; +} +.left-column ul > li > svg { + fill: rgba(215, 218, 224, 0.6) !important; +} +.left-column ul > li:hover > svg { + fill: #d7dae0 !important; +} +.left-column ul > li:hover > svg { + fill: #d7dae0 !important; +} +.left-column ul > li.active > svg { + fill: #4d78cc !important; +} + +.open-project button.button-primary, +.recent-files-projects button.button-primary { + box-shadow: none; +} + +.editor-tabs { + box-shadow: none !important; +} +.editor-tabs:after { + position: absolute; + content: ''; + border-bottom: 1px solid #181a1f; + bottom: 0; + left: 0; + right: 0; + z-index: 1; +} +.editor-tabs ul.tabs-container:after { + position: absolute; + content: ''; + border-bottom: 1px solid #181a1f; + bottom: 0; + left: 0; + right: 0; + z-index: 2; +} + +.tabs-container > li, +.tabs-container > li.active { + background: var(--editorBgColor) !important; +} +.tabs-container > li.active { + border: 1px solid #181a1f; + border-bottom: none; +} +.tabs-container > li.active:after { + top: 0 !important; + right: auto !important; + width: 2px !important; + height: auto !important; + background: #4d78cc !important; +} +.tabs-container svg.close-icon #unsaved-circle-icon { + fill: #4d78cc; +} + +/* --- Editor ---------------------------------------------------- */ + +blockquote::before { + background: var(--highlightThemeColor); +} + +:not(pre) > code[class*='language-'], +pre:not(.CodeMirror-line), +pre[class*='language-'], +pre.mu-code-block { + background: var(--codeBlockBgColor) !important; + border: none !important; +} + +.mu-thematic-break:not(.mu-active)::before { + border-top: 2px dashed #4b5362 !important; + background: none !important; +} + +li.mu-task-list-item { + list-style-type: none; + position: relative; +} +li.mu-task-list-item > input[type='checkbox'] { + position: absolute; + cursor: pointer; + width: 16px; + height: 16px; + top: 0.1em; + transform: rotate(-90deg); + margin: 0; + left: -24px; + transform-origin: center; + transition: all 0.2s ease; +} +li.mu-task-list-item > input.mu-checkbox-checked { + transform: rotate(0); + opacity: 0.6; +} +li.mu-task-list-item > input[type='checkbox']::before { + content: ''; + width: 16px; + height: 16px; + box-sizing: border-box; + display: inline-block; + border: 2px solid var(--editorColor); + border-radius: 2px; + background-color: var(--editorBgColor); + position: absolute; + top: 0; + left: 0; + transition: all 0.2s ease; +} +li.mu-task-list-item > input.mu-checkbox-checked::before { + border: transparent; + background-color: var(--editorColor); +} +li.mu-task-list-item > input::after { + content: ''; + transform: rotate(-45deg) scale(0); + width: 9px; + height: 5px; + border: 2px solid #fff; + border-top: none; + border-right: none; + position: absolute; + display: inline-block; + top: 1px; + left: 5px; + transition: all 0.2s ease; +} +li.mu-task-list-item > input.mu-checkbox-checked::after { + transform: rotate(-45deg) scale(1); +} +figure.mu-active.mu-math-block > div.mu-math-preview, +figure.mu-active.mu-diagram-block > div.mu-diagram-preview { + box-shadow: 0 3px 8px 0 var(--floatShadow) !important; +} + +/* ------------------------------------ */ + +/* + * Prism.js theme (override light theme) + */ + +/* @import url("prismjs/one-dark.theme.css"); */ diff --git a/packages/desktop/src/renderer/src/assets/themes/oxocarbon-dark.theme.css b/packages/desktop/src/renderer/src/assets/themes/oxocarbon-dark.theme.css new file mode 100644 index 0000000000..b960143526 --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/oxocarbon-dark.theme.css @@ -0,0 +1,214 @@ +:root { + /*editor - Oxocarbon Dark theme*/ + --themeColor: #78a9ff; + --themeColor90: rgba(120, 169, 255, 0.9); + --themeColor80: rgba(120, 169, 255, 0.8); + --themeColor70: rgba(120, 169, 255, 0.7); + --themeColor60: rgba(120, 169, 255, 0.6); + --themeColor50: rgba(120, 169, 255, 0.5); + --themeColor40: rgba(120, 169, 255, 0.4); + --themeColor30: rgba(120, 169, 255, 0.3); + --themeColor20: rgba(120, 169, 255, 0.2); + --themeColor10: rgba(120, 169, 255, 0.1); + + --highlightColor: rgba(120, 169, 255, 0.6); + --selectionColor: rgba(57, 57, 57, 0.8); + --editorColor: #f2f4f8; + --editorColor80: rgba(242, 244, 248, 0.8); + --editorColor60: rgba(242, 244, 248, 0.6); + --editorColor50: rgba(242, 244, 248, 0.5); + --editorColor40: rgba(242, 244, 248, 0.4); + --editorColor30: rgba(242, 244, 248, 0.3); + --editorColor10: rgba(242, 244, 248, 0.1); + --editorColor04: rgba(242, 244, 248, 0.04); + --editorBgColor: #161616; + --deleteColor: #ee5396; + --iconColor: rgba(242, 244, 248, 0.56); + --codeBgColor: #262626; + --codeBlockBgColor: #262626; + --footnoteBgColor: rgba(38, 38, 38, 0.3); + --inputBgColor: #0d0d0d; + + --focusColor: var(--themeColor); + + --buttonFontColor: rgba(242, 244, 248, 0.6); + --buttonBgColor: #262626; + --buttonBorder: 1px solid rgba(0, 0, 0, 0.2); + --buttonShadow: none; + --buttonFontColorHover: var(--buttonFontColor); + --buttonBgColorHover: #393939; + --buttonBorderHover: 1px solid rgba(0, 0, 0, 0.3); + --buttonFontColorActive: var(--buttonFontColor); + --buttonBgColorActive: #1e1e1e; + --buttonBorderActive: var(--buttonBorder); + + --buttonPrimaryFontColor: #161616; + --buttonPrimaryBgColor: var(--themeColor); + --buttonPrimaryBorder: none; + --buttonPrimaryShadow: none; + --buttonPrimaryFontColorHover: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorHover: #33b1ff; + --buttonPrimaryBorderHover: var(--buttonPrimaryBorder); + --buttonPrimaryFontColorActive: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorActive: #5890e6; + --buttonPrimaryBorderActive: var(--buttonPrimaryBorder); + --buttonPrimaryFocusBorder: none; + --buttonPrimaryFocusShadow: inset 0 0 0 1px rgba(24, 26, 31, 0.5), 0 0 0 1px var(--themeColor); + --tableBorderColor: #262626; + + /* Markdown element colors */ + --headingColor: #f2f4f8; + --h1Color: #ee5396; + --h2Color: #78a9ff; + --h3Color: #42be65; + --h4Color: #33b1ff; + --h5Color: #be95ff; + --h6Color: #ff7eb6; + --blockquoteTextColor: rgba(242, 244, 248, 0.6); + --blockquoteBorderColor: #525252; + --hrColor: #393939; + --linkColor: #78a9ff; + --strongColor: #f2f4f8; + --emColor: #be95ff; + --listMarkerColor: #33b1ff; + + /*marktext*/ + --sideBarColor: rgba(242, 244, 248, 0.6); + --sideBarIconColor: var(--iconColor); + --sideBarTitleColor: rgba(242, 244, 248, 0.8); + --sideBarTextColor: rgba(242, 244, 248, 0.4); + --sideBarBgColor: #111111; + --sideBarItemHoverBgColor: rgba(242, 244, 248, 0.03); + --itemBgColor: #262626; + + --floatFontColor: rgba(242, 244, 248, 0.7); + --floatBgColor: #262626; + --floatHoverColor: rgba(242, 244, 248, 0.04); + --floatBorderColor: rgba(0, 0, 0, 0.05); + --floatShadow: rgba(0, 0, 0, 0.2); + --maskColor: rgba(0, 0, 0, 0.7); + --editorAreaWidth: 750px; + + /* @muyajs/core (kebab-case) editor variables + Additive prep for the muyajs -> @muyajs/core editor swap. These + mirror the camelCase vars above; the legacy editor ignores them. + Do NOT remove the camelCase vars — they still drive the current + editor until editor.vue switches engines. */ + --theme-color: var(--themeColor); + --highlight-color: var(--highlightColor); + --selection-color: var(--selectionColor); + --editor-color: var(--editorColor); + --editor-color-80: var(--editorColor80); + --editor-color-50: var(--editorColor50); + --editor-color-30: var(--editorColor30); + --editor-color-10: var(--editorColor10); + --editor-color-04: var(--editorColor04); + --editor-bg-color: var(--editorBgColor); + --editor-area-width: var(--editorAreaWidth); + --delete-color: var(--deleteColor); + --icon-color: var(--iconColor); + --code-block-bg-color: var(--codeBlockBgColor); + --table-border-color: var(--tableBorderColor); + --input-bg-color: var(--inputBgColor); + --button-font-color: var(--buttonFontColor); + --button-bg-color: var(--buttonBgColor); + --button-border: var(--buttonBorder); + --button-bg-color-hover: var(--buttonBgColorHover); + --button-border-hover: var(--buttonBorderHover); + --button-bg-color-active: var(--buttonBgColorActive); + --button-border-active: var(--buttonBorderActive); + --button-border-focus: var(--button-border); + --float-bg-color: var(--floatBgColor); + --float-hover-color: var(--floatHoverColor); + --float-border-color: var(--floatBorderColor); + --float-shadow: rgb(15 15 15 / 3%) 0 0 0 1px, rgb(15 15 15 / 4%) 0 3px 6px, rgb(15 15 15 / 5%) 0 9px 24px; + --link-color: var(--linkColor); + --h1-color: var(--h1Color); + --h2-color: var(--h2Color); + --h3-color: var(--h3Color); + --h4-color: var(--h4Color); + --h5-color: var(--h5Color); + --h6-color: var(--h6Color); + --blockquote-text-color: var(--blockquoteTextColor); + --blockquote-border-color: var(--blockquoteBorderColor); + --strong-color: var(--strongColor); + --em-color: var(--emColor); + --list-marker-color: var(--listMarkerColor); + --hr-color: var(--hrColor); +} + +::-webkit-scrollbar { + background: var(--editorBgColor); +} + +.mu-front-menu, +.mu-float-wrapper { + box-shadow: 0 4px 8px 0 var(--floatShadow) !important; +} + +.title-bar .frameless-titlebar-button > div > svg { + fill: #f2f4f8; +} +.title-bar .frameless-titlebar-minimize:hover, +.title-bar .frameless-titlebar-toggle:hover { + background-color: rgba(242, 244, 248, 0.05); +} + +.side-bar { + border-right: 1px solid #080808 !important; +} + +.recent-files-projects a, +.open-project a { + box-shadow: none !important; +} + +.editor-tabs { + box-shadow: none !important; +} +.editor-tabs:after { + position: absolute; + content: ''; + border-bottom: 1px solid #080808; + bottom: 0; + left: 0; + right: 0; + z-index: 1; +} +.editor-tabs ul.tabs-container:after { + position: absolute; + content: ''; + border-bottom: 1px solid #080808; + bottom: 0; + left: 0; + right: 0; + z-index: 2; +} + +.tabs-container > li, +.tabs-container > li.active { + background: var(--editorBgColor) !important; +} + +.open-project button, +.recent-files-projects button { + box-shadow: none !important; +} + +/* ------------------------------------ */ + +:not(pre) > code[class*='language-'], +pre:not(.CodeMirror-line), +pre[class*='language-'], +pre.mu-code-block { + background: var(--codeBlockBgColor) !important; + border: none !important; +} +.mu-thematic-break:not(.mu-active)::before { + border-top: 2px dashed var(--editorColor10) !important; + background: none !important; +} +figure.mu-active.mu-math-block > div.mu-math-preview, +figure.mu-active.mu-diagram-block > div.mu-diagram-preview { + box-shadow: 0 3px 8px 0 var(--floatShadow) !important; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/palenight.theme.css b/packages/desktop/src/renderer/src/assets/themes/palenight.theme.css new file mode 100644 index 0000000000..62c59d207e --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/palenight.theme.css @@ -0,0 +1,214 @@ +:root { + /*editor - Palenight theme*/ + --themeColor: #82aaff; + --themeColor90: rgba(130, 170, 255, 0.9); + --themeColor80: rgba(130, 170, 255, 0.8); + --themeColor70: rgba(130, 170, 255, 0.7); + --themeColor60: rgba(130, 170, 255, 0.6); + --themeColor50: rgba(130, 170, 255, 0.5); + --themeColor40: rgba(130, 170, 255, 0.4); + --themeColor30: rgba(130, 170, 255, 0.3); + --themeColor20: rgba(130, 170, 255, 0.2); + --themeColor10: rgba(130, 170, 255, 0.1); + + --highlightColor: rgba(130, 170, 255, 0.6); + --selectionColor: rgba(113, 124, 180, 0.4); + --editorColor: #a6accd; + --editorColor80: rgba(166, 172, 205, 0.8); + --editorColor60: rgba(166, 172, 205, 0.6); + --editorColor50: rgba(166, 172, 205, 0.5); + --editorColor40: rgba(166, 172, 205, 0.4); + --editorColor30: rgba(166, 172, 205, 0.3); + --editorColor10: rgba(166, 172, 205, 0.1); + --editorColor04: rgba(166, 172, 205, 0.04); + --editorBgColor: #292d3e; + --deleteColor: #ff5370; + --iconColor: rgba(166, 172, 205, 0.56); + --codeBgColor: #32374d; + --codeBlockBgColor: #32374d; + --footnoteBgColor: rgba(50, 55, 77, 0.3); + --inputBgColor: #222534; + + --focusColor: var(--themeColor); + + --buttonFontColor: rgba(166, 172, 205, 0.6); + --buttonBgColor: #32374d; + --buttonBorder: 1px solid rgba(0, 0, 0, 0.2); + --buttonShadow: none; + --buttonFontColorHover: var(--buttonFontColor); + --buttonBgColorHover: #3e4460; + --buttonBorderHover: 1px solid rgba(0, 0, 0, 0.3); + --buttonFontColorActive: var(--buttonFontColor); + --buttonBgColorActive: #2a2f41; + --buttonBorderActive: var(--buttonBorder); + + --buttonPrimaryFontColor: #292d3e; + --buttonPrimaryBgColor: var(--themeColor); + --buttonPrimaryBorder: none; + --buttonPrimaryShadow: none; + --buttonPrimaryFontColorHover: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorHover: #c792ea; + --buttonPrimaryBorderHover: var(--buttonPrimaryBorder); + --buttonPrimaryFontColorActive: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorActive: #6590e6; + --buttonPrimaryBorderActive: var(--buttonPrimaryBorder); + --buttonPrimaryFocusBorder: none; + --buttonPrimaryFocusShadow: inset 0 0 0 1px rgba(24, 26, 31, 0.5), 0 0 0 1px var(--themeColor); + --tableBorderColor: #32374d; + + /* Markdown element colors */ + --headingColor: #a6accd; + --h1Color: #c792ea; + --h2Color: #82aaff; + --h3Color: #c3e88d; + --h4Color: #89ddff; + --h5Color: #f78c6c; + --h6Color: #ff5370; + --blockquoteTextColor: rgba(166, 172, 205, 0.6); + --blockquoteBorderColor: #4e5579; + --hrColor: #3e4460; + --linkColor: #82aaff; + --strongColor: #a6accd; + --emColor: #c792ea; + --listMarkerColor: #f78c6c; + + /*marktext*/ + --sideBarColor: rgba(166, 172, 205, 0.6); + --sideBarIconColor: var(--iconColor); + --sideBarTitleColor: rgba(166, 172, 205, 0.8); + --sideBarTextColor: rgba(166, 172, 205, 0.4); + --sideBarBgColor: #252939; + --sideBarItemHoverBgColor: rgba(166, 172, 205, 0.03); + --itemBgColor: #32374d; + + --floatFontColor: rgba(166, 172, 205, 0.7); + --floatBgColor: #32374d; + --floatHoverColor: rgba(166, 172, 205, 0.04); + --floatBorderColor: rgba(0, 0, 0, 0.05); + --floatShadow: rgba(0, 0, 0, 0.2); + --maskColor: rgba(0, 0, 0, 0.7); + --editorAreaWidth: 750px; + + /* @muyajs/core (kebab-case) editor variables + Additive prep for the muyajs -> @muyajs/core editor swap. These + mirror the camelCase vars above; the legacy editor ignores them. + Do NOT remove the camelCase vars — they still drive the current + editor until editor.vue switches engines. */ + --theme-color: var(--themeColor); + --highlight-color: var(--highlightColor); + --selection-color: var(--selectionColor); + --editor-color: var(--editorColor); + --editor-color-80: var(--editorColor80); + --editor-color-50: var(--editorColor50); + --editor-color-30: var(--editorColor30); + --editor-color-10: var(--editorColor10); + --editor-color-04: var(--editorColor04); + --editor-bg-color: var(--editorBgColor); + --editor-area-width: var(--editorAreaWidth); + --delete-color: var(--deleteColor); + --icon-color: var(--iconColor); + --code-block-bg-color: var(--codeBlockBgColor); + --table-border-color: var(--tableBorderColor); + --input-bg-color: var(--inputBgColor); + --button-font-color: var(--buttonFontColor); + --button-bg-color: var(--buttonBgColor); + --button-border: var(--buttonBorder); + --button-bg-color-hover: var(--buttonBgColorHover); + --button-border-hover: var(--buttonBorderHover); + --button-bg-color-active: var(--buttonBgColorActive); + --button-border-active: var(--buttonBorderActive); + --button-border-focus: var(--button-border); + --float-bg-color: var(--floatBgColor); + --float-hover-color: var(--floatHoverColor); + --float-border-color: var(--floatBorderColor); + --float-shadow: rgb(15 15 15 / 3%) 0 0 0 1px, rgb(15 15 15 / 4%) 0 3px 6px, rgb(15 15 15 / 5%) 0 9px 24px; + --link-color: var(--linkColor); + --h1-color: var(--h1Color); + --h2-color: var(--h2Color); + --h3-color: var(--h3Color); + --h4-color: var(--h4Color); + --h5-color: var(--h5Color); + --h6-color: var(--h6Color); + --blockquote-text-color: var(--blockquoteTextColor); + --blockquote-border-color: var(--blockquoteBorderColor); + --strong-color: var(--strongColor); + --em-color: var(--emColor); + --list-marker-color: var(--listMarkerColor); + --hr-color: var(--hrColor); +} + +::-webkit-scrollbar { + background: var(--editorBgColor); +} + +.mu-front-menu, +.mu-float-wrapper { + box-shadow: 0 4px 8px 0 var(--floatShadow) !important; +} + +.title-bar .frameless-titlebar-button > div > svg { + fill: #a6accd; +} +.title-bar .frameless-titlebar-minimize:hover, +.title-bar .frameless-titlebar-toggle:hover { + background-color: rgba(166, 172, 205, 0.05); +} + +.side-bar { + border-right: 1px solid #1c1f2b !important; +} + +.recent-files-projects a, +.open-project a { + box-shadow: none !important; +} + +.editor-tabs { + box-shadow: none !important; +} +.editor-tabs:after { + position: absolute; + content: ''; + border-bottom: 1px solid #1c1f2b; + bottom: 0; + left: 0; + right: 0; + z-index: 1; +} +.editor-tabs ul.tabs-container:after { + position: absolute; + content: ''; + border-bottom: 1px solid #1c1f2b; + bottom: 0; + left: 0; + right: 0; + z-index: 2; +} + +.tabs-container > li, +.tabs-container > li.active { + background: var(--editorBgColor) !important; +} + +.open-project button, +.recent-files-projects button { + box-shadow: none !important; +} + +/* ------------------------------------ */ + +:not(pre) > code[class*='language-'], +pre:not(.CodeMirror-line), +pre[class*='language-'], +pre.mu-code-block { + background: var(--codeBlockBgColor) !important; + border: none !important; +} +.mu-thematic-break:not(.mu-active)::before { + border-top: 2px dashed var(--editorColor10) !important; + background: none !important; +} +figure.mu-active.mu-math-block > div.mu-math-preview, +figure.mu-active.mu-diagram-block > div.mu-diagram-preview { + box-shadow: 0 3px 8px 0 var(--floatShadow) !important; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/prismjs/ayu-dark.theme.css b/packages/desktop/src/renderer/src/assets/themes/prismjs/ayu-dark.theme.css new file mode 100644 index 0000000000..e57dd48503 --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/prismjs/ayu-dark.theme.css @@ -0,0 +1,114 @@ +/* + * Prism.js Ayu Dark Theme + * Based on https://github.com/ayu-theme/ayu-colors + */ + +code[class*='language-'], +pre.ag-paragraph { + color: #bfbdb6; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; + overflow: visible; +} + +pre.ag-paragraph { + padding: 1em; + margin: 1em 0; + border-radius: 0.3em; +} + +:not(pre) > code[class*='language-'], +pre.ag-paragraph { + background: #0a0e14; +} + +:not(pre) > code[class*='language-'] { + padding: 0.1em; + border-radius: 0.3em; + white-space: normal; +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: #626a73; +} + +.token.punctuation { + color: #bfbdb6; +} + +.namespace { + opacity: 0.7; +} + +.token.property, +.token.tag, +.token.constant, +.token.symbol, +.token.deleted { + color: #39bae6; +} + +.token.boolean, +.token.number { + color: #d2a6ff; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: #c2d94c; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + color: #f29668; +} + +.token.atrule, +.token.attr-value, +.token.function, +.token.class-name { + color: #ffb454; +} + +.token.keyword { + color: #ff8f40; +} + +.token.regex, +.token.important, +.token.variable { + color: #95e6cb; +} + +.token.important, +.token.bold { + font-weight: bold; +} + +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/prismjs/ayu-light.theme.css b/packages/desktop/src/renderer/src/assets/themes/prismjs/ayu-light.theme.css new file mode 100644 index 0000000000..5c7cc66ef3 --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/prismjs/ayu-light.theme.css @@ -0,0 +1,114 @@ +/* + * Prism.js Ayu Light Theme + * Based on https://github.com/ayu-theme/ayu-colors + */ + +code[class*='language-'], +pre.ag-paragraph { + color: #575f66; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; + overflow: visible; +} + +pre.ag-paragraph { + padding: 1em; + margin: 1em 0; + border-radius: 0.3em; +} + +:not(pre) > code[class*='language-'], +pre.ag-paragraph { + background: #f0f0f0; +} + +:not(pre) > code[class*='language-'] { + padding: 0.1em; + border-radius: 0.3em; + white-space: normal; +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: #abb0b6; +} + +.token.punctuation { + color: #575f66; +} + +.namespace { + opacity: 0.7; +} + +.token.property, +.token.tag, +.token.constant, +.token.symbol, +.token.deleted { + color: #f07171; +} + +.token.boolean, +.token.number { + color: #a37acc; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: #86b300; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + color: #4cbf99; +} + +.token.atrule, +.token.attr-value, +.token.function, +.token.class-name { + color: #f2ae49; +} + +.token.keyword { + color: #fa8d3e; +} + +.token.regex, +.token.important, +.token.variable { + color: #399ee6; +} + +.token.important, +.token.bold { + font-weight: bold; +} + +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/prismjs/ayu-mirage.theme.css b/packages/desktop/src/renderer/src/assets/themes/prismjs/ayu-mirage.theme.css new file mode 100644 index 0000000000..6ea959170c --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/prismjs/ayu-mirage.theme.css @@ -0,0 +1,114 @@ +/* + * Prism.js Ayu Mirage Theme + * Based on https://github.com/ayu-theme/ayu-colors + */ + +code[class*='language-'], +pre.ag-paragraph { + color: #cbccc6; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; + overflow: visible; +} + +pre.ag-paragraph { + padding: 1em; + margin: 1em 0; + border-radius: 0.3em; +} + +:not(pre) > code[class*='language-'], +pre.ag-paragraph { + background: #272d38; +} + +:not(pre) > code[class*='language-'] { + padding: 0.1em; + border-radius: 0.3em; + white-space: normal; +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: #5c6773; +} + +.token.punctuation { + color: #cbccc6; +} + +.namespace { + opacity: 0.7; +} + +.token.property, +.token.tag, +.token.constant, +.token.symbol, +.token.deleted { + color: #f28779; +} + +.token.boolean, +.token.number { + color: #ffad66; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: #bae67e; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + color: #95e6cb; +} + +.token.atrule, +.token.attr-value, +.token.function, +.token.class-name { + color: #ffd580; +} + +.token.keyword { + color: #ffcc66; +} + +.token.regex, +.token.important, +.token.variable { + color: #73d0ff; +} + +.token.important, +.token.bold { + font-weight: bold; +} + +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/prismjs/catppuccin-latte.theme.css b/packages/desktop/src/renderer/src/assets/themes/prismjs/catppuccin-latte.theme.css new file mode 100644 index 0000000000..6a978e5630 --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/prismjs/catppuccin-latte.theme.css @@ -0,0 +1,114 @@ +/* + * Prism.js Catppuccin Latte Theme (Light) + * Based on https://catppuccin.com + */ + +code[class*='language-'], +pre.ag-paragraph { + color: #4c4f69; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; + overflow: visible; +} + +pre.ag-paragraph { + padding: 1em; + margin: 1em 0; + border-radius: 0.3em; +} + +:not(pre) > code[class*='language-'], +pre.ag-paragraph { + background: #e6e9ef; +} + +:not(pre) > code[class*='language-'] { + padding: 0.1em; + border-radius: 0.3em; + white-space: normal; +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: #9ca0b0; +} + +.token.punctuation { + color: #5c5f77; +} + +.namespace { + opacity: 0.7; +} + +.token.property, +.token.tag, +.token.constant, +.token.symbol, +.token.deleted { + color: #d20f39; +} + +.token.boolean, +.token.number { + color: #fe640b; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: #40a02b; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + color: #179299; +} + +.token.atrule, +.token.attr-value, +.token.function, +.token.class-name { + color: #1e66f5; +} + +.token.keyword { + color: #8839ef; +} + +.token.regex, +.token.important, +.token.variable { + color: #df8e1d; +} + +.token.important, +.token.bold { + font-weight: bold; +} + +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/prismjs/catppuccin-mocha.theme.css b/packages/desktop/src/renderer/src/assets/themes/prismjs/catppuccin-mocha.theme.css new file mode 100644 index 0000000000..a3c46ea40e --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/prismjs/catppuccin-mocha.theme.css @@ -0,0 +1,114 @@ +/* + * Prism.js Catppuccin Mocha Theme + * Based on https://catppuccin.com + */ + +code[class*='language-'], +pre.ag-paragraph { + color: #cdd6f4; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; + overflow: visible; +} + +pre.ag-paragraph { + padding: 1em; + margin: 1em 0; + border-radius: 0.3em; +} + +:not(pre) > code[class*='language-'], +pre.ag-paragraph { + background: #1e1e2e; +} + +:not(pre) > code[class*='language-'] { + padding: 0.1em; + border-radius: 0.3em; + white-space: normal; +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: #6c7086; +} + +.token.punctuation { + color: #bac2de; +} + +.namespace { + opacity: 0.7; +} + +.token.property, +.token.tag, +.token.constant, +.token.symbol, +.token.deleted { + color: #f38ba8; +} + +.token.boolean, +.token.number { + color: #fab387; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: #a6e3a1; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + color: #89dceb; +} + +.token.atrule, +.token.attr-value, +.token.function, +.token.class-name { + color: #89b4fa; +} + +.token.keyword { + color: #cba6f7; +} + +.token.regex, +.token.important, +.token.variable { + color: #f9e2af; +} + +.token.important, +.token.bold { + font-weight: bold; +} + +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/prismjs/cyberdream.theme.css b/packages/desktop/src/renderer/src/assets/themes/prismjs/cyberdream.theme.css new file mode 100644 index 0000000000..77133506da --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/prismjs/cyberdream.theme.css @@ -0,0 +1,114 @@ +/* + * Prism.js Cyberdream Theme + * Based on https://github.com/scottmckendry/cyberdream.nvim + */ + +code[class*='language-'], +pre.ag-paragraph { + color: #ffffff; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; + overflow: visible; +} + +pre.ag-paragraph { + padding: 1em; + margin: 1em 0; + border-radius: 0.3em; +} + +:not(pre) > code[class*='language-'], +pre.ag-paragraph { + background: #1e2124; +} + +:not(pre) > code[class*='language-'] { + padding: 0.1em; + border-radius: 0.3em; + white-space: normal; +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: #7b8496; +} + +.token.punctuation { + color: #ffffff; +} + +.namespace { + opacity: 0.7; +} + +.token.property, +.token.tag, +.token.constant, +.token.symbol, +.token.deleted { + color: #ff6e5e; +} + +.token.boolean, +.token.number { + color: #ffbd5e; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: #5eff6c; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + color: #5ef1ff; +} + +.token.atrule, +.token.attr-value, +.token.function, +.token.class-name { + color: #5ea1ff; +} + +.token.keyword { + color: #bd5eff; +} + +.token.regex, +.token.important, +.token.variable { + color: #ff5ef1; +} + +.token.important, +.token.bold { + font-weight: bold; +} + +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/prismjs/dark.theme.css b/packages/desktop/src/renderer/src/assets/themes/prismjs/dark.theme.css new file mode 100644 index 0000000000..d6566af03b --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/prismjs/dark.theme.css @@ -0,0 +1,126 @@ +/* + * ------------------------------------ + * Prism.js dark theme + */ + +code[class*='language-'], +pre.ag-paragraph { + color: #f8f8f2; + /*font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;*/ + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; + overflow: visible; +} + +/* Code Fence */ +pre.ag-paragraph { + padding: 1em; + margin: 1em 0; + border-radius: 0.3em; +} + +/* Inline Code */ +:not(pre) > code[class*='language-'], +pre.ag-paragraph { + background: #272822; +} + +/* Inline Code */ +:not(pre) > code[class*='language-'] { + padding: 0.1em; + border-radius: 0.3em; + white-space: normal; +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: slategray; +} + +.token.punctuation { + color: #f8f8f2; +} + +.namespace { + opacity: 0.7; +} + +.token.property, +.token.tag, +.token.constant, +.token.symbol { + color: #f92672; +} + +.token.boolean, +.token.number { + color: #ae81ff; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin { + color: #a6e22e; +} + +.token.inserted { + color: #22863a; + background: #f0fff4; +} + +.token.deleted { + color: #b31d28; + background: #ffeef0; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + color: #e67e65; +} + +.token.atrule, +.token.attr-value, +.token.function, +.token.class-name { + color: #e6db74; +} + +.token.keyword { + color: #66d9ef; +} + +.token.regex, +.token.important, +.token.variable { + color: #fd971f; +} + +.token.important, +.token.bold { + font-weight: bold; +} +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/prismjs/dracula.theme.css b/packages/desktop/src/renderer/src/assets/themes/prismjs/dracula.theme.css new file mode 100644 index 0000000000..f29eb4367a --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/prismjs/dracula.theme.css @@ -0,0 +1,114 @@ +/* + * Prism.js Dracula Theme + * Based on https://draculatheme.com + */ + +code[class*='language-'], +pre.ag-paragraph { + color: #f8f8f2; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; + overflow: visible; +} + +pre.ag-paragraph { + padding: 1em; + margin: 1em 0; + border-radius: 0.3em; +} + +:not(pre) > code[class*='language-'], +pre.ag-paragraph { + background: #282a36; +} + +:not(pre) > code[class*='language-'] { + padding: 0.1em; + border-radius: 0.3em; + white-space: normal; +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: #6272a4; +} + +.token.punctuation { + color: #f8f8f2; +} + +.namespace { + opacity: 0.7; +} + +.token.property, +.token.tag, +.token.constant, +.token.symbol, +.token.deleted { + color: #ff79c6; +} + +.token.boolean, +.token.number { + color: #bd93f9; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: #50fa7b; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + color: #f8f8f2; +} + +.token.atrule, +.token.attr-value, +.token.function, +.token.class-name { + color: #f1fa8c; +} + +.token.keyword { + color: #ff79c6; +} + +.token.regex, +.token.important, +.token.variable { + color: #ffb86c; +} + +.token.important, +.token.bold { + font-weight: bold; +} + +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/prismjs/everforest-dark.theme.css b/packages/desktop/src/renderer/src/assets/themes/prismjs/everforest-dark.theme.css new file mode 100644 index 0000000000..409b95f0c6 --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/prismjs/everforest-dark.theme.css @@ -0,0 +1,114 @@ +/* + * Prism.js Everforest Dark Theme + * Based on https://github.com/sainnhe/everforest + */ + +code[class*='language-'], +pre.ag-paragraph { + color: #d3c6aa; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; + overflow: visible; +} + +pre.ag-paragraph { + padding: 1em; + margin: 1em 0; + border-radius: 0.3em; +} + +:not(pre) > code[class*='language-'], +pre.ag-paragraph { + background: #2d353b; +} + +:not(pre) > code[class*='language-'] { + padding: 0.1em; + border-radius: 0.3em; + white-space: normal; +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: #859289; +} + +.token.punctuation { + color: #d3c6aa; +} + +.namespace { + opacity: 0.7; +} + +.token.property, +.token.tag, +.token.constant, +.token.symbol, +.token.deleted { + color: #e67e80; +} + +.token.boolean, +.token.number { + color: #d699b6; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: #a7c080; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + color: #83c092; +} + +.token.atrule, +.token.attr-value, +.token.function, +.token.class-name { + color: #7fbbb3; +} + +.token.keyword { + color: #e67e80; +} + +.token.regex, +.token.important, +.token.variable { + color: #dbbc7f; +} + +.token.important, +.token.bold { + font-weight: bold; +} + +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/prismjs/everforest-light.theme.css b/packages/desktop/src/renderer/src/assets/themes/prismjs/everforest-light.theme.css new file mode 100644 index 0000000000..f648782279 --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/prismjs/everforest-light.theme.css @@ -0,0 +1,114 @@ +/* + * Prism.js Everforest Light Theme + * Based on https://github.com/sainnhe/everforest + */ + +code[class*='language-'], +pre.ag-paragraph { + color: #5c6a72; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; + overflow: visible; +} + +pre.ag-paragraph { + padding: 1em; + margin: 1em 0; + border-radius: 0.3em; +} + +:not(pre) > code[class*='language-'], +pre.ag-paragraph { + background: #f4f0d9; +} + +:not(pre) > code[class*='language-'] { + padding: 0.1em; + border-radius: 0.3em; + white-space: normal; +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: #939f91; +} + +.token.punctuation { + color: #5c6a72; +} + +.namespace { + opacity: 0.7; +} + +.token.property, +.token.tag, +.token.constant, +.token.symbol, +.token.deleted { + color: #f85552; +} + +.token.boolean, +.token.number { + color: #df69ba; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: #8da101; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + color: #35a77c; +} + +.token.atrule, +.token.attr-value, +.token.function, +.token.class-name { + color: #dfa000; +} + +.token.keyword { + color: #f57d26; +} + +.token.regex, +.token.important, +.token.variable { + color: #3a94c5; +} + +.token.important, +.token.bold { + font-weight: bold; +} + +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/prismjs/graphite.theme.css b/packages/desktop/src/renderer/src/assets/themes/prismjs/graphite.theme.css new file mode 100644 index 0000000000..0ce12a1ac5 --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/prismjs/graphite.theme.css @@ -0,0 +1,114 @@ +/* + * Prism.js Graphite Theme (Light) + * Based on MarkText Graphite theme + */ + +code[class*='language-'], +pre.ag-paragraph { + color: #505558; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; + overflow: visible; +} + +pre.ag-paragraph { + padding: 1em; + margin: 1em 0; + border-radius: 0.3em; +} + +:not(pre) > code[class*='language-'], +pre.ag-paragraph { + background: #ebebeb; +} + +:not(pre) > code[class*='language-'] { + padding: 0.1em; + border-radius: 0.3em; + white-space: normal; +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: #969696; +} + +.token.punctuation { + color: #505558; +} + +.namespace { + opacity: 0.7; +} + +.token.property, +.token.tag, +.token.constant, +.token.symbol, +.token.deleted { + color: #cd5c5c; +} + +.token.boolean, +.token.number { + color: #7b68ee; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: #5f9ea0; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + color: #708090; +} + +.token.atrule, +.token.attr-value, +.token.function, +.token.class-name { + color: #b8860b; +} + +.token.keyword { + color: #6886aa; +} + +.token.regex, +.token.important, +.token.variable { + color: #cd5c5c; +} + +.token.important, +.token.bold { + font-weight: bold; +} + +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/prismjs/gruvbox-dark.theme.css b/packages/desktop/src/renderer/src/assets/themes/prismjs/gruvbox-dark.theme.css new file mode 100644 index 0000000000..1f4aaf32db --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/prismjs/gruvbox-dark.theme.css @@ -0,0 +1,114 @@ +/* + * Prism.js Gruvbox Dark Theme + * Based on https://github.com/morhetz/gruvbox + */ + +code[class*='language-'], +pre.ag-paragraph { + color: #ebdbb2; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; + overflow: visible; +} + +pre.ag-paragraph { + padding: 1em; + margin: 1em 0; + border-radius: 0.3em; +} + +:not(pre) > code[class*='language-'], +pre.ag-paragraph { + background: #282828; +} + +:not(pre) > code[class*='language-'] { + padding: 0.1em; + border-radius: 0.3em; + white-space: normal; +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: #928374; +} + +.token.punctuation { + color: #ebdbb2; +} + +.namespace { + opacity: 0.7; +} + +.token.property, +.token.tag, +.token.constant, +.token.symbol, +.token.deleted { + color: #fb4934; +} + +.token.boolean, +.token.number { + color: #d3869b; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: #b8bb26; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + color: #8ec07c; +} + +.token.atrule, +.token.attr-value, +.token.function, +.token.class-name { + color: #fabd2f; +} + +.token.keyword { + color: #fb4934; +} + +.token.regex, +.token.important, +.token.variable { + color: #fe8019; +} + +.token.important, +.token.bold { + font-weight: bold; +} + +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/prismjs/gruvbox-light.theme.css b/packages/desktop/src/renderer/src/assets/themes/prismjs/gruvbox-light.theme.css new file mode 100644 index 0000000000..4d05cbee4c --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/prismjs/gruvbox-light.theme.css @@ -0,0 +1,114 @@ +/* + * Prism.js Gruvbox Light Theme + * Based on https://github.com/morhetz/gruvbox + */ + +code[class*='language-'], +pre.ag-paragraph { + color: #3c3836; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; + overflow: visible; +} + +pre.ag-paragraph { + padding: 1em; + margin: 1em 0; + border-radius: 0.3em; +} + +:not(pre) > code[class*='language-'], +pre.ag-paragraph { + background: #fbf1c7; +} + +:not(pre) > code[class*='language-'] { + padding: 0.1em; + border-radius: 0.3em; + white-space: normal; +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: #928374; +} + +.token.punctuation { + color: #3c3836; +} + +.namespace { + opacity: 0.7; +} + +.token.property, +.token.tag, +.token.constant, +.token.symbol, +.token.deleted { + color: #9d0006; +} + +.token.boolean, +.token.number { + color: #8f3f71; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: #79740e; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + color: #427b58; +} + +.token.atrule, +.token.attr-value, +.token.function, +.token.class-name { + color: #b57614; +} + +.token.keyword { + color: #9d0006; +} + +.token.regex, +.token.important, +.token.variable { + color: #af3a03; +} + +.token.important, +.token.bold { + font-weight: bold; +} + +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/prismjs/horizon-dark.theme.css b/packages/desktop/src/renderer/src/assets/themes/prismjs/horizon-dark.theme.css new file mode 100644 index 0000000000..3624cc4218 --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/prismjs/horizon-dark.theme.css @@ -0,0 +1,114 @@ +/* + * Prism.js Horizon Dark Theme + * Based on https://horizontheme.com + */ + +code[class*='language-'], +pre.ag-paragraph { + color: #d5d8da; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; + overflow: visible; +} + +pre.ag-paragraph { + padding: 1em; + margin: 1em 0; + border-radius: 0.3em; +} + +:not(pre) > code[class*='language-'], +pre.ag-paragraph { + background: #1c1e26; +} + +:not(pre) > code[class*='language-'] { + padding: 0.1em; + border-radius: 0.3em; + white-space: normal; +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: #6c6f93; +} + +.token.punctuation { + color: #d5d8da; +} + +.namespace { + opacity: 0.7; +} + +.token.property, +.token.tag, +.token.constant, +.token.symbol, +.token.deleted { + color: #e95678; +} + +.token.boolean, +.token.number { + color: #f09483; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: #29d398; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + color: #59e3e3; +} + +.token.atrule, +.token.attr-value, +.token.function, +.token.class-name { + color: #fab795; +} + +.token.keyword { + color: #b877db; +} + +.token.regex, +.token.important, +.token.variable { + color: #fab795; +} + +.token.important, +.token.bold { + font-weight: bold; +} + +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/prismjs/kanagawa.theme.css b/packages/desktop/src/renderer/src/assets/themes/prismjs/kanagawa.theme.css new file mode 100644 index 0000000000..1b1c53f3cf --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/prismjs/kanagawa.theme.css @@ -0,0 +1,114 @@ +/* + * Prism.js Kanagawa Theme + * Based on https://github.com/rebelot/kanagawa.nvim + */ + +code[class*='language-'], +pre.ag-paragraph { + color: #dcd7ba; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; + overflow: visible; +} + +pre.ag-paragraph { + padding: 1em; + margin: 1em 0; + border-radius: 0.3em; +} + +:not(pre) > code[class*='language-'], +pre.ag-paragraph { + background: #1f1f28; +} + +:not(pre) > code[class*='language-'] { + padding: 0.1em; + border-radius: 0.3em; + white-space: normal; +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: #727169; +} + +.token.punctuation { + color: #9cabca; +} + +.namespace { + opacity: 0.7; +} + +.token.property, +.token.tag, +.token.constant, +.token.symbol, +.token.deleted { + color: #c34043; +} + +.token.boolean, +.token.number { + color: #d27e99; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: #98bb6c; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + color: #7fb4ca; +} + +.token.atrule, +.token.attr-value, +.token.function, +.token.class-name { + color: #7e9cd8; +} + +.token.keyword { + color: #957fb8; +} + +.token.regex, +.token.important, +.token.variable { + color: #e6c384; +} + +.token.important, +.token.bold { + font-weight: bold; +} + +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/prismjs/monokai-pro.theme.css b/packages/desktop/src/renderer/src/assets/themes/prismjs/monokai-pro.theme.css new file mode 100644 index 0000000000..bb69658f65 --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/prismjs/monokai-pro.theme.css @@ -0,0 +1,114 @@ +/* + * Prism.js Monokai Pro Theme + * Based on https://monokai.pro + */ + +code[class*='language-'], +pre.ag-paragraph { + color: #fcfcfa; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; + overflow: visible; +} + +pre.ag-paragraph { + padding: 1em; + margin: 1em 0; + border-radius: 0.3em; +} + +:not(pre) > code[class*='language-'], +pre.ag-paragraph { + background: #2d2a2e; +} + +:not(pre) > code[class*='language-'] { + padding: 0.1em; + border-radius: 0.3em; + white-space: normal; +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: #727072; +} + +.token.punctuation { + color: #fcfcfa; +} + +.namespace { + opacity: 0.7; +} + +.token.property, +.token.tag, +.token.constant, +.token.symbol, +.token.deleted { + color: #ff6188; +} + +.token.boolean, +.token.number { + color: #ab9df2; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: #a9dc76; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + color: #78dce8; +} + +.token.atrule, +.token.attr-value, +.token.function, +.token.class-name { + color: #ffd866; +} + +.token.keyword { + color: #ff6188; +} + +.token.regex, +.token.important, +.token.variable { + color: #fc9867; +} + +.token.important, +.token.bold { + font-weight: bold; +} + +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/prismjs/nightfox.theme.css b/packages/desktop/src/renderer/src/assets/themes/prismjs/nightfox.theme.css new file mode 100644 index 0000000000..60280ab1ce --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/prismjs/nightfox.theme.css @@ -0,0 +1,114 @@ +/* + * Prism.js Nightfox Theme + * Based on https://github.com/EdenEast/nightfox.nvim + */ + +code[class*='language-'], +pre.ag-paragraph { + color: #cdcecf; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; + overflow: visible; +} + +pre.ag-paragraph { + padding: 1em; + margin: 1em 0; + border-radius: 0.3em; +} + +:not(pre) > code[class*='language-'], +pre.ag-paragraph { + background: #212e3f; +} + +:not(pre) > code[class*='language-'] { + padding: 0.1em; + border-radius: 0.3em; + white-space: normal; +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: #738091; +} + +.token.punctuation { + color: #cdcecf; +} + +.namespace { + opacity: 0.7; +} + +.token.property, +.token.tag, +.token.constant, +.token.symbol, +.token.deleted { + color: #c94f6d; +} + +.token.boolean, +.token.number { + color: #f4a261; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: #81b29a; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + color: #63cdcf; +} + +.token.atrule, +.token.attr-value, +.token.function, +.token.class-name { + color: #dbc074; +} + +.token.keyword { + color: #9d79d6; +} + +.token.regex, +.token.important, +.token.variable { + color: #719cd6; +} + +.token.important, +.token.bold { + font-weight: bold; +} + +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/prismjs/nord.theme.css b/packages/desktop/src/renderer/src/assets/themes/prismjs/nord.theme.css new file mode 100644 index 0000000000..58bf7efeed --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/prismjs/nord.theme.css @@ -0,0 +1,114 @@ +/* + * Prism.js Nord Theme + * Based on https://nordtheme.com + */ + +code[class*='language-'], +pre.ag-paragraph { + color: #d8dee9; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; + overflow: visible; +} + +pre.ag-paragraph { + padding: 1em; + margin: 1em 0; + border-radius: 0.3em; +} + +:not(pre) > code[class*='language-'], +pre.ag-paragraph { + background: #2e3440; +} + +:not(pre) > code[class*='language-'] { + padding: 0.1em; + border-radius: 0.3em; + white-space: normal; +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: #616e88; +} + +.token.punctuation { + color: #eceff4; +} + +.namespace { + opacity: 0.7; +} + +.token.property, +.token.tag, +.token.constant, +.token.symbol, +.token.deleted { + color: #81a1c1; +} + +.token.boolean, +.token.number { + color: #b48ead; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: #a3be8c; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + color: #81a1c1; +} + +.token.atrule, +.token.attr-value, +.token.function, +.token.class-name { + color: #88c0d0; +} + +.token.keyword { + color: #81a1c1; +} + +.token.regex, +.token.important, +.token.variable { + color: #ebcb8b; +} + +.token.important, +.token.bold { + font-weight: bold; +} + +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/prismjs/one-dark.theme.css b/packages/desktop/src/renderer/src/assets/themes/prismjs/one-dark.theme.css new file mode 100644 index 0000000000..07a2db50a5 --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/prismjs/one-dark.theme.css @@ -0,0 +1,137 @@ +/** + * prism.js default theme for JavaScript, CSS and HTML + * Based on dabblet (http://dabblet.com) + * @author Lea Verou + +The MIT License (MIT) + +Copyright (c) 2016 Adrián + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + */ + +code[class*='language-'], +pre.ag-paragraph { + color: #abb2bf; + /*font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;*/ + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; + overflow: visible; +} + +/* Code Fence */ +pre.ag-paragraph { + padding: 1em; + margin: 1em 0; + border-radius: 0.3em; +} + +/* Inline Code */ +:not(pre) > code[class*='language-'], +pre.ag-paragraph { + background: #3a3f4b; +} + +/* Inline Code */ +:not(pre) > code[class*='language-'] { + padding: 0.1em; + border-radius: 0.3em; + white-space: normal; +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: #5c6370; +} + +.token.punctuation { + color: #abb2bf; +} + +.token.selector, +.token.tag { + color: #e06c75; +} + +.token.property, +.token.boolean, +.token.number, +.token.constant, +.token.symbol, +.token.attr-name, +.token.deleted { + color: #d19a66; +} + +.token.string, +.token.char, +.token.attr-value, +.token.builtin, +.token.inserted { + color: #98c379; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + color: #56b6c2; +} + +.token.atrule, +.token.keyword { + color: #c678dd; +} + +.token.function { + color: #61afef; +} + +.token.regex, +.token.important, +.token.variable { + color: #c678dd; +} + +.token.important, +.token.bold { + font-weight: bold; +} + +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/prismjs/oxocarbon-dark.theme.css b/packages/desktop/src/renderer/src/assets/themes/prismjs/oxocarbon-dark.theme.css new file mode 100644 index 0000000000..9e3fcf3b3b --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/prismjs/oxocarbon-dark.theme.css @@ -0,0 +1,114 @@ +/* + * Prism.js Oxocarbon Dark Theme + * Based on https://github.com/nyoom-engineering/oxocarbon.nvim + */ + +code[class*='language-'], +pre.ag-paragraph { + color: #f2f4f8; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; + overflow: visible; +} + +pre.ag-paragraph { + padding: 1em; + margin: 1em 0; + border-radius: 0.3em; +} + +:not(pre) > code[class*='language-'], +pre.ag-paragraph { + background: #262626; +} + +:not(pre) > code[class*='language-'] { + padding: 0.1em; + border-radius: 0.3em; + white-space: normal; +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: #525252; +} + +.token.punctuation { + color: #f2f4f8; +} + +.namespace { + opacity: 0.7; +} + +.token.property, +.token.tag, +.token.constant, +.token.symbol, +.token.deleted { + color: #ee5396; +} + +.token.boolean, +.token.number { + color: #ff7eb6; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: #42be65; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + color: #3ddbd9; +} + +.token.atrule, +.token.attr-value, +.token.function, +.token.class-name { + color: #78a9ff; +} + +.token.keyword { + color: #be95ff; +} + +.token.regex, +.token.important, +.token.variable { + color: #33b1ff; +} + +.token.important, +.token.bold { + font-weight: bold; +} + +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/prismjs/palenight.theme.css b/packages/desktop/src/renderer/src/assets/themes/prismjs/palenight.theme.css new file mode 100644 index 0000000000..366f25d297 --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/prismjs/palenight.theme.css @@ -0,0 +1,114 @@ +/* + * Prism.js Palenight Theme + * Based on Material Palenight + */ + +code[class*='language-'], +pre.ag-paragraph { + color: #a6accd; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; + overflow: visible; +} + +pre.ag-paragraph { + padding: 1em; + margin: 1em 0; + border-radius: 0.3em; +} + +:not(pre) > code[class*='language-'], +pre.ag-paragraph { + background: #292d3e; +} + +:not(pre) > code[class*='language-'] { + padding: 0.1em; + border-radius: 0.3em; + white-space: normal; +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: #676e95; +} + +.token.punctuation { + color: #89ddff; +} + +.namespace { + opacity: 0.7; +} + +.token.property, +.token.tag, +.token.constant, +.token.symbol, +.token.deleted { + color: #f07178; +} + +.token.boolean, +.token.number { + color: #f78c6c; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: #c3e88d; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + color: #89ddff; +} + +.token.atrule, +.token.attr-value, +.token.function, +.token.class-name { + color: #82aaff; +} + +.token.keyword { + color: #c792ea; +} + +.token.regex, +.token.important, +.token.variable { + color: #ffcb6b; +} + +.token.important, +.token.bold { + font-weight: bold; +} + +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/prismjs/rose-pine-dawn.theme.css b/packages/desktop/src/renderer/src/assets/themes/prismjs/rose-pine-dawn.theme.css new file mode 100644 index 0000000000..6a2ec09408 --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/prismjs/rose-pine-dawn.theme.css @@ -0,0 +1,114 @@ +/* + * Prism.js Rosé Pine Dawn Theme (Light) + * Based on https://rosepinetheme.com + */ + +code[class*='language-'], +pre.ag-paragraph { + color: #575279; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; + overflow: visible; +} + +pre.ag-paragraph { + padding: 1em; + margin: 1em 0; + border-radius: 0.3em; +} + +:not(pre) > code[class*='language-'], +pre.ag-paragraph { + background: #f2e9e1; +} + +:not(pre) > code[class*='language-'] { + padding: 0.1em; + border-radius: 0.3em; + white-space: normal; +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: #9893a5; +} + +.token.punctuation { + color: #797593; +} + +.namespace { + opacity: 0.7; +} + +.token.property, +.token.tag, +.token.constant, +.token.symbol, +.token.deleted { + color: #b4637a; +} + +.token.boolean, +.token.number { + color: #d7827e; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: #ea9d34; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + color: #56949f; +} + +.token.atrule, +.token.attr-value, +.token.function, +.token.class-name { + color: #286983; +} + +.token.keyword { + color: #907aa9; +} + +.token.regex, +.token.important, +.token.variable { + color: #b4637a; +} + +.token.important, +.token.bold { + font-weight: bold; +} + +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/prismjs/rose-pine-moon.theme.css b/packages/desktop/src/renderer/src/assets/themes/prismjs/rose-pine-moon.theme.css new file mode 100644 index 0000000000..7cfd74ebc2 --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/prismjs/rose-pine-moon.theme.css @@ -0,0 +1,114 @@ +/* + * Prism.js Rosé Pine Moon Theme + * Based on https://rosepinetheme.com + */ + +code[class*='language-'], +pre.ag-paragraph { + color: #e0def4; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; + overflow: visible; +} + +pre.ag-paragraph { + padding: 1em; + margin: 1em 0; + border-radius: 0.3em; +} + +:not(pre) > code[class*='language-'], +pre.ag-paragraph { + background: #2a273f; +} + +:not(pre) > code[class*='language-'] { + padding: 0.1em; + border-radius: 0.3em; + white-space: normal; +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: #6e6a86; +} + +.token.punctuation { + color: #908caa; +} + +.namespace { + opacity: 0.7; +} + +.token.property, +.token.tag, +.token.constant, +.token.symbol, +.token.deleted { + color: #eb6f92; +} + +.token.boolean, +.token.number { + color: #ea9a97; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: #f6c177; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + color: #9ccfd8; +} + +.token.atrule, +.token.attr-value, +.token.function, +.token.class-name { + color: #3e8fb0; +} + +.token.keyword { + color: #c4a7e7; +} + +.token.regex, +.token.important, +.token.variable { + color: #eb6f92; +} + +.token.important, +.token.bold { + font-weight: bold; +} + +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/prismjs/rose-pine.theme.css b/packages/desktop/src/renderer/src/assets/themes/prismjs/rose-pine.theme.css new file mode 100644 index 0000000000..85ca0cfc62 --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/prismjs/rose-pine.theme.css @@ -0,0 +1,114 @@ +/* + * Prism.js Rosé Pine Theme + * Based on https://rosepinetheme.com + */ + +code[class*='language-'], +pre.ag-paragraph { + color: #e0def4; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; + overflow: visible; +} + +pre.ag-paragraph { + padding: 1em; + margin: 1em 0; + border-radius: 0.3em; +} + +:not(pre) > code[class*='language-'], +pre.ag-paragraph { + background: #191724; +} + +:not(pre) > code[class*='language-'] { + padding: 0.1em; + border-radius: 0.3em; + white-space: normal; +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: #6e6a86; +} + +.token.punctuation { + color: #908caa; +} + +.namespace { + opacity: 0.7; +} + +.token.property, +.token.tag, +.token.constant, +.token.symbol, +.token.deleted { + color: #eb6f92; +} + +.token.boolean, +.token.number { + color: #f6c177; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: #31748f; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + color: #9ccfd8; +} + +.token.atrule, +.token.attr-value, +.token.function, +.token.class-name { + color: #ebbcba; +} + +.token.keyword { + color: #c4a7e7; +} + +.token.regex, +.token.important, +.token.variable { + color: #f6c177; +} + +.token.important, +.token.bold { + font-weight: bold; +} + +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/prismjs/solarized-dark.theme.css b/packages/desktop/src/renderer/src/assets/themes/prismjs/solarized-dark.theme.css new file mode 100644 index 0000000000..d52b4de49e --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/prismjs/solarized-dark.theme.css @@ -0,0 +1,114 @@ +/* + * Prism.js Solarized Dark Theme + * Based on https://ethanschoonover.com/solarized/ + */ + +code[class*='language-'], +pre.ag-paragraph { + color: #839496; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; + overflow: visible; +} + +pre.ag-paragraph { + padding: 1em; + margin: 1em 0; + border-radius: 0.3em; +} + +:not(pre) > code[class*='language-'], +pre.ag-paragraph { + background: #002b36; +} + +:not(pre) > code[class*='language-'] { + padding: 0.1em; + border-radius: 0.3em; + white-space: normal; +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: #586e75; +} + +.token.punctuation { + color: #93a1a1; +} + +.namespace { + opacity: 0.7; +} + +.token.property, +.token.tag, +.token.constant, +.token.symbol, +.token.deleted { + color: #268bd2; +} + +.token.boolean, +.token.number { + color: #d33682; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: #2aa198; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + color: #cb4b16; +} + +.token.atrule, +.token.attr-value, +.token.function, +.token.class-name { + color: #b58900; +} + +.token.keyword { + color: #859900; +} + +.token.regex, +.token.important, +.token.variable { + color: #cb4b16; +} + +.token.important, +.token.bold { + font-weight: bold; +} + +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/prismjs/solarized-light.theme.css b/packages/desktop/src/renderer/src/assets/themes/prismjs/solarized-light.theme.css new file mode 100644 index 0000000000..0d829bc805 --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/prismjs/solarized-light.theme.css @@ -0,0 +1,114 @@ +/* + * Prism.js Solarized Light Theme + * Based on https://ethanschoonover.com/solarized/ + */ + +code[class*='language-'], +pre.ag-paragraph { + color: #657b83; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; + overflow: visible; +} + +pre.ag-paragraph { + padding: 1em; + margin: 1em 0; + border-radius: 0.3em; +} + +:not(pre) > code[class*='language-'], +pre.ag-paragraph { + background: #fdf6e3; +} + +:not(pre) > code[class*='language-'] { + padding: 0.1em; + border-radius: 0.3em; + white-space: normal; +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: #93a1a1; +} + +.token.punctuation { + color: #586e75; +} + +.namespace { + opacity: 0.7; +} + +.token.property, +.token.tag, +.token.constant, +.token.symbol, +.token.deleted { + color: #268bd2; +} + +.token.boolean, +.token.number { + color: #d33682; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: #2aa198; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + color: #cb4b16; +} + +.token.atrule, +.token.attr-value, +.token.function, +.token.class-name { + color: #b58900; +} + +.token.keyword { + color: #859900; +} + +.token.regex, +.token.important, +.token.variable { + color: #cb4b16; +} + +.token.important, +.token.bold { + font-weight: bold; +} + +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/prismjs/synthwave-84.theme.css b/packages/desktop/src/renderer/src/assets/themes/prismjs/synthwave-84.theme.css new file mode 100644 index 0000000000..2a74fc94e6 --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/prismjs/synthwave-84.theme.css @@ -0,0 +1,114 @@ +/* + * Prism.js Synthwave '84 Theme + * Based on https://github.com/robb0wen/synthwave-vscode + */ + +code[class*='language-'], +pre.ag-paragraph { + color: #f8f8f2; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; + overflow: visible; +} + +pre.ag-paragraph { + padding: 1em; + margin: 1em 0; + border-radius: 0.3em; +} + +:not(pre) > code[class*='language-'], +pre.ag-paragraph { + background: #262335; +} + +:not(pre) > code[class*='language-'] { + padding: 0.1em; + border-radius: 0.3em; + white-space: normal; +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: #848bbd; +} + +.token.punctuation { + color: #36f9f6; +} + +.namespace { + opacity: 0.7; +} + +.token.property, +.token.tag, +.token.constant, +.token.symbol, +.token.deleted { + color: #ff7edb; +} + +.token.boolean, +.token.number { + color: #f97e72; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: #72f1b8; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + color: #36f9f6; +} + +.token.atrule, +.token.attr-value, +.token.function, +.token.class-name { + color: #fede5d; +} + +.token.keyword { + color: #ff7edb; +} + +.token.regex, +.token.important, +.token.variable { + color: #fede5d; +} + +.token.important, +.token.bold { + font-weight: bold; +} + +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/prismjs/tokyo-night-light.theme.css b/packages/desktop/src/renderer/src/assets/themes/prismjs/tokyo-night-light.theme.css new file mode 100644 index 0000000000..f1efd280d4 --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/prismjs/tokyo-night-light.theme.css @@ -0,0 +1,114 @@ +/* + * Prism.js Tokyo Night Light Theme + * Based on https://github.com/folke/tokyonight.nvim + */ + +code[class*='language-'], +pre.ag-paragraph { + color: #343b58; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; + overflow: visible; +} + +pre.ag-paragraph { + padding: 1em; + margin: 1em 0; + border-radius: 0.3em; +} + +:not(pre) > code[class*='language-'], +pre.ag-paragraph { + background: #c8c9ce; +} + +:not(pre) > code[class*='language-'] { + padding: 0.1em; + border-radius: 0.3em; + white-space: normal; +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: #9699a3; +} + +.token.punctuation { + color: #343b58; +} + +.namespace { + opacity: 0.7; +} + +.token.property, +.token.tag, +.token.constant, +.token.symbol, +.token.deleted { + color: #8c4351; +} + +.token.boolean, +.token.number { + color: #965027; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: #485e30; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + color: #166775; +} + +.token.atrule, +.token.attr-value, +.token.function, +.token.class-name { + color: #34548a; +} + +.token.keyword { + color: #5a4a78; +} + +.token.regex, +.token.important, +.token.variable { + color: #8f5e15; +} + +.token.important, +.token.bold { + font-weight: bold; +} + +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/prismjs/tokyo-night-storm.theme.css b/packages/desktop/src/renderer/src/assets/themes/prismjs/tokyo-night-storm.theme.css new file mode 100644 index 0000000000..d8d42b6dc0 --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/prismjs/tokyo-night-storm.theme.css @@ -0,0 +1,114 @@ +/* + * Prism.js Tokyo Night Storm Theme + * Based on https://github.com/folke/tokyonight.nvim + */ + +code[class*='language-'], +pre.ag-paragraph { + color: #c0caf5; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; + overflow: visible; +} + +pre.ag-paragraph { + padding: 1em; + margin: 1em 0; + border-radius: 0.3em; +} + +:not(pre) > code[class*='language-'], +pre.ag-paragraph { + background: #1f2335; +} + +:not(pre) > code[class*='language-'] { + padding: 0.1em; + border-radius: 0.3em; + white-space: normal; +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: #565f89; +} + +.token.punctuation { + color: #c0caf5; +} + +.namespace { + opacity: 0.7; +} + +.token.property, +.token.tag, +.token.constant, +.token.symbol, +.token.deleted { + color: #f7768e; +} + +.token.boolean, +.token.number { + color: #ff9e64; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: #9ece6a; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + color: #7dcfff; +} + +.token.atrule, +.token.attr-value, +.token.function, +.token.class-name { + color: #7aa2f7; +} + +.token.keyword { + color: #bb9af7; +} + +.token.regex, +.token.important, +.token.variable { + color: #e0af68; +} + +.token.important, +.token.bold { + font-weight: bold; +} + +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/prismjs/tokyo-night.theme.css b/packages/desktop/src/renderer/src/assets/themes/prismjs/tokyo-night.theme.css new file mode 100644 index 0000000000..af83414ff4 --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/prismjs/tokyo-night.theme.css @@ -0,0 +1,114 @@ +/* + * Prism.js Tokyo Night Theme + * Based on https://github.com/enkia/tokyo-night-vscode-theme + */ + +code[class*='language-'], +pre.ag-paragraph { + color: #a9b1d6; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; + overflow: visible; +} + +pre.ag-paragraph { + padding: 1em; + margin: 1em 0; + border-radius: 0.3em; +} + +:not(pre) > code[class*='language-'], +pre.ag-paragraph { + background: #1a1b26; +} + +:not(pre) > code[class*='language-'] { + padding: 0.1em; + border-radius: 0.3em; + white-space: normal; +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: #565f89; +} + +.token.punctuation { + color: #9aa5ce; +} + +.namespace { + opacity: 0.7; +} + +.token.property, +.token.tag, +.token.constant, +.token.symbol, +.token.deleted { + color: #f7768e; +} + +.token.boolean, +.token.number { + color: #ff9e64; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: #9ece6a; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + color: #89ddff; +} + +.token.atrule, +.token.attr-value, +.token.function, +.token.class-name { + color: #7aa2f7; +} + +.token.keyword { + color: #bb9af7; +} + +.token.regex, +.token.important, +.token.variable { + color: #e0af68; +} + +.token.important, +.token.bold { + font-weight: bold; +} + +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/prismjs/ulysses.theme.css b/packages/desktop/src/renderer/src/assets/themes/prismjs/ulysses.theme.css new file mode 100644 index 0000000000..810a995bec --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/prismjs/ulysses.theme.css @@ -0,0 +1,114 @@ +/* + * Prism.js Ulysses Theme (Light) + * Based on MarkText Ulysses theme + */ + +code[class*='language-'], +pre.ag-paragraph { + color: #656565; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; + overflow: visible; +} + +pre.ag-paragraph { + padding: 1em; + margin: 1em 0; + border-radius: 0.3em; +} + +:not(pre) > code[class*='language-'], +pre.ag-paragraph { + background: #e9e9e9; +} + +:not(pre) > code[class*='language-'] { + padding: 0.1em; + border-radius: 0.3em; + white-space: normal; +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: #999999; +} + +.token.punctuation { + color: #656565; +} + +.namespace { + opacity: 0.7; +} + +.token.property, +.token.tag, +.token.constant, +.token.symbol, +.token.deleted { + color: #c75991; +} + +.token.boolean, +.token.number { + color: #7a70ba; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: #1a9f7f; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + color: #708090; +} + +.token.atrule, +.token.attr-value, +.token.function, +.token.class-name { + color: #d16a3d; +} + +.token.keyword { + color: #0c8bba; +} + +.token.regex, +.token.important, +.token.variable { + color: #c75991; +} + +.token.important, +.token.bold { + font-weight: bold; +} + +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/rose-pine-dawn.theme.css b/packages/desktop/src/renderer/src/assets/themes/rose-pine-dawn.theme.css new file mode 100644 index 0000000000..85b1211ad2 --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/rose-pine-dawn.theme.css @@ -0,0 +1,214 @@ +:root { + /*editor - Rosé Pine Dawn theme*/ + --themeColor: #907aa9; + --themeColor90: rgba(144, 122, 169, 0.9); + --themeColor80: rgba(144, 122, 169, 0.8); + --themeColor70: rgba(144, 122, 169, 0.7); + --themeColor60: rgba(144, 122, 169, 0.6); + --themeColor50: rgba(144, 122, 169, 0.5); + --themeColor40: rgba(144, 122, 169, 0.4); + --themeColor30: rgba(144, 122, 169, 0.3); + --themeColor20: rgba(144, 122, 169, 0.2); + --themeColor10: rgba(144, 122, 169, 0.1); + + --highlightColor: rgba(144, 122, 169, 0.3); + --selectionColor: rgba(242, 233, 222, 0.8); + --editorColor: #575279; + --editorColor80: rgba(87, 82, 121, 0.8); + --editorColor60: rgba(87, 82, 121, 0.6); + --editorColor50: rgba(87, 82, 121, 0.5); + --editorColor40: rgba(87, 82, 121, 0.4); + --editorColor30: rgba(87, 82, 121, 0.3); + --editorColor10: rgba(87, 82, 121, 0.1); + --editorColor04: rgba(87, 82, 121, 0.04); + --editorBgColor: #faf4ed; + --deleteColor: #b4637a; + --iconColor: rgba(87, 82, 121, 0.56); + --codeBgColor: #f2e9e1; + --codeBlockBgColor: #f2e9e1; + --footnoteBgColor: rgba(242, 233, 225, 0.5); + --inputBgColor: #f6ede5; + + --focusColor: var(--themeColor); + + --buttonFontColor: rgba(87, 82, 121, 0.7); + --buttonBgColor: #f2e9e1; + --buttonBorder: 1px solid rgba(0, 0, 0, 0.08); + --buttonShadow: none; + --buttonFontColorHover: var(--buttonFontColor); + --buttonBgColorHover: #e9dfd7; + --buttonBorderHover: 1px solid rgba(0, 0, 0, 0.12); + --buttonFontColorActive: var(--buttonFontColor); + --buttonBgColorActive: #e0d6cd; + --buttonBorderActive: var(--buttonBorder); + + --buttonPrimaryFontColor: #ffffff; + --buttonPrimaryBgColor: var(--themeColor); + --buttonPrimaryBorder: none; + --buttonPrimaryShadow: none; + --buttonPrimaryFontColorHover: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorHover: #d7827e; + --buttonPrimaryBorderHover: var(--buttonPrimaryBorder); + --buttonPrimaryFontColorActive: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorActive: #7a6591; + --buttonPrimaryBorderActive: var(--buttonPrimaryBorder); + --buttonPrimaryFocusBorder: none; + --buttonPrimaryFocusShadow: inset 0 0 0 1px rgba(255, 255, 255, 0.5), 0 0 0 1px var(--themeColor); + --tableBorderColor: #e0d6cd; + + /* Markdown element colors */ + --headingColor: #575279; + --h1Color: #907aa9; + --h2Color: #d7827e; + --h3Color: #ea9d34; + --h4Color: #286983; + --h5Color: #56949f; + --h6Color: #b4637a; + --blockquoteTextColor: rgba(87, 82, 121, 0.6); + --blockquoteBorderColor: #9893a5; + --hrColor: #e0d6cd; + --linkColor: #286983; + --strongColor: #575279; + --emColor: #d7827e; + --listMarkerColor: #907aa9; + + /*marktext*/ + --sideBarColor: rgba(87, 82, 121, 0.7); + --sideBarIconColor: var(--iconColor); + --sideBarTitleColor: rgba(87, 82, 121, 0.9); + --sideBarTextColor: rgba(87, 82, 121, 0.5); + --sideBarBgColor: #f6eee7; + --sideBarItemHoverBgColor: rgba(87, 82, 121, 0.05); + --itemBgColor: #e9dfd7; + + --floatFontColor: rgba(87, 82, 121, 0.8); + --floatBgColor: #f2e9e1; + --floatHoverColor: rgba(87, 82, 121, 0.06); + --floatBorderColor: rgba(0, 0, 0, 0.08); + --floatShadow: rgba(0, 0, 0, 0.1); + --maskColor: rgba(0, 0, 0, 0.3); + --editorAreaWidth: 750px; + + /* @muyajs/core (kebab-case) editor variables + Additive prep for the muyajs -> @muyajs/core editor swap. These + mirror the camelCase vars above; the legacy editor ignores them. + Do NOT remove the camelCase vars — they still drive the current + editor until editor.vue switches engines. */ + --theme-color: var(--themeColor); + --highlight-color: var(--highlightColor); + --selection-color: var(--selectionColor); + --editor-color: var(--editorColor); + --editor-color-80: var(--editorColor80); + --editor-color-50: var(--editorColor50); + --editor-color-30: var(--editorColor30); + --editor-color-10: var(--editorColor10); + --editor-color-04: var(--editorColor04); + --editor-bg-color: var(--editorBgColor); + --editor-area-width: var(--editorAreaWidth); + --delete-color: var(--deleteColor); + --icon-color: var(--iconColor); + --code-block-bg-color: var(--codeBlockBgColor); + --table-border-color: var(--tableBorderColor); + --input-bg-color: var(--inputBgColor); + --button-font-color: var(--buttonFontColor); + --button-bg-color: var(--buttonBgColor); + --button-border: var(--buttonBorder); + --button-bg-color-hover: var(--buttonBgColorHover); + --button-border-hover: var(--buttonBorderHover); + --button-bg-color-active: var(--buttonBgColorActive); + --button-border-active: var(--buttonBorderActive); + --button-border-focus: var(--button-border); + --float-bg-color: var(--floatBgColor); + --float-hover-color: var(--floatHoverColor); + --float-border-color: var(--floatBorderColor); + --float-shadow: rgb(15 15 15 / 3%) 0 0 0 1px, rgb(15 15 15 / 4%) 0 3px 6px, rgb(15 15 15 / 5%) 0 9px 24px; + --link-color: var(--linkColor); + --h1-color: var(--h1Color); + --h2-color: var(--h2Color); + --h3-color: var(--h3Color); + --h4-color: var(--h4Color); + --h5-color: var(--h5Color); + --h6-color: var(--h6Color); + --blockquote-text-color: var(--blockquoteTextColor); + --blockquote-border-color: var(--blockquoteBorderColor); + --strong-color: var(--strongColor); + --em-color: var(--emColor); + --list-marker-color: var(--listMarkerColor); + --hr-color: var(--hrColor); +} + +::-webkit-scrollbar { + background: var(--editorBgColor); +} + +.mu-front-menu, +.mu-float-wrapper { + box-shadow: 0 4px 8px 0 var(--floatShadow) !important; +} + +.title-bar .frameless-titlebar-button > div > svg { + fill: #575279; +} +.title-bar .frameless-titlebar-minimize:hover, +.title-bar .frameless-titlebar-toggle:hover { + background-color: rgba(87, 82, 121, 0.08); +} + +.side-bar { + border-right: 1px solid #e0d6cd !important; +} + +.recent-files-projects a, +.open-project a { + box-shadow: none !important; +} + +.editor-tabs { + box-shadow: none !important; +} +.editor-tabs:after { + position: absolute; + content: ''; + border-bottom: 1px solid #e0d6cd; + bottom: 0; + left: 0; + right: 0; + z-index: 1; +} +.editor-tabs ul.tabs-container:after { + position: absolute; + content: ''; + border-bottom: 1px solid #e0d6cd; + bottom: 0; + left: 0; + right: 0; + z-index: 2; +} + +.tabs-container > li, +.tabs-container > li.active { + background: var(--editorBgColor) !important; +} + +.open-project button, +.recent-files-projects button { + box-shadow: none !important; +} + +/* ------------------------------------ */ + +:not(pre) > code[class*='language-'], +pre:not(.CodeMirror-line), +pre[class*='language-'], +pre.mu-code-block { + background: var(--codeBlockBgColor) !important; + border: none !important; +} +.mu-thematic-break:not(.mu-active)::before { + border-top: 2px dashed var(--editorColor10) !important; + background: none !important; +} +figure.mu-active.mu-math-block > div.mu-math-preview, +figure.mu-active.mu-diagram-block > div.mu-diagram-preview { + box-shadow: 0 3px 8px 0 var(--floatShadow) !important; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/rose-pine-moon.theme.css b/packages/desktop/src/renderer/src/assets/themes/rose-pine-moon.theme.css new file mode 100644 index 0000000000..f30f9322d5 --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/rose-pine-moon.theme.css @@ -0,0 +1,214 @@ +:root { + /*editor - Rosé Pine Moon theme*/ + --themeColor: #c4a7e7; + --themeColor90: rgba(196, 167, 231, 0.9); + --themeColor80: rgba(196, 167, 231, 0.8); + --themeColor70: rgba(196, 167, 231, 0.7); + --themeColor60: rgba(196, 167, 231, 0.6); + --themeColor50: rgba(196, 167, 231, 0.5); + --themeColor40: rgba(196, 167, 231, 0.4); + --themeColor30: rgba(196, 167, 231, 0.3); + --themeColor20: rgba(196, 167, 231, 0.2); + --themeColor10: rgba(196, 167, 231, 0.1); + + --highlightColor: rgba(196, 167, 231, 0.6); + --selectionColor: rgba(57, 53, 82, 0.8); + --editorColor: #e0def4; + --editorColor80: rgba(224, 222, 244, 0.8); + --editorColor60: rgba(224, 222, 244, 0.6); + --editorColor50: rgba(224, 222, 244, 0.5); + --editorColor40: rgba(224, 222, 244, 0.4); + --editorColor30: rgba(224, 222, 244, 0.3); + --editorColor10: rgba(224, 222, 244, 0.1); + --editorColor04: rgba(224, 222, 244, 0.04); + --editorBgColor: #232136; + --deleteColor: #eb6f92; + --iconColor: rgba(224, 222, 244, 0.56); + --codeBgColor: #2a273f; + --codeBlockBgColor: #2a273f; + --footnoteBgColor: rgba(42, 39, 63, 0.3); + --inputBgColor: #1b1929; + + --focusColor: var(--themeColor); + + --buttonFontColor: rgba(224, 222, 244, 0.6); + --buttonBgColor: #2a273f; + --buttonBorder: 1px solid rgba(0, 0, 0, 0.2); + --buttonShadow: none; + --buttonFontColorHover: var(--buttonFontColor); + --buttonBgColorHover: #393552; + --buttonBorderHover: 1px solid rgba(0, 0, 0, 0.3); + --buttonFontColorActive: var(--buttonFontColor); + --buttonBgColorActive: #242137; + --buttonBorderActive: var(--buttonBorder); + + --buttonPrimaryFontColor: #232136; + --buttonPrimaryBgColor: var(--themeColor); + --buttonPrimaryBorder: none; + --buttonPrimaryShadow: none; + --buttonPrimaryFontColorHover: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorHover: #ea9a97; + --buttonPrimaryBorderHover: var(--buttonPrimaryBorder); + --buttonPrimaryFontColorActive: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorActive: #a88fc2; + --buttonPrimaryBorderActive: var(--buttonPrimaryBorder); + --buttonPrimaryFocusBorder: none; + --buttonPrimaryFocusShadow: inset 0 0 0 1px rgba(24, 26, 31, 0.5), 0 0 0 1px var(--themeColor); + --tableBorderColor: #2a273f; + + /* Markdown element colors */ + --headingColor: #e0def4; + --h1Color: #c4a7e7; + --h2Color: #ea9a97; + --h3Color: #f6c177; + --h4Color: #9ccfd8; + --h5Color: #3e8fb0; + --h6Color: #eb6f92; + --blockquoteTextColor: rgba(224, 222, 244, 0.6); + --blockquoteBorderColor: #44415a; + --hrColor: #393552; + --linkColor: #9ccfd8; + --strongColor: #e0def4; + --emColor: #ea9a97; + --listMarkerColor: #c4a7e7; + + /*marktext*/ + --sideBarColor: rgba(224, 222, 244, 0.6); + --sideBarIconColor: var(--iconColor); + --sideBarTitleColor: rgba(224, 222, 244, 0.8); + --sideBarTextColor: rgba(224, 222, 244, 0.4); + --sideBarBgColor: #1f1d2f; + --sideBarItemHoverBgColor: rgba(224, 222, 244, 0.03); + --itemBgColor: #2a273f; + + --floatFontColor: rgba(224, 222, 244, 0.7); + --floatBgColor: #2a273f; + --floatHoverColor: rgba(224, 222, 244, 0.04); + --floatBorderColor: rgba(0, 0, 0, 0.05); + --floatShadow: rgba(0, 0, 0, 0.2); + --maskColor: rgba(0, 0, 0, 0.7); + --editorAreaWidth: 750px; + + /* @muyajs/core (kebab-case) editor variables + Additive prep for the muyajs -> @muyajs/core editor swap. These + mirror the camelCase vars above; the legacy editor ignores them. + Do NOT remove the camelCase vars — they still drive the current + editor until editor.vue switches engines. */ + --theme-color: var(--themeColor); + --highlight-color: var(--highlightColor); + --selection-color: var(--selectionColor); + --editor-color: var(--editorColor); + --editor-color-80: var(--editorColor80); + --editor-color-50: var(--editorColor50); + --editor-color-30: var(--editorColor30); + --editor-color-10: var(--editorColor10); + --editor-color-04: var(--editorColor04); + --editor-bg-color: var(--editorBgColor); + --editor-area-width: var(--editorAreaWidth); + --delete-color: var(--deleteColor); + --icon-color: var(--iconColor); + --code-block-bg-color: var(--codeBlockBgColor); + --table-border-color: var(--tableBorderColor); + --input-bg-color: var(--inputBgColor); + --button-font-color: var(--buttonFontColor); + --button-bg-color: var(--buttonBgColor); + --button-border: var(--buttonBorder); + --button-bg-color-hover: var(--buttonBgColorHover); + --button-border-hover: var(--buttonBorderHover); + --button-bg-color-active: var(--buttonBgColorActive); + --button-border-active: var(--buttonBorderActive); + --button-border-focus: var(--button-border); + --float-bg-color: var(--floatBgColor); + --float-hover-color: var(--floatHoverColor); + --float-border-color: var(--floatBorderColor); + --float-shadow: rgb(15 15 15 / 3%) 0 0 0 1px, rgb(15 15 15 / 4%) 0 3px 6px, rgb(15 15 15 / 5%) 0 9px 24px; + --link-color: var(--linkColor); + --h1-color: var(--h1Color); + --h2-color: var(--h2Color); + --h3-color: var(--h3Color); + --h4-color: var(--h4Color); + --h5-color: var(--h5Color); + --h6-color: var(--h6Color); + --blockquote-text-color: var(--blockquoteTextColor); + --blockquote-border-color: var(--blockquoteBorderColor); + --strong-color: var(--strongColor); + --em-color: var(--emColor); + --list-marker-color: var(--listMarkerColor); + --hr-color: var(--hrColor); +} + +::-webkit-scrollbar { + background: var(--editorBgColor); +} + +.mu-front-menu, +.mu-float-wrapper { + box-shadow: 0 4px 8px 0 var(--floatShadow) !important; +} + +.title-bar .frameless-titlebar-button > div > svg { + fill: #e0def4; +} +.title-bar .frameless-titlebar-minimize:hover, +.title-bar .frameless-titlebar-toggle:hover { + background-color: rgba(224, 222, 244, 0.05); +} + +.side-bar { + border-right: 1px solid #171523 !important; +} + +.recent-files-projects a, +.open-project a { + box-shadow: none !important; +} + +.editor-tabs { + box-shadow: none !important; +} +.editor-tabs:after { + position: absolute; + content: ''; + border-bottom: 1px solid #171523; + bottom: 0; + left: 0; + right: 0; + z-index: 1; +} +.editor-tabs ul.tabs-container:after { + position: absolute; + content: ''; + border-bottom: 1px solid #171523; + bottom: 0; + left: 0; + right: 0; + z-index: 2; +} + +.tabs-container > li, +.tabs-container > li.active { + background: var(--editorBgColor) !important; +} + +.open-project button, +.recent-files-projects button { + box-shadow: none !important; +} + +/* ------------------------------------ */ + +:not(pre) > code[class*='language-'], +pre:not(.CodeMirror-line), +pre[class*='language-'], +pre.mu-code-block { + background: var(--codeBlockBgColor) !important; + border: none !important; +} +.mu-thematic-break:not(.mu-active)::before { + border-top: 2px dashed var(--editorColor10) !important; + background: none !important; +} +figure.mu-active.mu-math-block > div.mu-math-preview, +figure.mu-active.mu-diagram-block > div.mu-diagram-preview { + box-shadow: 0 3px 8px 0 var(--floatShadow) !important; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/rose-pine.theme.css b/packages/desktop/src/renderer/src/assets/themes/rose-pine.theme.css new file mode 100644 index 0000000000..5a5df7db23 --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/rose-pine.theme.css @@ -0,0 +1,214 @@ +:root { + /*editor - Rosé Pine theme*/ + --themeColor: #c4a7e7; + --themeColor90: rgba(196, 167, 231, 0.9); + --themeColor80: rgba(196, 167, 231, 0.8); + --themeColor70: rgba(196, 167, 231, 0.7); + --themeColor60: rgba(196, 167, 231, 0.6); + --themeColor50: rgba(196, 167, 231, 0.5); + --themeColor40: rgba(196, 167, 231, 0.4); + --themeColor30: rgba(196, 167, 231, 0.3); + --themeColor20: rgba(196, 167, 231, 0.2); + --themeColor10: rgba(196, 167, 231, 0.1); + + --highlightColor: rgba(196, 167, 231, 0.6); + --selectionColor: rgba(38, 35, 53, 0.8); + --editorColor: #e0def4; + --editorColor80: rgba(224, 222, 244, 0.8); + --editorColor60: rgba(224, 222, 244, 0.6); + --editorColor50: rgba(224, 222, 244, 0.5); + --editorColor40: rgba(224, 222, 244, 0.4); + --editorColor30: rgba(224, 222, 244, 0.3); + --editorColor10: rgba(224, 222, 244, 0.1); + --editorColor04: rgba(224, 222, 244, 0.04); + --editorBgColor: #191724; + --deleteColor: #eb6f92; + --iconColor: rgba(224, 222, 244, 0.56); + --codeBgColor: #1f1d2e; + --codeBlockBgColor: #1f1d2e; + --footnoteBgColor: rgba(31, 29, 46, 0.3); + --inputBgColor: #13111e; + + --focusColor: var(--themeColor); + + --buttonFontColor: rgba(224, 222, 244, 0.6); + --buttonBgColor: #1f1d2e; + --buttonBorder: 1px solid rgba(0, 0, 0, 0.2); + --buttonShadow: none; + --buttonFontColorHover: var(--buttonFontColor); + --buttonBgColorHover: #26233a; + --buttonBorderHover: 1px solid rgba(0, 0, 0, 0.3); + --buttonFontColorActive: var(--buttonFontColor); + --buttonBgColorActive: #1a1826; + --buttonBorderActive: var(--buttonBorder); + + --buttonPrimaryFontColor: #191724; + --buttonPrimaryBgColor: var(--themeColor); + --buttonPrimaryBorder: none; + --buttonPrimaryShadow: none; + --buttonPrimaryFontColorHover: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorHover: #ebbcba; + --buttonPrimaryBorderHover: var(--buttonPrimaryBorder); + --buttonPrimaryFontColorActive: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorActive: #a88fc2; + --buttonPrimaryBorderActive: var(--buttonPrimaryBorder); + --buttonPrimaryFocusBorder: none; + --buttonPrimaryFocusShadow: inset 0 0 0 1px rgba(24, 26, 31, 0.5), 0 0 0 1px var(--themeColor); + --tableBorderColor: #1f1d2e; + + /* Markdown element colors */ + --headingColor: #e0def4; + --h1Color: #c4a7e7; + --h2Color: #ebbcba; + --h3Color: #f6c177; + --h4Color: #9ccfd8; + --h5Color: #31748f; + --h6Color: #eb6f92; + --blockquoteTextColor: rgba(224, 222, 244, 0.6); + --blockquoteBorderColor: #403d52; + --hrColor: #26233a; + --linkColor: #9ccfd8; + --strongColor: #e0def4; + --emColor: #ebbcba; + --listMarkerColor: #c4a7e7; + + /*marktext*/ + --sideBarColor: rgba(224, 222, 244, 0.6); + --sideBarIconColor: var(--iconColor); + --sideBarTitleColor: rgba(224, 222, 244, 0.8); + --sideBarTextColor: rgba(224, 222, 244, 0.4); + --sideBarBgColor: #161421; + --sideBarItemHoverBgColor: rgba(224, 222, 244, 0.03); + --itemBgColor: #1f1d2e; + + --floatFontColor: rgba(224, 222, 244, 0.7); + --floatBgColor: #1f1d2e; + --floatHoverColor: rgba(224, 222, 244, 0.04); + --floatBorderColor: rgba(0, 0, 0, 0.05); + --floatShadow: rgba(0, 0, 0, 0.2); + --maskColor: rgba(0, 0, 0, 0.7); + --editorAreaWidth: 750px; + + /* @muyajs/core (kebab-case) editor variables + Additive prep for the muyajs -> @muyajs/core editor swap. These + mirror the camelCase vars above; the legacy editor ignores them. + Do NOT remove the camelCase vars — they still drive the current + editor until editor.vue switches engines. */ + --theme-color: var(--themeColor); + --highlight-color: var(--highlightColor); + --selection-color: var(--selectionColor); + --editor-color: var(--editorColor); + --editor-color-80: var(--editorColor80); + --editor-color-50: var(--editorColor50); + --editor-color-30: var(--editorColor30); + --editor-color-10: var(--editorColor10); + --editor-color-04: var(--editorColor04); + --editor-bg-color: var(--editorBgColor); + --editor-area-width: var(--editorAreaWidth); + --delete-color: var(--deleteColor); + --icon-color: var(--iconColor); + --code-block-bg-color: var(--codeBlockBgColor); + --table-border-color: var(--tableBorderColor); + --input-bg-color: var(--inputBgColor); + --button-font-color: var(--buttonFontColor); + --button-bg-color: var(--buttonBgColor); + --button-border: var(--buttonBorder); + --button-bg-color-hover: var(--buttonBgColorHover); + --button-border-hover: var(--buttonBorderHover); + --button-bg-color-active: var(--buttonBgColorActive); + --button-border-active: var(--buttonBorderActive); + --button-border-focus: var(--button-border); + --float-bg-color: var(--floatBgColor); + --float-hover-color: var(--floatHoverColor); + --float-border-color: var(--floatBorderColor); + --float-shadow: rgb(15 15 15 / 3%) 0 0 0 1px, rgb(15 15 15 / 4%) 0 3px 6px, rgb(15 15 15 / 5%) 0 9px 24px; + --link-color: var(--linkColor); + --h1-color: var(--h1Color); + --h2-color: var(--h2Color); + --h3-color: var(--h3Color); + --h4-color: var(--h4Color); + --h5-color: var(--h5Color); + --h6-color: var(--h6Color); + --blockquote-text-color: var(--blockquoteTextColor); + --blockquote-border-color: var(--blockquoteBorderColor); + --strong-color: var(--strongColor); + --em-color: var(--emColor); + --list-marker-color: var(--listMarkerColor); + --hr-color: var(--hrColor); +} + +::-webkit-scrollbar { + background: var(--editorBgColor); +} + +.mu-front-menu, +.mu-float-wrapper { + box-shadow: 0 4px 8px 0 var(--floatShadow) !important; +} + +.title-bar .frameless-titlebar-button > div > svg { + fill: #e0def4; +} +.title-bar .frameless-titlebar-minimize:hover, +.title-bar .frameless-titlebar-toggle:hover { + background-color: rgba(224, 222, 244, 0.05); +} + +.side-bar { + border-right: 1px solid #0f0d19 !important; +} + +.recent-files-projects a, +.open-project a { + box-shadow: none !important; +} + +.editor-tabs { + box-shadow: none !important; +} +.editor-tabs:after { + position: absolute; + content: ''; + border-bottom: 1px solid #0f0d19; + bottom: 0; + left: 0; + right: 0; + z-index: 1; +} +.editor-tabs ul.tabs-container:after { + position: absolute; + content: ''; + border-bottom: 1px solid #0f0d19; + bottom: 0; + left: 0; + right: 0; + z-index: 2; +} + +.tabs-container > li, +.tabs-container > li.active { + background: var(--editorBgColor) !important; +} + +.open-project button, +.recent-files-projects button { + box-shadow: none !important; +} + +/* ------------------------------------ */ + +:not(pre) > code[class*='language-'], +pre:not(.CodeMirror-line), +pre[class*='language-'], +pre.mu-code-block { + background: var(--codeBlockBgColor) !important; + border: none !important; +} +.mu-thematic-break:not(.mu-active)::before { + border-top: 2px dashed var(--editorColor10) !important; + background: none !important; +} +figure.mu-active.mu-math-block > div.mu-math-preview, +figure.mu-active.mu-diagram-block > div.mu-diagram-preview { + box-shadow: 0 3px 8px 0 var(--floatShadow) !important; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/solarized-dark.theme.css b/packages/desktop/src/renderer/src/assets/themes/solarized-dark.theme.css new file mode 100644 index 0000000000..db64f7620e --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/solarized-dark.theme.css @@ -0,0 +1,214 @@ +:root { + /*editor - Solarized Dark theme*/ + --themeColor: #268bd2; + --themeColor90: rgba(38, 139, 210, 0.9); + --themeColor80: rgba(38, 139, 210, 0.8); + --themeColor70: rgba(38, 139, 210, 0.7); + --themeColor60: rgba(38, 139, 210, 0.6); + --themeColor50: rgba(38, 139, 210, 0.5); + --themeColor40: rgba(38, 139, 210, 0.4); + --themeColor30: rgba(38, 139, 210, 0.3); + --themeColor20: rgba(38, 139, 210, 0.2); + --themeColor10: rgba(38, 139, 210, 0.1); + + --highlightColor: rgba(38, 139, 210, 0.6); + --selectionColor: rgba(7, 54, 66, 0.8); + --editorColor: #839496; + --editorColor80: rgba(131, 148, 150, 0.8); + --editorColor60: rgba(131, 148, 150, 0.6); + --editorColor50: rgba(131, 148, 150, 0.5); + --editorColor40: rgba(131, 148, 150, 0.4); + --editorColor30: rgba(131, 148, 150, 0.3); + --editorColor10: rgba(131, 148, 150, 0.1); + --editorColor04: rgba(131, 148, 150, 0.04); + --editorBgColor: #002b36; + --deleteColor: #dc322f; + --iconColor: rgba(131, 148, 150, 0.56); + --codeBgColor: #073642; + --codeBlockBgColor: #073642; + --footnoteBgColor: rgba(7, 54, 66, 0.3); + --inputBgColor: #001e26; + + --focusColor: var(--themeColor); + + --buttonFontColor: rgba(131, 148, 150, 0.6); + --buttonBgColor: #073642; + --buttonBorder: 1px solid rgba(0, 0, 0, 0.2); + --buttonShadow: none; + --buttonFontColorHover: var(--buttonFontColor); + --buttonBgColorHover: #0a4a5a; + --buttonBorderHover: 1px solid rgba(0, 0, 0, 0.3); + --buttonFontColorActive: var(--buttonFontColor); + --buttonBgColorActive: #052a35; + --buttonBorderActive: var(--buttonBorder); + + --buttonPrimaryFontColor: #002b36; + --buttonPrimaryBgColor: var(--themeColor); + --buttonPrimaryBorder: none; + --buttonPrimaryShadow: none; + --buttonPrimaryFontColorHover: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorHover: #2aa198; + --buttonPrimaryBorderHover: var(--buttonPrimaryBorder); + --buttonPrimaryFontColorActive: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorActive: #1a6fb0; + --buttonPrimaryBorderActive: var(--buttonPrimaryBorder); + --buttonPrimaryFocusBorder: none; + --buttonPrimaryFocusShadow: inset 0 0 0 1px rgba(24, 26, 31, 0.5), 0 0 0 1px var(--themeColor); + --tableBorderColor: #073642; + + /* Markdown element colors */ + --headingColor: #93a1a1; + --h1Color: #dc322f; + --h2Color: #cb4b16; + --h3Color: #b58900; + --h4Color: #859900; + --h5Color: #2aa198; + --h6Color: #268bd2; + --blockquoteTextColor: rgba(131, 148, 150, 0.6); + --blockquoteBorderColor: #586e75; + --hrColor: #073642; + --linkColor: #268bd2; + --strongColor: #93a1a1; + --emColor: #b58900; + --listMarkerColor: #cb4b16; + + /*marktext*/ + --sideBarColor: rgba(131, 148, 150, 0.6); + --sideBarIconColor: var(--iconColor); + --sideBarTitleColor: rgba(131, 148, 150, 0.8); + --sideBarTextColor: rgba(131, 148, 150, 0.4); + --sideBarBgColor: #00242e; + --sideBarItemHoverBgColor: rgba(131, 148, 150, 0.03); + --itemBgColor: #073642; + + --floatFontColor: rgba(131, 148, 150, 0.7); + --floatBgColor: #073642; + --floatHoverColor: rgba(131, 148, 150, 0.04); + --floatBorderColor: rgba(0, 0, 0, 0.05); + --floatShadow: rgba(0, 0, 0, 0.2); + --maskColor: rgba(0, 0, 0, 0.7); + --editorAreaWidth: 750px; + + /* @muyajs/core (kebab-case) editor variables + Additive prep for the muyajs -> @muyajs/core editor swap. These + mirror the camelCase vars above; the legacy editor ignores them. + Do NOT remove the camelCase vars — they still drive the current + editor until editor.vue switches engines. */ + --theme-color: var(--themeColor); + --highlight-color: var(--highlightColor); + --selection-color: var(--selectionColor); + --editor-color: var(--editorColor); + --editor-color-80: var(--editorColor80); + --editor-color-50: var(--editorColor50); + --editor-color-30: var(--editorColor30); + --editor-color-10: var(--editorColor10); + --editor-color-04: var(--editorColor04); + --editor-bg-color: var(--editorBgColor); + --editor-area-width: var(--editorAreaWidth); + --delete-color: var(--deleteColor); + --icon-color: var(--iconColor); + --code-block-bg-color: var(--codeBlockBgColor); + --table-border-color: var(--tableBorderColor); + --input-bg-color: var(--inputBgColor); + --button-font-color: var(--buttonFontColor); + --button-bg-color: var(--buttonBgColor); + --button-border: var(--buttonBorder); + --button-bg-color-hover: var(--buttonBgColorHover); + --button-border-hover: var(--buttonBorderHover); + --button-bg-color-active: var(--buttonBgColorActive); + --button-border-active: var(--buttonBorderActive); + --button-border-focus: var(--button-border); + --float-bg-color: var(--floatBgColor); + --float-hover-color: var(--floatHoverColor); + --float-border-color: var(--floatBorderColor); + --float-shadow: rgb(15 15 15 / 3%) 0 0 0 1px, rgb(15 15 15 / 4%) 0 3px 6px, rgb(15 15 15 / 5%) 0 9px 24px; + --link-color: var(--linkColor); + --h1-color: var(--h1Color); + --h2-color: var(--h2Color); + --h3-color: var(--h3Color); + --h4-color: var(--h4Color); + --h5-color: var(--h5Color); + --h6-color: var(--h6Color); + --blockquote-text-color: var(--blockquoteTextColor); + --blockquote-border-color: var(--blockquoteBorderColor); + --strong-color: var(--strongColor); + --em-color: var(--emColor); + --list-marker-color: var(--listMarkerColor); + --hr-color: var(--hrColor); +} + +::-webkit-scrollbar { + background: var(--editorBgColor); +} + +.mu-front-menu, +.mu-float-wrapper { + box-shadow: 0 4px 8px 0 var(--floatShadow) !important; +} + +.title-bar .frameless-titlebar-button > div > svg { + fill: #839496; +} +.title-bar .frameless-titlebar-minimize:hover, +.title-bar .frameless-titlebar-toggle:hover { + background-color: rgba(131, 148, 150, 0.05); +} + +.side-bar { + border-right: 1px solid #00171d !important; +} + +.recent-files-projects a, +.open-project a { + box-shadow: none !important; +} + +.editor-tabs { + box-shadow: none !important; +} +.editor-tabs:after { + position: absolute; + content: ''; + border-bottom: 1px solid #00171d; + bottom: 0; + left: 0; + right: 0; + z-index: 1; +} +.editor-tabs ul.tabs-container:after { + position: absolute; + content: ''; + border-bottom: 1px solid #00171d; + bottom: 0; + left: 0; + right: 0; + z-index: 2; +} + +.tabs-container > li, +.tabs-container > li.active { + background: var(--editorBgColor) !important; +} + +.open-project button, +.recent-files-projects button { + box-shadow: none !important; +} + +/* ------------------------------------ */ + +:not(pre) > code[class*='language-'], +pre:not(.CodeMirror-line), +pre[class*='language-'], +pre.mu-code-block { + background: var(--codeBlockBgColor) !important; + border: none !important; +} +.mu-thematic-break:not(.mu-active)::before { + border-top: 2px dashed var(--editorColor10) !important; + background: none !important; +} +figure.mu-active.mu-math-block > div.mu-math-preview, +figure.mu-active.mu-diagram-block > div.mu-diagram-preview { + box-shadow: 0 3px 8px 0 var(--floatShadow) !important; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/solarized-light.theme.css b/packages/desktop/src/renderer/src/assets/themes/solarized-light.theme.css new file mode 100644 index 0000000000..3fd7cdfeae --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/solarized-light.theme.css @@ -0,0 +1,214 @@ +:root { + /*editor - Solarized Light theme*/ + --themeColor: #268bd2; + --themeColor90: rgba(38, 139, 210, 0.9); + --themeColor80: rgba(38, 139, 210, 0.8); + --themeColor70: rgba(38, 139, 210, 0.7); + --themeColor60: rgba(38, 139, 210, 0.6); + --themeColor50: rgba(38, 139, 210, 0.5); + --themeColor40: rgba(38, 139, 210, 0.4); + --themeColor30: rgba(38, 139, 210, 0.3); + --themeColor20: rgba(38, 139, 210, 0.2); + --themeColor10: rgba(38, 139, 210, 0.1); + + --highlightColor: rgba(38, 139, 210, 0.3); + --selectionColor: rgba(238, 232, 213, 0.8); + --editorColor: #657b83; + --editorColor80: rgba(101, 123, 131, 0.8); + --editorColor60: rgba(101, 123, 131, 0.6); + --editorColor50: rgba(101, 123, 131, 0.5); + --editorColor40: rgba(101, 123, 131, 0.4); + --editorColor30: rgba(101, 123, 131, 0.3); + --editorColor10: rgba(101, 123, 131, 0.1); + --editorColor04: rgba(101, 123, 131, 0.04); + --editorBgColor: #fdf6e3; + --deleteColor: #dc322f; + --iconColor: rgba(101, 123, 131, 0.56); + --codeBgColor: #eee8d5; + --codeBlockBgColor: #eee8d5; + --footnoteBgColor: rgba(238, 232, 213, 0.5); + --inputBgColor: #f5efdc; + + --focusColor: var(--themeColor); + + --buttonFontColor: rgba(101, 123, 131, 0.7); + --buttonBgColor: #eee8d5; + --buttonBorder: 1px solid rgba(0, 0, 0, 0.08); + --buttonShadow: none; + --buttonFontColorHover: var(--buttonFontColor); + --buttonBgColorHover: #e5dfcc; + --buttonBorderHover: 1px solid rgba(0, 0, 0, 0.12); + --buttonFontColorActive: var(--buttonFontColor); + --buttonBgColorActive: #dcd6c3; + --buttonBorderActive: var(--buttonBorder); + + --buttonPrimaryFontColor: #fdf6e3; + --buttonPrimaryBgColor: var(--themeColor); + --buttonPrimaryBorder: none; + --buttonPrimaryShadow: none; + --buttonPrimaryFontColorHover: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorHover: #2aa198; + --buttonPrimaryBorderHover: var(--buttonPrimaryBorder); + --buttonPrimaryFontColorActive: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorActive: #1a6fb0; + --buttonPrimaryBorderActive: var(--buttonPrimaryBorder); + --buttonPrimaryFocusBorder: none; + --buttonPrimaryFocusShadow: inset 0 0 0 1px rgba(255, 255, 255, 0.5), 0 0 0 1px var(--themeColor); + --tableBorderColor: #dcd6c3; + + /* Markdown element colors */ + --headingColor: #586e75; + --h1Color: #dc322f; + --h2Color: #cb4b16; + --h3Color: #b58900; + --h4Color: #859900; + --h5Color: #2aa198; + --h6Color: #268bd2; + --blockquoteTextColor: rgba(101, 123, 131, 0.6); + --blockquoteBorderColor: #93a1a1; + --hrColor: #dcd6c3; + --linkColor: #268bd2; + --strongColor: #586e75; + --emColor: #b58900; + --listMarkerColor: #cb4b16; + + /*marktext*/ + --sideBarColor: rgba(101, 123, 131, 0.7); + --sideBarIconColor: var(--iconColor); + --sideBarTitleColor: rgba(101, 123, 131, 0.9); + --sideBarTextColor: rgba(101, 123, 131, 0.5); + --sideBarBgColor: #f5efdc; + --sideBarItemHoverBgColor: rgba(101, 123, 131, 0.05); + --itemBgColor: #e5dfcc; + + --floatFontColor: rgba(101, 123, 131, 0.8); + --floatBgColor: #eee8d5; + --floatHoverColor: rgba(101, 123, 131, 0.06); + --floatBorderColor: rgba(0, 0, 0, 0.08); + --floatShadow: rgba(0, 0, 0, 0.1); + --maskColor: rgba(0, 0, 0, 0.3); + --editorAreaWidth: 750px; + + /* @muyajs/core (kebab-case) editor variables + Additive prep for the muyajs -> @muyajs/core editor swap. These + mirror the camelCase vars above; the legacy editor ignores them. + Do NOT remove the camelCase vars — they still drive the current + editor until editor.vue switches engines. */ + --theme-color: var(--themeColor); + --highlight-color: var(--highlightColor); + --selection-color: var(--selectionColor); + --editor-color: var(--editorColor); + --editor-color-80: var(--editorColor80); + --editor-color-50: var(--editorColor50); + --editor-color-30: var(--editorColor30); + --editor-color-10: var(--editorColor10); + --editor-color-04: var(--editorColor04); + --editor-bg-color: var(--editorBgColor); + --editor-area-width: var(--editorAreaWidth); + --delete-color: var(--deleteColor); + --icon-color: var(--iconColor); + --code-block-bg-color: var(--codeBlockBgColor); + --table-border-color: var(--tableBorderColor); + --input-bg-color: var(--inputBgColor); + --button-font-color: var(--buttonFontColor); + --button-bg-color: var(--buttonBgColor); + --button-border: var(--buttonBorder); + --button-bg-color-hover: var(--buttonBgColorHover); + --button-border-hover: var(--buttonBorderHover); + --button-bg-color-active: var(--buttonBgColorActive); + --button-border-active: var(--buttonBorderActive); + --button-border-focus: var(--button-border); + --float-bg-color: var(--floatBgColor); + --float-hover-color: var(--floatHoverColor); + --float-border-color: var(--floatBorderColor); + --float-shadow: rgb(15 15 15 / 3%) 0 0 0 1px, rgb(15 15 15 / 4%) 0 3px 6px, rgb(15 15 15 / 5%) 0 9px 24px; + --link-color: var(--linkColor); + --h1-color: var(--h1Color); + --h2-color: var(--h2Color); + --h3-color: var(--h3Color); + --h4-color: var(--h4Color); + --h5-color: var(--h5Color); + --h6-color: var(--h6Color); + --blockquote-text-color: var(--blockquoteTextColor); + --blockquote-border-color: var(--blockquoteBorderColor); + --strong-color: var(--strongColor); + --em-color: var(--emColor); + --list-marker-color: var(--listMarkerColor); + --hr-color: var(--hrColor); +} + +::-webkit-scrollbar { + background: var(--editorBgColor); +} + +.mu-front-menu, +.mu-float-wrapper { + box-shadow: 0 4px 8px 0 var(--floatShadow) !important; +} + +.title-bar .frameless-titlebar-button > div > svg { + fill: #657b83; +} +.title-bar .frameless-titlebar-minimize:hover, +.title-bar .frameless-titlebar-toggle:hover { + background-color: rgba(101, 123, 131, 0.08); +} + +.side-bar { + border-right: 1px solid #dcd6c3 !important; +} + +.recent-files-projects a, +.open-project a { + box-shadow: none !important; +} + +.editor-tabs { + box-shadow: none !important; +} +.editor-tabs:after { + position: absolute; + content: ''; + border-bottom: 1px solid #dcd6c3; + bottom: 0; + left: 0; + right: 0; + z-index: 1; +} +.editor-tabs ul.tabs-container:after { + position: absolute; + content: ''; + border-bottom: 1px solid #dcd6c3; + bottom: 0; + left: 0; + right: 0; + z-index: 2; +} + +.tabs-container > li, +.tabs-container > li.active { + background: var(--editorBgColor) !important; +} + +.open-project button, +.recent-files-projects button { + box-shadow: none !important; +} + +/* ------------------------------------ */ + +:not(pre) > code[class*='language-'], +pre:not(.CodeMirror-line), +pre[class*='language-'], +pre.mu-code-block { + background: var(--codeBlockBgColor) !important; + border: none !important; +} +.mu-thematic-break:not(.mu-active)::before { + border-top: 2px dashed var(--editorColor10) !important; + background: none !important; +} +figure.mu-active.mu-math-block > div.mu-math-preview, +figure.mu-active.mu-diagram-block > div.mu-diagram-preview { + box-shadow: 0 3px 8px 0 var(--floatShadow) !important; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/synthwave-84.theme.css b/packages/desktop/src/renderer/src/assets/themes/synthwave-84.theme.css new file mode 100644 index 0000000000..ff0f71c8ae --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/synthwave-84.theme.css @@ -0,0 +1,214 @@ +:root { + /*editor - Synthwave 84 theme*/ + --themeColor: #ff7edb; + --themeColor90: rgba(255, 126, 219, 0.9); + --themeColor80: rgba(255, 126, 219, 0.8); + --themeColor70: rgba(255, 126, 219, 0.7); + --themeColor60: rgba(255, 126, 219, 0.6); + --themeColor50: rgba(255, 126, 219, 0.5); + --themeColor40: rgba(255, 126, 219, 0.4); + --themeColor30: rgba(255, 126, 219, 0.3); + --themeColor20: rgba(255, 126, 219, 0.2); + --themeColor10: rgba(255, 126, 219, 0.1); + + --highlightColor: rgba(255, 126, 219, 0.6); + --selectionColor: rgba(52, 41, 72, 0.8); + --editorColor: #ffffff; + --editorColor80: rgba(255, 255, 255, 0.8); + --editorColor60: rgba(255, 255, 255, 0.6); + --editorColor50: rgba(255, 255, 255, 0.5); + --editorColor40: rgba(255, 255, 255, 0.4); + --editorColor30: rgba(255, 255, 255, 0.3); + --editorColor10: rgba(255, 255, 255, 0.1); + --editorColor04: rgba(255, 255, 255, 0.04); + --editorBgColor: #262335; + --deleteColor: #fe4450; + --iconColor: rgba(255, 255, 255, 0.56); + --codeBgColor: #34294a; + --codeBlockBgColor: #34294a; + --footnoteBgColor: rgba(52, 41, 74, 0.3); + --inputBgColor: #1e1a29; + + --focusColor: var(--themeColor); + + --buttonFontColor: rgba(255, 255, 255, 0.6); + --buttonBgColor: #34294a; + --buttonBorder: 1px solid rgba(0, 0, 0, 0.2); + --buttonShadow: none; + --buttonFontColorHover: var(--buttonFontColor); + --buttonBgColorHover: #463860; + --buttonBorderHover: 1px solid rgba(0, 0, 0, 0.3); + --buttonFontColorActive: var(--buttonFontColor); + --buttonBgColorActive: #2b223d; + --buttonBorderActive: var(--buttonBorder); + + --buttonPrimaryFontColor: #262335; + --buttonPrimaryBgColor: var(--themeColor); + --buttonPrimaryBorder: none; + --buttonPrimaryShadow: none; + --buttonPrimaryFontColorHover: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorHover: #72f1b8; + --buttonPrimaryBorderHover: var(--buttonPrimaryBorder); + --buttonPrimaryFontColorActive: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorActive: #e660c2; + --buttonPrimaryBorderActive: var(--buttonPrimaryBorder); + --buttonPrimaryFocusBorder: none; + --buttonPrimaryFocusShadow: inset 0 0 0 1px rgba(24, 26, 31, 0.5), 0 0 0 1px var(--themeColor); + --tableBorderColor: #34294a; + + /* Markdown element colors */ + --headingColor: #ffffff; + --h1Color: #ff7edb; + --h2Color: #36f9f6; + --h3Color: #fede5d; + --h4Color: #72f1b8; + --h5Color: #f97e72; + --h6Color: #b084eb; + --blockquoteTextColor: rgba(255, 255, 255, 0.6); + --blockquoteBorderColor: #614d85; + --hrColor: #463860; + --linkColor: #36f9f6; + --strongColor: #ffffff; + --emColor: #ff7edb; + --listMarkerColor: #fede5d; + + /*marktext*/ + --sideBarColor: rgba(255, 255, 255, 0.6); + --sideBarIconColor: var(--iconColor); + --sideBarTitleColor: rgba(255, 255, 255, 0.8); + --sideBarTextColor: rgba(255, 255, 255, 0.4); + --sideBarBgColor: #221e2f; + --sideBarItemHoverBgColor: rgba(255, 255, 255, 0.03); + --itemBgColor: #34294a; + + --floatFontColor: rgba(255, 255, 255, 0.7); + --floatBgColor: #34294a; + --floatHoverColor: rgba(255, 255, 255, 0.04); + --floatBorderColor: rgba(0, 0, 0, 0.05); + --floatShadow: rgba(0, 0, 0, 0.2); + --maskColor: rgba(0, 0, 0, 0.7); + --editorAreaWidth: 750px; + + /* @muyajs/core (kebab-case) editor variables + Additive prep for the muyajs -> @muyajs/core editor swap. These + mirror the camelCase vars above; the legacy editor ignores them. + Do NOT remove the camelCase vars — they still drive the current + editor until editor.vue switches engines. */ + --theme-color: var(--themeColor); + --highlight-color: var(--highlightColor); + --selection-color: var(--selectionColor); + --editor-color: var(--editorColor); + --editor-color-80: var(--editorColor80); + --editor-color-50: var(--editorColor50); + --editor-color-30: var(--editorColor30); + --editor-color-10: var(--editorColor10); + --editor-color-04: var(--editorColor04); + --editor-bg-color: var(--editorBgColor); + --editor-area-width: var(--editorAreaWidth); + --delete-color: var(--deleteColor); + --icon-color: var(--iconColor); + --code-block-bg-color: var(--codeBlockBgColor); + --table-border-color: var(--tableBorderColor); + --input-bg-color: var(--inputBgColor); + --button-font-color: var(--buttonFontColor); + --button-bg-color: var(--buttonBgColor); + --button-border: var(--buttonBorder); + --button-bg-color-hover: var(--buttonBgColorHover); + --button-border-hover: var(--buttonBorderHover); + --button-bg-color-active: var(--buttonBgColorActive); + --button-border-active: var(--buttonBorderActive); + --button-border-focus: var(--button-border); + --float-bg-color: var(--floatBgColor); + --float-hover-color: var(--floatHoverColor); + --float-border-color: var(--floatBorderColor); + --float-shadow: rgb(15 15 15 / 3%) 0 0 0 1px, rgb(15 15 15 / 4%) 0 3px 6px, rgb(15 15 15 / 5%) 0 9px 24px; + --link-color: var(--linkColor); + --h1-color: var(--h1Color); + --h2-color: var(--h2Color); + --h3-color: var(--h3Color); + --h4-color: var(--h4Color); + --h5-color: var(--h5Color); + --h6-color: var(--h6Color); + --blockquote-text-color: var(--blockquoteTextColor); + --blockquote-border-color: var(--blockquoteBorderColor); + --strong-color: var(--strongColor); + --em-color: var(--emColor); + --list-marker-color: var(--listMarkerColor); + --hr-color: var(--hrColor); +} + +::-webkit-scrollbar { + background: var(--editorBgColor); +} + +.mu-front-menu, +.mu-float-wrapper { + box-shadow: 0 4px 8px 0 var(--floatShadow) !important; +} + +.title-bar .frameless-titlebar-button > div > svg { + fill: #ffffff; +} +.title-bar .frameless-titlebar-minimize:hover, +.title-bar .frameless-titlebar-toggle:hover { + background-color: rgba(255, 255, 255, 0.05); +} + +.side-bar { + border-right: 1px solid #181521 !important; +} + +.recent-files-projects a, +.open-project a { + box-shadow: none !important; +} + +.editor-tabs { + box-shadow: none !important; +} +.editor-tabs:after { + position: absolute; + content: ''; + border-bottom: 1px solid #181521; + bottom: 0; + left: 0; + right: 0; + z-index: 1; +} +.editor-tabs ul.tabs-container:after { + position: absolute; + content: ''; + border-bottom: 1px solid #181521; + bottom: 0; + left: 0; + right: 0; + z-index: 2; +} + +.tabs-container > li, +.tabs-container > li.active { + background: var(--editorBgColor) !important; +} + +.open-project button, +.recent-files-projects button { + box-shadow: none !important; +} + +/* ------------------------------------ */ + +:not(pre) > code[class*='language-'], +pre:not(.CodeMirror-line), +pre[class*='language-'], +pre.mu-code-block { + background: var(--codeBlockBgColor) !important; + border: none !important; +} +.mu-thematic-break:not(.mu-active)::before { + border-top: 2px dashed var(--editorColor10) !important; + background: none !important; +} +figure.mu-active.mu-math-block > div.mu-math-preview, +figure.mu-active.mu-diagram-block > div.mu-diagram-preview { + box-shadow: 0 3px 8px 0 var(--floatShadow) !important; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/tokyo-night-light.theme.css b/packages/desktop/src/renderer/src/assets/themes/tokyo-night-light.theme.css new file mode 100644 index 0000000000..cf6d007efe --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/tokyo-night-light.theme.css @@ -0,0 +1,214 @@ +:root { + /*editor - Tokyo Night Light theme*/ + --themeColor: #34548a; + --themeColor90: rgba(52, 84, 138, 0.9); + --themeColor80: rgba(52, 84, 138, 0.8); + --themeColor70: rgba(52, 84, 138, 0.7); + --themeColor60: rgba(52, 84, 138, 0.6); + --themeColor50: rgba(52, 84, 138, 0.5); + --themeColor40: rgba(52, 84, 138, 0.4); + --themeColor30: rgba(52, 84, 138, 0.3); + --themeColor20: rgba(52, 84, 138, 0.2); + --themeColor10: rgba(52, 84, 138, 0.1); + + --highlightColor: rgba(52, 84, 138, 0.3); + --selectionColor: rgba(157, 170, 200, 0.4); + --editorColor: #343b58; + --editorColor80: rgba(52, 59, 88, 0.8); + --editorColor60: rgba(52, 59, 88, 0.6); + --editorColor50: rgba(52, 59, 88, 0.5); + --editorColor40: rgba(52, 59, 88, 0.4); + --editorColor30: rgba(52, 59, 88, 0.3); + --editorColor10: rgba(52, 59, 88, 0.1); + --editorColor04: rgba(52, 59, 88, 0.04); + --editorBgColor: #d5d6db; + --deleteColor: #8c4351; + --iconColor: rgba(52, 59, 88, 0.56); + --codeBgColor: #c8c9ce; + --codeBlockBgColor: #c8c9ce; + --footnoteBgColor: rgba(200, 201, 206, 0.5); + --inputBgColor: #cbccd1; + + --focusColor: var(--themeColor); + + --buttonFontColor: rgba(52, 59, 88, 0.7); + --buttonBgColor: #c8c9ce; + --buttonBorder: 1px solid rgba(0, 0, 0, 0.08); + --buttonShadow: none; + --buttonFontColorHover: var(--buttonFontColor); + --buttonBgColorHover: #bbbcc1; + --buttonBorderHover: 1px solid rgba(0, 0, 0, 0.12); + --buttonFontColorActive: var(--buttonFontColor); + --buttonBgColorActive: #b0b1b7; + --buttonBorderActive: var(--buttonBorder); + + --buttonPrimaryFontColor: #ffffff; + --buttonPrimaryBgColor: var(--themeColor); + --buttonPrimaryBorder: none; + --buttonPrimaryShadow: none; + --buttonPrimaryFontColorHover: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorHover: #5a7aad; + --buttonPrimaryBorderHover: var(--buttonPrimaryBorder); + --buttonPrimaryFontColorActive: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorActive: #2a4270; + --buttonPrimaryBorderActive: var(--buttonPrimaryBorder); + --buttonPrimaryFocusBorder: none; + --buttonPrimaryFocusShadow: inset 0 0 0 1px rgba(255, 255, 255, 0.5), 0 0 0 1px var(--themeColor); + --tableBorderColor: #b0b1b7; + + /* Markdown element colors */ + --headingColor: #343b58; + --h1Color: #965027; + --h2Color: #34548a; + --h3Color: #33635c; + --h4Color: #166775; + --h5Color: #5a4a78; + --h6Color: #8c4351; + --blockquoteTextColor: rgba(52, 59, 88, 0.6); + --blockquoteBorderColor: #9699a3; + --hrColor: #b0b1b7; + --linkColor: #34548a; + --strongColor: #343b58; + --emColor: #5a4a78; + --listMarkerColor: #965027; + + /*marktext*/ + --sideBarColor: rgba(52, 59, 88, 0.7); + --sideBarIconColor: var(--iconColor); + --sideBarTitleColor: rgba(52, 59, 88, 0.9); + --sideBarTextColor: rgba(52, 59, 88, 0.5); + --sideBarBgColor: #d0d1d6; + --sideBarItemHoverBgColor: rgba(52, 59, 88, 0.05); + --itemBgColor: #c8c9ce; + + --floatFontColor: rgba(52, 59, 88, 0.8); + --floatBgColor: #c8c9ce; + --floatHoverColor: rgba(52, 59, 88, 0.06); + --floatBorderColor: rgba(0, 0, 0, 0.08); + --floatShadow: rgba(0, 0, 0, 0.1); + --maskColor: rgba(0, 0, 0, 0.3); + --editorAreaWidth: 750px; + + /* @muyajs/core (kebab-case) editor variables + Additive prep for the muyajs -> @muyajs/core editor swap. These + mirror the camelCase vars above; the legacy editor ignores them. + Do NOT remove the camelCase vars — they still drive the current + editor until editor.vue switches engines. */ + --theme-color: var(--themeColor); + --highlight-color: var(--highlightColor); + --selection-color: var(--selectionColor); + --editor-color: var(--editorColor); + --editor-color-80: var(--editorColor80); + --editor-color-50: var(--editorColor50); + --editor-color-30: var(--editorColor30); + --editor-color-10: var(--editorColor10); + --editor-color-04: var(--editorColor04); + --editor-bg-color: var(--editorBgColor); + --editor-area-width: var(--editorAreaWidth); + --delete-color: var(--deleteColor); + --icon-color: var(--iconColor); + --code-block-bg-color: var(--codeBlockBgColor); + --table-border-color: var(--tableBorderColor); + --input-bg-color: var(--inputBgColor); + --button-font-color: var(--buttonFontColor); + --button-bg-color: var(--buttonBgColor); + --button-border: var(--buttonBorder); + --button-bg-color-hover: var(--buttonBgColorHover); + --button-border-hover: var(--buttonBorderHover); + --button-bg-color-active: var(--buttonBgColorActive); + --button-border-active: var(--buttonBorderActive); + --button-border-focus: var(--button-border); + --float-bg-color: var(--floatBgColor); + --float-hover-color: var(--floatHoverColor); + --float-border-color: var(--floatBorderColor); + --float-shadow: rgb(15 15 15 / 3%) 0 0 0 1px, rgb(15 15 15 / 4%) 0 3px 6px, rgb(15 15 15 / 5%) 0 9px 24px; + --link-color: var(--linkColor); + --h1-color: var(--h1Color); + --h2-color: var(--h2Color); + --h3-color: var(--h3Color); + --h4-color: var(--h4Color); + --h5-color: var(--h5Color); + --h6-color: var(--h6Color); + --blockquote-text-color: var(--blockquoteTextColor); + --blockquote-border-color: var(--blockquoteBorderColor); + --strong-color: var(--strongColor); + --em-color: var(--emColor); + --list-marker-color: var(--listMarkerColor); + --hr-color: var(--hrColor); +} + +::-webkit-scrollbar { + background: var(--editorBgColor); +} + +.mu-front-menu, +.mu-float-wrapper { + box-shadow: 0 4px 8px 0 var(--floatShadow) !important; +} + +.title-bar .frameless-titlebar-button > div > svg { + fill: #343b58; +} +.title-bar .frameless-titlebar-minimize:hover, +.title-bar .frameless-titlebar-toggle:hover { + background-color: rgba(52, 59, 88, 0.08); +} + +.side-bar { + border-right: 1px solid #b0b1b7 !important; +} + +.recent-files-projects a, +.open-project a { + box-shadow: none !important; +} + +.editor-tabs { + box-shadow: none !important; +} +.editor-tabs:after { + position: absolute; + content: ''; + border-bottom: 1px solid #b0b1b7; + bottom: 0; + left: 0; + right: 0; + z-index: 1; +} +.editor-tabs ul.tabs-container:after { + position: absolute; + content: ''; + border-bottom: 1px solid #b0b1b7; + bottom: 0; + left: 0; + right: 0; + z-index: 2; +} + +.tabs-container > li, +.tabs-container > li.active { + background: var(--editorBgColor) !important; +} + +.open-project button, +.recent-files-projects button { + box-shadow: none !important; +} + +/* ------------------------------------ */ + +:not(pre) > code[class*='language-'], +pre:not(.CodeMirror-line), +pre[class*='language-'], +pre.mu-code-block { + background: var(--codeBlockBgColor) !important; + border: none !important; +} +.mu-thematic-break:not(.mu-active)::before { + border-top: 2px dashed var(--editorColor10) !important; + background: none !important; +} +figure.mu-active.mu-math-block > div.mu-math-preview, +figure.mu-active.mu-diagram-block > div.mu-diagram-preview { + box-shadow: 0 3px 8px 0 var(--floatShadow) !important; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/tokyo-night-storm.theme.css b/packages/desktop/src/renderer/src/assets/themes/tokyo-night-storm.theme.css new file mode 100644 index 0000000000..cf02da5870 --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/tokyo-night-storm.theme.css @@ -0,0 +1,214 @@ +:root { + /*editor - Tokyo Night Storm theme*/ + --themeColor: #7aa2f7; + --themeColor90: rgba(122, 162, 247, 0.9); + --themeColor80: rgba(122, 162, 247, 0.8); + --themeColor70: rgba(122, 162, 247, 0.7); + --themeColor60: rgba(122, 162, 247, 0.6); + --themeColor50: rgba(122, 162, 247, 0.5); + --themeColor40: rgba(122, 162, 247, 0.4); + --themeColor30: rgba(122, 162, 247, 0.3); + --themeColor20: rgba(122, 162, 247, 0.2); + --themeColor10: rgba(122, 162, 247, 0.1); + + --highlightColor: rgba(122, 162, 247, 0.6); + --selectionColor: rgba(52, 59, 88, 0.8); + --editorColor: #c0caf5; + --editorColor80: rgba(192, 202, 245, 0.8); + --editorColor60: rgba(192, 202, 245, 0.6); + --editorColor50: rgba(192, 202, 245, 0.5); + --editorColor40: rgba(192, 202, 245, 0.4); + --editorColor30: rgba(192, 202, 245, 0.3); + --editorColor10: rgba(192, 202, 245, 0.1); + --editorColor04: rgba(192, 202, 245, 0.04); + --editorBgColor: #24283b; + --deleteColor: #f7768e; + --iconColor: rgba(192, 202, 245, 0.56); + --codeBgColor: #1f2335; + --codeBlockBgColor: #1f2335; + --footnoteBgColor: rgba(31, 35, 53, 0.3); + --inputBgColor: #1a1b26; + + --focusColor: var(--themeColor); + + --buttonFontColor: rgba(192, 202, 245, 0.6); + --buttonBgColor: #1f2335; + --buttonBorder: 1px solid rgba(0, 0, 0, 0.2); + --buttonShadow: none; + --buttonFontColorHover: var(--buttonFontColor); + --buttonBgColorHover: #343b58; + --buttonBorderHover: 1px solid rgba(0, 0, 0, 0.3); + --buttonFontColorActive: var(--buttonFontColor); + --buttonBgColorActive: #1a1e30; + --buttonBorderActive: var(--buttonBorder); + + --buttonPrimaryFontColor: #1a1b26; + --buttonPrimaryBgColor: var(--themeColor); + --buttonPrimaryBorder: none; + --buttonPrimaryShadow: none; + --buttonPrimaryFontColorHover: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorHover: #89b4fa; + --buttonPrimaryBorderHover: var(--buttonPrimaryBorder); + --buttonPrimaryFontColorActive: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorActive: #5d7bd5; + --buttonPrimaryBorderActive: var(--buttonPrimaryBorder); + --buttonPrimaryFocusBorder: none; + --buttonPrimaryFocusShadow: inset 0 0 0 1px rgba(24, 26, 31, 0.5), 0 0 0 1px var(--themeColor); + --tableBorderColor: #1f2335; + + /* Markdown element colors */ + --headingColor: #c0caf5; + --h1Color: #bb9af7; + --h2Color: #7aa2f7; + --h3Color: #7dcfff; + --h4Color: #9ece6a; + --h5Color: #e0af68; + --h6Color: #f7768e; + --blockquoteTextColor: rgba(192, 202, 245, 0.6); + --blockquoteBorderColor: #414868; + --hrColor: #3b4261; + --linkColor: #7dcfff; + --strongColor: #c0caf5; + --emColor: #bb9af7; + --listMarkerColor: #7aa2f7; + + /*marktext*/ + --sideBarColor: rgba(192, 202, 245, 0.6); + --sideBarIconColor: var(--iconColor); + --sideBarTitleColor: rgba(192, 202, 245, 0.8); + --sideBarTextColor: rgba(192, 202, 245, 0.4); + --sideBarBgColor: #1f2130; + --sideBarItemHoverBgColor: rgba(192, 202, 245, 0.03); + --itemBgColor: #1f2335; + + --floatFontColor: rgba(192, 202, 245, 0.7); + --floatBgColor: #1f2335; + --floatHoverColor: rgba(192, 202, 245, 0.04); + --floatBorderColor: rgba(0, 0, 0, 0.05); + --floatShadow: rgba(0, 0, 0, 0.2); + --maskColor: rgba(0, 0, 0, 0.7); + --editorAreaWidth: 750px; + + /* @muyajs/core (kebab-case) editor variables + Additive prep for the muyajs -> @muyajs/core editor swap. These + mirror the camelCase vars above; the legacy editor ignores them. + Do NOT remove the camelCase vars — they still drive the current + editor until editor.vue switches engines. */ + --theme-color: var(--themeColor); + --highlight-color: var(--highlightColor); + --selection-color: var(--selectionColor); + --editor-color: var(--editorColor); + --editor-color-80: var(--editorColor80); + --editor-color-50: var(--editorColor50); + --editor-color-30: var(--editorColor30); + --editor-color-10: var(--editorColor10); + --editor-color-04: var(--editorColor04); + --editor-bg-color: var(--editorBgColor); + --editor-area-width: var(--editorAreaWidth); + --delete-color: var(--deleteColor); + --icon-color: var(--iconColor); + --code-block-bg-color: var(--codeBlockBgColor); + --table-border-color: var(--tableBorderColor); + --input-bg-color: var(--inputBgColor); + --button-font-color: var(--buttonFontColor); + --button-bg-color: var(--buttonBgColor); + --button-border: var(--buttonBorder); + --button-bg-color-hover: var(--buttonBgColorHover); + --button-border-hover: var(--buttonBorderHover); + --button-bg-color-active: var(--buttonBgColorActive); + --button-border-active: var(--buttonBorderActive); + --button-border-focus: var(--button-border); + --float-bg-color: var(--floatBgColor); + --float-hover-color: var(--floatHoverColor); + --float-border-color: var(--floatBorderColor); + --float-shadow: rgb(15 15 15 / 3%) 0 0 0 1px, rgb(15 15 15 / 4%) 0 3px 6px, rgb(15 15 15 / 5%) 0 9px 24px; + --link-color: var(--linkColor); + --h1-color: var(--h1Color); + --h2-color: var(--h2Color); + --h3-color: var(--h3Color); + --h4-color: var(--h4Color); + --h5-color: var(--h5Color); + --h6-color: var(--h6Color); + --blockquote-text-color: var(--blockquoteTextColor); + --blockquote-border-color: var(--blockquoteBorderColor); + --strong-color: var(--strongColor); + --em-color: var(--emColor); + --list-marker-color: var(--listMarkerColor); + --hr-color: var(--hrColor); +} + +::-webkit-scrollbar { + background: var(--editorBgColor); +} + +.mu-front-menu, +.mu-float-wrapper { + box-shadow: 0 4px 8px 0 var(--floatShadow) !important; +} + +.title-bar .frameless-titlebar-button > div > svg { + fill: #c0caf5; +} +.title-bar .frameless-titlebar-minimize:hover, +.title-bar .frameless-titlebar-toggle:hover { + background-color: rgba(192, 202, 245, 0.05); +} + +.side-bar { + border-right: 1px solid #16171f !important; +} + +.recent-files-projects a, +.open-project a { + box-shadow: none !important; +} + +.editor-tabs { + box-shadow: none !important; +} +.editor-tabs:after { + position: absolute; + content: ''; + border-bottom: 1px solid #16171f; + bottom: 0; + left: 0; + right: 0; + z-index: 1; +} +.editor-tabs ul.tabs-container:after { + position: absolute; + content: ''; + border-bottom: 1px solid #16171f; + bottom: 0; + left: 0; + right: 0; + z-index: 2; +} + +.tabs-container > li, +.tabs-container > li.active { + background: var(--editorBgColor) !important; +} + +.open-project button, +.recent-files-projects button { + box-shadow: none !important; +} + +/* ------------------------------------ */ + +:not(pre) > code[class*='language-'], +pre:not(.CodeMirror-line), +pre[class*='language-'], +pre.mu-code-block { + background: var(--codeBlockBgColor) !important; + border: none !important; +} +.mu-thematic-break:not(.mu-active)::before { + border-top: 2px dashed var(--editorColor10) !important; + background: none !important; +} +figure.mu-active.mu-math-block > div.mu-math-preview, +figure.mu-active.mu-diagram-block > div.mu-diagram-preview { + box-shadow: 0 3px 8px 0 var(--floatShadow) !important; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/tokyo-night.theme.css b/packages/desktop/src/renderer/src/assets/themes/tokyo-night.theme.css new file mode 100644 index 0000000000..c958f611c5 --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/tokyo-night.theme.css @@ -0,0 +1,214 @@ +:root { + /*editor - Tokyo Night theme*/ + --themeColor: #7aa2f7; + --themeColor90: rgba(122, 162, 247, 0.9); + --themeColor80: rgba(122, 162, 247, 0.8); + --themeColor70: rgba(122, 162, 247, 0.7); + --themeColor60: rgba(122, 162, 247, 0.6); + --themeColor50: rgba(122, 162, 247, 0.5); + --themeColor40: rgba(122, 162, 247, 0.4); + --themeColor30: rgba(122, 162, 247, 0.3); + --themeColor20: rgba(122, 162, 247, 0.2); + --themeColor10: rgba(122, 162, 247, 0.1); + + --highlightColor: rgba(122, 162, 247, 0.6); + --selectionColor: rgba(41, 46, 66, 0.8); + --editorColor: #c0caf5; + --editorColor80: rgba(192, 202, 245, 0.8); + --editorColor60: rgba(192, 202, 245, 0.6); + --editorColor50: rgba(192, 202, 245, 0.5); + --editorColor40: rgba(192, 202, 245, 0.4); + --editorColor30: rgba(192, 202, 245, 0.3); + --editorColor10: rgba(192, 202, 245, 0.1); + --editorColor04: rgba(192, 202, 245, 0.04); + --editorBgColor: #1a1b26; + --deleteColor: #f7768e; + --iconColor: rgba(192, 202, 245, 0.56); + --codeBgColor: #24283b; + --codeBlockBgColor: #24283b; + --footnoteBgColor: rgba(36, 40, 59, 0.3); + --inputBgColor: #16161e; + + --focusColor: var(--themeColor); + + --buttonFontColor: rgba(192, 202, 245, 0.6); + --buttonBgColor: #24283b; + --buttonBorder: 1px solid rgba(0, 0, 0, 0.2); + --buttonShadow: none; + --buttonFontColorHover: var(--buttonFontColor); + --buttonBgColorHover: #343b58; + --buttonBorderHover: 1px solid rgba(0, 0, 0, 0.3); + --buttonFontColorActive: var(--buttonFontColor); + --buttonBgColorActive: #1f2335; + --buttonBorderActive: var(--buttonBorder); + + --buttonPrimaryFontColor: #1a1b26; + --buttonPrimaryBgColor: var(--themeColor); + --buttonPrimaryBorder: none; + --buttonPrimaryShadow: none; + --buttonPrimaryFontColorHover: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorHover: #89b4fa; + --buttonPrimaryBorderHover: var(--buttonPrimaryBorder); + --buttonPrimaryFontColorActive: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorActive: #5d7bd5; + --buttonPrimaryBorderActive: var(--buttonPrimaryBorder); + --buttonPrimaryFocusBorder: none; + --buttonPrimaryFocusShadow: inset 0 0 0 1px rgba(24, 26, 31, 0.5), 0 0 0 1px var(--themeColor); + --tableBorderColor: #24283b; + + /* Markdown element colors */ + --headingColor: #c0caf5; + --h1Color: #bb9af7; + --h2Color: #7aa2f7; + --h3Color: #7dcfff; + --h4Color: #9ece6a; + --h5Color: #e0af68; + --h6Color: #f7768e; + --blockquoteTextColor: rgba(192, 202, 245, 0.6); + --blockquoteBorderColor: #414868; + --hrColor: #3b4261; + --linkColor: #7dcfff; + --strongColor: #c0caf5; + --emColor: #bb9af7; + --listMarkerColor: #7aa2f7; + + /*marktext*/ + --sideBarColor: rgba(192, 202, 245, 0.6); + --sideBarIconColor: var(--iconColor); + --sideBarTitleColor: rgba(192, 202, 245, 0.8); + --sideBarTextColor: rgba(192, 202, 245, 0.4); + --sideBarBgColor: #181822; + --sideBarItemHoverBgColor: rgba(192, 202, 245, 0.03); + --itemBgColor: #24283b; + + --floatFontColor: rgba(192, 202, 245, 0.7); + --floatBgColor: #24283b; + --floatHoverColor: rgba(192, 202, 245, 0.04); + --floatBorderColor: rgba(0, 0, 0, 0.05); + --floatShadow: rgba(0, 0, 0, 0.2); + --maskColor: rgba(0, 0, 0, 0.7); + --editorAreaWidth: 750px; + + /* @muyajs/core (kebab-case) editor variables + Additive prep for the muyajs -> @muyajs/core editor swap. These + mirror the camelCase vars above; the legacy editor ignores them. + Do NOT remove the camelCase vars — they still drive the current + editor until editor.vue switches engines. */ + --theme-color: var(--themeColor); + --highlight-color: var(--highlightColor); + --selection-color: var(--selectionColor); + --editor-color: var(--editorColor); + --editor-color-80: var(--editorColor80); + --editor-color-50: var(--editorColor50); + --editor-color-30: var(--editorColor30); + --editor-color-10: var(--editorColor10); + --editor-color-04: var(--editorColor04); + --editor-bg-color: var(--editorBgColor); + --editor-area-width: var(--editorAreaWidth); + --delete-color: var(--deleteColor); + --icon-color: var(--iconColor); + --code-block-bg-color: var(--codeBlockBgColor); + --table-border-color: var(--tableBorderColor); + --input-bg-color: var(--inputBgColor); + --button-font-color: var(--buttonFontColor); + --button-bg-color: var(--buttonBgColor); + --button-border: var(--buttonBorder); + --button-bg-color-hover: var(--buttonBgColorHover); + --button-border-hover: var(--buttonBorderHover); + --button-bg-color-active: var(--buttonBgColorActive); + --button-border-active: var(--buttonBorderActive); + --button-border-focus: var(--button-border); + --float-bg-color: var(--floatBgColor); + --float-hover-color: var(--floatHoverColor); + --float-border-color: var(--floatBorderColor); + --float-shadow: rgb(15 15 15 / 3%) 0 0 0 1px, rgb(15 15 15 / 4%) 0 3px 6px, rgb(15 15 15 / 5%) 0 9px 24px; + --link-color: var(--linkColor); + --h1-color: var(--h1Color); + --h2-color: var(--h2Color); + --h3-color: var(--h3Color); + --h4-color: var(--h4Color); + --h5-color: var(--h5Color); + --h6-color: var(--h6Color); + --blockquote-text-color: var(--blockquoteTextColor); + --blockquote-border-color: var(--blockquoteBorderColor); + --strong-color: var(--strongColor); + --em-color: var(--emColor); + --list-marker-color: var(--listMarkerColor); + --hr-color: var(--hrColor); +} + +::-webkit-scrollbar { + background: var(--editorBgColor); +} + +.mu-front-menu, +.mu-float-wrapper { + box-shadow: 0 4px 8px 0 var(--floatShadow) !important; +} + +.title-bar .frameless-titlebar-button > div > svg { + fill: #c0caf5; +} +.title-bar .frameless-titlebar-minimize:hover, +.title-bar .frameless-titlebar-toggle:hover { + background-color: rgba(192, 202, 245, 0.05); +} + +.side-bar { + border-right: 1px solid #13131a !important; +} + +.recent-files-projects a, +.open-project a { + box-shadow: none !important; +} + +.editor-tabs { + box-shadow: none !important; +} +.editor-tabs:after { + position: absolute; + content: ''; + border-bottom: 1px solid #13131a; + bottom: 0; + left: 0; + right: 0; + z-index: 1; +} +.editor-tabs ul.tabs-container:after { + position: absolute; + content: ''; + border-bottom: 1px solid #13131a; + bottom: 0; + left: 0; + right: 0; + z-index: 2; +} + +.tabs-container > li, +.tabs-container > li.active { + background: var(--editorBgColor) !important; +} + +.open-project button, +.recent-files-projects button { + box-shadow: none !important; +} + +/* ------------------------------------ */ + +:not(pre) > code[class*='language-'], +pre:not(.CodeMirror-line), +pre[class*='language-'], +pre.mu-code-block { + background: var(--codeBlockBgColor) !important; + border: none !important; +} +.mu-thematic-break:not(.mu-active)::before { + border-top: 2px dashed var(--editorColor10) !important; + background: none !important; +} +figure.mu-active.mu-math-block > div.mu-math-preview, +figure.mu-active.mu-diagram-block > div.mu-diagram-preview { + box-shadow: 0 3px 8px 0 var(--floatShadow) !important; +} diff --git a/packages/desktop/src/renderer/src/assets/themes/ulysses.theme.css b/packages/desktop/src/renderer/src/assets/themes/ulysses.theme.css new file mode 100644 index 0000000000..c665902b89 --- /dev/null +++ b/packages/desktop/src/renderer/src/assets/themes/ulysses.theme.css @@ -0,0 +1,252 @@ +:root { + --themeColor: rgb(12, 139, 186); + --themeColor90: rgba(12, 139, 186, 0.9); + --themeColor80: rgba(12, 139, 186, 0.8); + --themeColor70: rgba(12, 139, 186, 0.7); + --themeColor60: rgba(12, 139, 186, 0.6); + --themeColor50: rgba(12, 139, 186, 0.5); + --themeColor40: rgba(12, 139, 186, 0.4); + --themeColor30: rgba(12, 139, 186, 0.3); + --themeColor20: rgba(12, 139, 186, 0.2); + --themeColor10: rgba(12, 139, 186, 0.1); + + --highlightColor: rgba(12, 139, 186, 0.4); + --selectionColor: rgba(0, 0, 0, 0.1); + --editorColor: rgba(101, 101, 101, 0.7); + --editorColor80: rgba(101, 101, 101, 0.8); + --editorColor60: rgba(101, 101, 101, 0.6); + --editorColor50: rgba(101, 101, 101, 0.5); + --editorColor40: rgba(101, 101, 101, 0.4); + --editorColor30: rgba(101, 101, 101, 0.3); + --editorColor10: rgba(101, 101, 101, 0.1); + --editorColor04: rgba(101, 101, 101, 0.04); + --editorBgColor: #f3f3f3; + --deleteColor: #ff6969; + --iconColor: rgba(101, 101, 101, 0.8); + --codeBgColor: #d8d8d869; + --codeBlockBgColor: rgba(12, 139, 186, 0.05); + --footnoteBgColor: rgba(0, 0, 0, 0.03); + --inputBgColor: rgba(0, 0, 0, 0.06); + --focusColor: var(--themeColor); + + --buttonFontColor: var(--editorColor); + --buttonBgColor: #ffffff; + --buttonBorder: 1px solid #dcdfe6; + --buttonShadow: none; + --buttonFontColorHover: var(--buttonFontColor); + --buttonBgColorHover: linear-gradient(#fafafa, #f5f5f5); + --buttonBorderHover: var(--buttonBorder); + --buttonFontColorActive: var(--buttonFontColor); + --buttonBgColorActive: #f5f5f5; + --buttonBorderActive: var(--buttonBorder); + --buttonFocusBorder: 1px solid var(--themeColor); + + --buttonPrimaryFontColor: #ffffff; + --buttonPrimaryBgColor: var(--themeColor); + --buttonPrimaryBorder: none; + --buttonPrimaryShadow: 0 0 8px 0 rgba(0, 0, 0, 0.1); + --buttonPrimaryFontColorHover: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorHover: var(--buttonPrimaryBgColor); + --buttonPrimaryBorderHover: var(--buttonPrimaryBorder); + --buttonPrimaryFontColorActive: var(--buttonPrimaryFontColor); + --buttonPrimaryBgColorActive: var(--buttonPrimaryBgColor); + --buttonPrimaryBorderActive: var(--buttonPrimaryBorder); + --buttonPrimaryFocusBorder: none; + --buttonPrimaryFocusShadow: inset 0 0 0 1px rgba(5, 66, 89, 0.5), 0 0 0 1px var(--themeColor); + --tableBorderColor: #e5e5e5; + + /* Markdown element colors */ + --headingColor: rgb(12, 139, 186); + --h1Color: rgb(12, 139, 186); + --h2Color: #1a9f7f; + --h3Color: #d16a3d; + --h4Color: #7a70ba; + --h5Color: #c75991; + --h6Color: #708090; + --blockquoteTextColor: rgba(101, 101, 101, 0.5); + --blockquoteBorderColor: rgb(12, 139, 186); + --hrColor: rgba(101, 101, 101, 0.1); + --linkColor: rgb(12, 139, 186); + --strongColor: rgba(101, 101, 101, 0.8); + --emColor: #1a9f7f; + --listMarkerColor: rgb(12, 139, 186); + + --sideBarColor: rgba(101, 101, 101, 0.6); + --sideBarIconColor: var(--iconColor); + --sideBarTitleColor: rgba(101, 101, 101, 1); + --sideBarTextColor: rgba(101, 101, 101, 0.4); + --sideBarBgColor: rgba(245, 245, 245, 0.95); + --sideBarItemHoverBgColor: rgba(101, 101, 101, 0.03); + --itemBgColor: rgba(245, 245, 245, 0.6); + + --floatFontColor: rgba(101, 101, 101, 0.7); + --floatBgColor: #ffffff; + --floatHoverColor: rgba(101, 101, 101, 0.04); + --floatBorderColor: rgba(0, 0, 0, 0.03); + --maskColor: rgba(232, 232, 232, 0.8); + --editorAreaWidth: 750px; + + /* @muyajs/core (kebab-case) editor variables + Additive prep for the muyajs -> @muyajs/core editor swap. These + mirror the camelCase vars above; the legacy editor ignores them. + Do NOT remove the camelCase vars — they still drive the current + editor until editor.vue switches engines. */ + --theme-color: var(--themeColor); + --highlight-color: var(--highlightColor); + --selection-color: var(--selectionColor); + --editor-color: var(--editorColor); + --editor-color-80: var(--editorColor80); + --editor-color-50: var(--editorColor50); + --editor-color-30: var(--editorColor30); + --editor-color-10: var(--editorColor10); + --editor-color-04: var(--editorColor04); + --editor-bg-color: var(--editorBgColor); + --editor-area-width: var(--editorAreaWidth); + --delete-color: var(--deleteColor); + --icon-color: var(--iconColor); + --code-block-bg-color: var(--codeBlockBgColor); + --table-border-color: var(--tableBorderColor); + --input-bg-color: var(--inputBgColor); + --button-font-color: var(--buttonFontColor); + --button-bg-color: var(--buttonBgColor); + --button-border: var(--buttonBorder); + --button-bg-color-hover: var(--buttonBgColorHover); + --button-border-hover: var(--buttonBorderHover); + --button-bg-color-active: var(--buttonBgColorActive); + --button-border-active: var(--buttonBorderActive); + --button-border-focus: var(--button-border); + --float-bg-color: var(--floatBgColor); + --float-hover-color: var(--floatHoverColor); + --float-border-color: var(--floatBorderColor); + --float-shadow: rgb(15 15 15 / 3%) 0 0 0 1px, rgb(15 15 15 / 4%) 0 3px 6px, rgb(15 15 15 / 5%) 0 9px 24px; + --link-color: var(--linkColor); + --h1-color: var(--h1Color); + --h2-color: var(--h2Color); + --h3-color: var(--h3Color); + --h4-color: var(--h4Color); + --h5-color: var(--h5Color); + --h6-color: var(--h6Color); + --blockquote-text-color: var(--blockquoteTextColor); + --blockquote-border-color: var(--blockquoteBorderColor); + --strong-color: var(--strongColor); + --em-color: var(--emColor); + --list-marker-color: var(--listMarkerColor); + --hr-color: var(--hrColor); +} + +.editor-tabs { + box-shadow: none !important; +} + +.tabs-container > li { + border-right: 1px solid #e5e5e5 !important; + background: var(--editorBgColor) !important; +} + +/* ------------------------------------ */ + +.mu-container h1, +.mu-container h2, +.mu-container h3, +.mu-container h4, +.mu-container h5, +.mu-container h6 { + color: var(--theme-color); + text-align: center; +} + +ul.mu-bullet-list > li.mu-list-item { + position: relative; + list-style: none; +} +ul.mu-bullet-list > li.mu-list-item::before { + content: ''; + display: block; + position: absolute; + width: 5px; + height: 2px; + left: -18px; + top: 15px; + background: var(--editor-color); +} + +.mu-container blockquote { + background: rgb(233, 233, 233); +} +.mu-container blockquote::before { + content: none; +} + +.mu-container li.mu-list-item { + color: var(--editor-color); +} + +/*task list*/ +li.mu-task-list-item > input[type='checkbox'], +li.mu-task-list-item > span.mu-task-list-checkbox { + position: absolute; + cursor: pointer; + width: 16px; + height: 16px; + top: 0.1em; + left: -24px; + margin: 0; + transform: rotate(-90deg); + transform-origin: center; + transition: all 0.2s ease; +} +li.mu-task-list-item > input.mu-checkbox-checked, +li.mu-task-list-item > span.mu-checkbox-checked { + transform: rotate(0); + opacity: 0.5; +} +li.mu-task-list-item > input[type='checkbox']::before, +li.mu-task-list-item > span.mu-task-list-checkbox::before { + content: ''; + width: 16px; + height: 16px; + box-sizing: border-box; + display: inline-block; + border: 2px solid var(--editor-color); + border-radius: 2px; + background-color: var(--editor-bg-color); + position: absolute; + top: 0; + left: 0; + transition: all 0.2s ease; +} +li.mu-task-list-item > input.mu-checkbox-checked::before, +li.mu-task-list-item > span.mu-checkbox-checked::before { + border: transparent; + background-color: var(--editor-color); + box-shadow: none; +} +li.mu-task-list-item > input::after, +li.mu-task-list-item > span.mu-task-list-checkbox::after { + content: ''; + width: 9px; + height: 5px; + box-sizing: content-box; + border: 2px solid #fff; + border-top: none; + border-right: none; + transform: rotate(-45deg) scale(0); + transform-origin: bottom; + position: absolute; + display: inline-block; + top: 1px; + left: 5px; + transition: all 0.2s ease; +} +li.mu-task-list-item > input.mu-checkbox-checked::after, +li.mu-task-list-item > span.mu-checkbox-checked::after { + transform: rotate(-45deg) scale(1); +} + +/*horizontal line*/ +.mu-thematic-break:not(.mu-active)::before { + width: 50%; + left: 50%; + border-top: 2px dashed var(--editor-color-50); + transform: translateX(-50%) translateY(-50%); +} diff --git a/src/renderer/assets/window-controls.js b/packages/desktop/src/renderer/src/assets/window-controls.ts similarity index 100% rename from src/renderer/assets/window-controls.js rename to packages/desktop/src/renderer/src/assets/window-controls.ts diff --git a/packages/desktop/src/renderer/src/axios/index.ts b/packages/desktop/src/renderer/src/axios/index.ts new file mode 100644 index 0000000000..5b3fa60689 --- /dev/null +++ b/packages/desktop/src/renderer/src/axios/index.ts @@ -0,0 +1,5 @@ +import axios, { type AxiosInstance } from 'axios' + +const http: AxiosInstance = axios.create({}) + +export default http diff --git a/packages/desktop/src/renderer/src/bootstrap.ts b/packages/desktop/src/renderer/src/bootstrap.ts new file mode 100644 index 0000000000..d9a3a2e639 --- /dev/null +++ b/packages/desktop/src/renderer/src/bootstrap.ts @@ -0,0 +1,126 @@ +import log from 'electron-log/renderer' +import RendererPaths from './node/paths' + +let exceptionLogger: (s: unknown) => void = (s) => console.error(s) + +const configureLogger = (): void => { + const isDev = window.electron?.process?.env?.NODE_ENV === 'development' + log.transports.console.level = isDev ? 'info' : false // mirror to window console + exceptionLogger = log.error +} + +interface UrlArgs { + type: string | null + debug: boolean + userDataPath: string | null + windowId: number + initialState: { + codeFontFamily: string | null + codeFontSize: string | null + hideScrollbar: boolean + theme: string | null + titleBarStyle: string | null + } +} + +const parseUrlArgs = (): UrlArgs => { + const params = new URLSearchParams(window.location.search) + const codeFontFamily = params.get('cff') + const codeFontSize = params.get('cfs') + const debug = params.get('debug') === '1' + const hideScrollbar = params.get('hsb') === '1' + const theme = params.get('theme') + const titleBarStyle = params.get('tbs') + const userDataPath = params.get('udp') + const windowId = Number(params.get('wid')) + const type = params.get('type') + + if (Number.isNaN(windowId)) { + throw new Error('Error while parsing URL arguments: windowId!') + } + + return { + type, + debug, + userDataPath, + windowId, + initialState: { + codeFontFamily, + codeFontSize, + hideScrollbar, + theme, + titleBarStyle + } + } +} + +/** + * Check if an error is a known non-fatal CodeMirror race condition. + * These errors occur when clicking in the editor during rapid state changes + * and don't affect functionality - the user can simply click again. + * + * @param error - The error to check + * @returns True if this is a suppressible CodeMirror error + */ +const isCodeMirrorRaceCondition = (error: Error | null | undefined): boolean => { + if (!error || !error.stack) return false + + // CodeMirror internal error when line measurement data is unavailable during mouse click + // This happens when the document state is out of sync with the display during rapid changes + const isMapOnUndefined = error.message === "Cannot read properties of undefined (reading 'map')" + const isInPrepareMeasure = error.stack.includes('prepareMeasureForLine') + const isInCoordsChar = error.stack.includes('coordsChar') || error.stack.includes('posFromMouse') + + return isMapOnUndefined && isInPrepareMeasure && isInCoordsChar +} + +const handleRendererError = (event: ErrorEvent | PromiseRejectionEvent | Event): void => { + const errorEvent = event as ErrorEvent + if (errorEvent.error) { + if (isCodeMirrorRaceCondition(errorEvent.error)) { + console.warn('Suppressed non-fatal CodeMirror race condition:', errorEvent.error.message) + return + } + + const { message, name, stack } = errorEvent.error + const copy = { + message, + name, + stack + } + + exceptionLogger(errorEvent.error) + + // Pass exception to main process exception handler to show a error dialog. + window.electron.ipcRenderer.send('mt::handle-renderer-error', copy) + } else { + console.error(event) + } +} + +const bootstrapRenderer = (): void => { + // Register renderer exception handler + window.addEventListener('error', handleRendererError) + window.addEventListener('unhandledrejection', handleRendererError) + + const { debug, initialState, userDataPath, windowId, type } = parseUrlArgs() + // RendererPaths throws when userDataPath is missing; preserve that runtime check. + const paths = new RendererPaths(userDataPath as string) + const marktext = { + initialState, + env: { + debug, + paths, + windowId, + type + }, + paths + } + // `global` is not available in a sandboxed renderer — attach to window. + // RendererPaths has no string index signature, so widen through `unknown`. + window.marktext = marktext as unknown as Window['marktext'] + + configureLogger() +} + +export default bootstrapRenderer diff --git a/packages/desktop/src/renderer/src/bus/index.ts b/packages/desktop/src/renderer/src/bus/index.ts new file mode 100644 index 0000000000..efef72c4be --- /dev/null +++ b/packages/desktop/src/renderer/src/bus/index.ts @@ -0,0 +1,13 @@ +import mitt, { type Emitter } from 'mitt' + +// NOTE: We intentionally use mitt's default (string → unknown) event map +// rather than `BusEvents` from `@shared/types/bus`. `BusEvents` uses a +// tuple/`unknown[]` payload shape (designed for the Node EventEmitter +// `...args` convention), but mitt is strictly single-arg +// (`emit(type, event)`). Adopting `BusEvents` here would +// require wrapping every existing emit call in an array, changing +// runtime semantics. As individual events are typed in later commits, +// we can swap to a tuple-aware wrapper. +const emitter: Emitter> = mitt() + +export default emitter diff --git a/packages/desktop/src/renderer/src/codeMirror/index.css b/packages/desktop/src/renderer/src/codeMirror/index.css new file mode 100644 index 0000000000..bf56b29778 --- /dev/null +++ b/packages/desktop/src/renderer/src/codeMirror/index.css @@ -0,0 +1,17 @@ +.CodeMirror { + background: transparent; + height: auto; +} +.CodeMirror-line::selection, +.CodeMirror-line > span::selection, +.CodeMirror-line > span > span::selection { + background: rgb(178, 215, 254); +} +/* #2372 — make the source-mode selection use the same visible colour as the + WYSIWYG editor. The bundled railscasts theme paints the selection at #272935, + almost identical to its #2b2b2b background, so it was invisible on dark + themes. The higher specificity (+ !important) beats both railscasts' own + !important rule and the one-dark theme. */ +.source-code .CodeMirror div.CodeMirror-selected { + background: var(--selection-color, rgb(45 170 219 / 30%)) !important; +} diff --git a/packages/desktop/src/renderer/src/codeMirror/index.ts b/packages/desktop/src/renderer/src/codeMirror/index.ts new file mode 100644 index 0000000000..31882de60b --- /dev/null +++ b/packages/desktop/src/renderer/src/codeMirror/index.ts @@ -0,0 +1,184 @@ +import { filter } from 'fuzzaldrin' +import 'codemirror/addon/edit/closebrackets' +import 'codemirror/addon/edit/closetag' +import 'codemirror/addon/selection/active-line' +import 'codemirror/mode/meta' +import codeMirror from 'codemirror/lib/codemirror' +import type CodeMirror from 'codemirror' + +import loadmode from './loadmode' +import overlayMode from './overlayMode' +import multiplexMode from './mltiplexMode' +import registerMarkdownMathMode from './markdownMathMode' +import languages from './modes' +import 'codemirror/lib/codemirror.css' +import './index.css' +import 'codemirror/theme/railscasts.css' + +// The runtime is imported from `codemirror/lib/codemirror` (an `any` shim), but +// the public editor surface is typed via `@types/codemirror`. +// Only the legacy `window.CodeMirror` exposure references this; never read typed. +type CodeMirrorLike = unknown +type CodeMirrorInstance = CodeMirror.Editor +type CodeMirrorDoc = CodeMirror.Doc +// `getCursor()` returns extra runtime flags not present on the typed `Position`. +type CursorPosition = CodeMirror.Position & { outside?: boolean; hitSide?: boolean } + +interface ModeInfoEntry { + name?: string + mime?: string | string[] + mimes?: string[] + mode?: string + [key: string]: unknown +} + +interface MatchedMode { + name: string + mode: ModeInfoEntry +} + +loadmode(codeMirror) +overlayMode(codeMirror) +multiplexMode(codeMirror) +registerMarkdownMathMode(codeMirror) +;(window as unknown as { CodeMirror: CodeMirrorLike }).CodeMirror = codeMirror + +const modes: ModeInfoEntry[] = codeMirror.modeInfo +codeMirror.modeURL = '../../../../node_modules/codemirror/mode/%N/%N.js' + +const getModeFromName = (name: string): MatchedMode | null => { + let result: MatchedMode | null = null + const lang = languages.filter((lang) => lang.name === name)[0] + if (lang) { + const { name, mode, mime } = lang + const matched = modes.filter((m) => { + if (m.mime) { + if (Array.isArray(m.mime) && m.mime.indexOf(mime) > -1 && m.mode === mode) { + return true + } else if (typeof m.mime === 'string' && m.mime === mime && m.mode === mode) { + return true + } + } + if (Array.isArray(m.mimes) && m.mimes.indexOf(mime) > -1 && m.mode === mode) { + return true + } + return false + }) + if (matched.length && typeof matched[0] === 'object') { + result = { + name, + mode: matched[0] + } + } + } + return result +} + +export const search = (text: string): MatchedMode[] => { + const matchedLangs = filter(languages, text, { key: 'name' }) + return matchedLangs + .map(({ name }: { name: string }) => getModeFromName(name)) + .filter((lang: MatchedMode | null): lang is MatchedMode => !!lang) +} + +/** + * set cursor at the end of last line. + */ +export const setCursorAtLastLine = (cm: CodeMirrorInstance): void => { + const lastLine = cm.lastLine() + const lineHandle = cm.getLineHandle(lastLine) + + cm.focus() + cm.setCursor(lastLine, lineHandle.text.length) +} + +// if cursor at firstLine return true +export const isCursorAtFirstLine = (cm: CodeMirrorInstance): boolean => { + const cursor = cm.getCursor() as CursorPosition + const { line, ch, outside } = cursor + + return line === 0 && ch === 0 && !!outside +} + +export const isCursorAtLastLine = (cm: CodeMirrorInstance): boolean => { + const lastLine = cm.lastLine() + const cursor = cm.getCursor() as CursorPosition + const { line, outside, sticky } = cursor + return line === lastLine && (outside || !sticky) +} + +export const isCursorAtBegin = (cm: CodeMirrorInstance): boolean => { + const cursor = cm.getCursor() as CursorPosition + const { line, ch, hitSide } = cursor + return line === 0 && ch === 0 && !!hitSide +} + +export const onlyHaveOneLine = (cm: CodeMirrorInstance): boolean => { + return cm.lineCount() === 1 +} + +export const isCursorAtEnd = (cm: CodeMirrorInstance): boolean => { + const lastLine = cm.lastLine() + const lastLineHandle = cm.getLineHandle(lastLine) + const cursor = cm.getCursor() as CursorPosition + const { line, ch, hitSide } = cursor + + return line === lastLine && ch === lastLineHandle.text.length && !!hitSide +} + +export const getBeginPosition = (): { + anchor: { line: number; ch: number } + head: { line: number; ch: number } +} => { + return { + anchor: { line: 0, ch: 0 }, + head: { line: 0, ch: 0 } + } +} + +export const getEndPosition = ( + cm: CodeMirrorInstance +): { + anchor: { line: number; ch: number } + head: { line: number; ch: number } +} => { + const lastLine = cm.lastLine() + const lastLineHandle = cm.getLineHandle(lastLine) + const line = lastLine + const ch = lastLineHandle.text.length + return { anchor: { line, ch }, head: { line, ch } } +} + +export const setCursorAtFirstLine = (cm: CodeMirrorInstance): void => { + cm.focus() + cm.setCursor(0, 0) +} + +export const setMode = (doc: CodeMirrorDoc, text: string): Promise => { + const m = getModeFromName(text) + + if (!m) { + const errMsg = !text + ? "You'd better provided a language mode when you create code block" + : `${text} is not a valid language mode!` + return Promise.reject(errMsg) + } + + const { mode, mime } = m.mode as { mode: string; mime: string | string[] } + return new Promise((resolve) => { + codeMirror.requireMode(mode, () => { + ;(doc as unknown as { setOption(option: string, value: unknown): void }).setOption( + 'mode', + mime || mode + ) + codeMirror.autoLoadMode(doc, mode) + resolve(m) + }) + }) +} + +export const setTextDirection = (cm: CodeMirrorInstance, textDirection: string): void => { + cm.setOption('direction', textDirection as 'ltr' | 'rtl') +} + +export default codeMirror diff --git a/packages/desktop/src/renderer/src/codeMirror/loadmode.ts b/packages/desktop/src/renderer/src/codeMirror/loadmode.ts new file mode 100644 index 0000000000..c00620a250 --- /dev/null +++ b/packages/desktop/src/renderer/src/codeMirror/loadmode.ts @@ -0,0 +1,89 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +type ModeLoader = () => Promise + +const codeMirrorMode = import.meta.glob( + '../../../../node_modules/codemirror/mode/**/*.js' +) as Record + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type CodeMirrorLike = any + +const loadMore = (CodeMirror: CodeMirrorLike): void => { + if (!CodeMirror.modeURL) { + CodeMirror.modeURL = '../../../../node_modules/codemirror/mode/%N/%N.js' + } + + const loading: Record void>> = {} + function splitCallback(cont: () => void, n: number): () => void { + let countDown = n + return function() { + if (--countDown === 0) cont() + } + } + function ensureDeps(mode: string, cont: () => void): void { + const deps: string[] | undefined = CodeMirror.modes[mode].dependencies + if (!deps) return cont() + const missing: string[] = [] + for (let i = 0; i < deps.length; ++i) { + if (!Object.prototype.hasOwnProperty.call(CodeMirror.modes, deps[i])) { + missing.push(deps[i]) + } + } + if (!missing.length) return cont() + const split = splitCallback(cont, missing.length) + for (let i = 0; i < missing.length; ++i) { + CodeMirror.requireMode(missing[i], split) + } + } + + CodeMirror.requireMode = function(mode: string | { name: string }, cont: () => void): void { + if (typeof mode !== 'string') { + mode = mode.name + } + if (Object.prototype.hasOwnProperty.call(CodeMirror.modes, mode)) return ensureDeps(mode, cont) + if (Object.prototype.hasOwnProperty.call(loading, mode)) { + loading[mode].push(cont) + return + } + + const list = (loading[mode] = [cont]) + + const pathKey: string = CodeMirror.modeURL.replace(/%N/g, mode) + + if (!pathKey) { + delete loading[mode] + console.error(`Cannot find path for CodeMirror mode ${mode}`) + return + } + + const loader = codeMirrorMode[pathKey] + if (typeof loader !== 'function') { + delete loading[mode] + console.error(`Invalid loader for CodeMirror mode ${mode}`) + return + } + + loader() + .then(() => { + ensureDeps(mode as string, function() { + for (let i = 0; i < list.length; ++i) { + list[i]() + } + }) + }) + .catch((err: unknown) => { + console.error(`Failed to load CodeMirror mode "${mode as string}"`, err) + }) + } + + CodeMirror.autoLoadMode = function(instance: CodeMirrorLike, mode: string): void { + if (!Object.prototype.hasOwnProperty.call(CodeMirror.modes, mode)) { + CodeMirror.requireMode(mode, function() { + instance.setOption('mode', instance.getOption('mode')) + }) + } + } +} +export default loadMore diff --git a/packages/desktop/src/renderer/src/codeMirror/markdownMathMode.ts b/packages/desktop/src/renderer/src/codeMirror/markdownMathMode.ts new file mode 100644 index 0000000000..3295b19ebb --- /dev/null +++ b/packages/desktop/src/renderer/src/codeMirror/markdownMathMode.ts @@ -0,0 +1,69 @@ +// CodeMirror "markdown-math" — a GFM-flavoured Markdown mode that delegates +// the contents of `$...$` (inline) and `$$...$$` (block) math spans to the +// stex (LaTeX) mode. Without this wrapper the standard markdown mode highlights +// `_` as emphasis delimiters even inside math, producing spurious italics for +// subscript expressions like `$\text{F}_\text{A} = \text{F}_\text{B}$` in the +// source view. See https://github.com/marktext/marktext/issues/4121. +// +// The outer mode is `gfm` to preserve the tables / autolinks / task lists +// styling the previous `setMode(cm, 'markdown')` call resolved to via +// `codeMirror/modes.js` (which maps "markdown" → `gfm` / `text/x-gfm`). +// +// All inner modes are loaded eagerly so `getMode` resolves synchronously at +// the time the wrapper is instantiated. `gfm` itself depends on `markdown`. +import 'codemirror/mode/markdown/markdown' +import 'codemirror/mode/gfm/gfm' +import 'codemirror/mode/stex/stex' + +// Open guard for inline `$…$`. Mirrors Muya's `inline_math` rule +// (`src/muya/lib/parser/rules.js`): require non-empty content with no inner +// `$`, last char before the closer not being `\`, and the closing `$` not +// followed by another `$` (which would be block math). This stops a single +// stray `$` (e.g. "$5 owed") from flipping the inner mode on forever and keeps +// the source-view tokenization aligned with the inline parse. +const INLINE_MATH_OPEN = /\$(?!\$)(?=[^$\n]*?[^$\\]\$(?!\$))/ + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type CodeMirrorLike = any +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type AnyObj = any + +const registerMarkdownMathMode = (CodeMirror: CodeMirrorLike): void => { + if (CodeMirror.modes && Object.prototype.hasOwnProperty.call(CodeMirror.modes, 'markdown-math')) { + return + } + + CodeMirror.defineMode('markdown-math', function(config: AnyObj) { + const gfmMode = CodeMirror.getMode(config, { + name: 'gfm', + fencedCodeBlocks: true, + strikethrough: true, + taskLists: true + }) + const stexMode = CodeMirror.getMode(config, 'stex') + + // `$$` must come before `$` so the longer delimiter is matched first. + // Block math (`$$…$$`) intentionally has no lookahead guard: a matching + // closer typically lives on a later line, which CodeMirror's per-line + // tokenizer cannot see from the opener. + return CodeMirror.multiplexingMode( + gfmMode, + { + open: '$$', + close: '$$', + mode: stexMode, + delimStyle: 'formatting formatting-math formatting-math-block math-block', + innerStyle: 'math math-block' + }, + { + open: INLINE_MATH_OPEN, + close: '$', + mode: stexMode, + delimStyle: 'formatting formatting-math formatting-math-inline math-inline', + innerStyle: 'math math-inline' + } + ) + }) +} + +export default registerMarkdownMathMode diff --git a/packages/desktop/src/renderer/src/codeMirror/mltiplexMode.ts b/packages/desktop/src/renderer/src/codeMirror/mltiplexMode.ts new file mode 100644 index 0000000000..5daef0486a --- /dev/null +++ b/packages/desktop/src/renderer/src/codeMirror/mltiplexMode.ts @@ -0,0 +1,144 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type CodeMirrorLike = any +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type AnyObj = any + +const multiplexMode = (CodeMirror: CodeMirrorLike): void => { + CodeMirror.multiplexingMode = function(outer: AnyObj /*, others */): AnyObj { + // Others should be {open, close, mode [, delimStyle] [, innerStyle]} objects + // eslint-disable-next-line prefer-rest-params + const others = Array.prototype.slice.call(arguments, 1) + + function indexOf( + string: string, + pattern: string | RegExp, + from: number, + returnEnd?: boolean + ): number { + if (typeof pattern === 'string') { + const found = string.indexOf(pattern, from) + return returnEnd && found > -1 ? found + pattern.length : found + } + const m = pattern.exec(from ? string.slice(from) : string) + return m ? m.index + from + (returnEnd ? m[0].length : 0) : -1 + } + + return { + startState() { + return { + outer: CodeMirror.startState(outer), + innerActive: null, + inner: null + } + }, + + copyState(state: AnyObj) { + return { + outer: CodeMirror.copyState(outer, state.outer), + innerActive: state.innerActive, + inner: state.innerActive && CodeMirror.copyState(state.innerActive.mode, state.inner) + } + }, + + token(stream: AnyObj, state: AnyObj): string | null { + if (!state.innerActive) { + let cutOff = Infinity + const oldContent = stream.string + for (let i = 0; i < others.length; ++i) { + const other = others[i] + const found = indexOf(oldContent, other.open, stream.pos) + if (found === stream.pos) { + if (!other.parseDelimiters) stream.match(other.open) + state.innerActive = other + + // Get the outer indent, making sure to handle CodeMirror.Pass + let outerIndent = 0 + if (outer.indent) { + const possibleOuterIndent = outer.indent(state.outer, '') + if (possibleOuterIndent !== CodeMirror.Pass) outerIndent = possibleOuterIndent + } + + state.inner = CodeMirror.startState(other.mode, outerIndent) + return other.delimStyle && other.delimStyle + ' ' + other.delimStyle + '-open' + } else if (found !== -1 && found < cutOff) { + cutOff = found + } + } + if (cutOff !== Infinity) stream.string = oldContent.slice(0, cutOff) + const outerToken = outer.token(stream, state.outer) + if (cutOff !== Infinity) stream.string = oldContent + return outerToken + } else { + const curInner = state.innerActive + const oldContent = stream.string + if (!curInner.close && stream.sol()) { + state.innerActive = state.inner = null + return this.token(stream, state) + } + const found = curInner.close + ? indexOf(oldContent, curInner.close, stream.pos, curInner.parseDelimiters) + : -1 + if (found === stream.pos && !curInner.parseDelimiters) { + stream.match(curInner.close) + state.innerActive = state.inner = null + return curInner.delimStyle && curInner.delimStyle + ' ' + curInner.delimStyle + '-close' + } + if (found > -1) stream.string = oldContent.slice(0, found) + let innerToken = curInner.mode.token(stream, state.inner) + if (found > -1) stream.string = oldContent + + if (found === stream.pos && curInner.parseDelimiters) { + state.innerActive = state.inner = null + } + + if (curInner.innerStyle) { + if (innerToken) innerToken = innerToken + ' ' + curInner.innerStyle + else innerToken = curInner.innerStyle + } + + return innerToken + } + }, + + indent(state: AnyObj, textAfter: string) { + const mode = state.innerActive ? state.innerActive.mode : outer + if (!mode.indent) return CodeMirror.Pass + return mode.indent(state.innerActive ? state.inner : state.outer, textAfter) + }, + + blankLine(state: AnyObj) { + const mode = state.innerActive ? state.innerActive.mode : outer + if (mode.blankLine) { + mode.blankLine(state.innerActive ? state.inner : state.outer) + } + if (!state.innerActive) { + for (let i = 0; i < others.length; ++i) { + const other = others[i] + if (other.open === '\n') { + state.innerActive = other + state.inner = CodeMirror.startState( + other.mode, + mode.indent ? mode.indent(state.outer, '') : 0 + ) + } + } + } else if (state.innerActive.close === '\n') { + state.innerActive = state.inner = null + } + }, + + electricChars: outer.electricChars, + + innerMode(state: AnyObj) { + return state.inner + ? { state: state.inner, mode: state.innerActive.mode } + : { state: state.outer, mode: outer } + } + } + } +} + +export default multiplexMode diff --git a/packages/desktop/src/renderer/src/codeMirror/modes.ts b/packages/desktop/src/renderer/src/codeMirror/modes.ts new file mode 100644 index 0000000000..f34c3e5498 --- /dev/null +++ b/packages/desktop/src/renderer/src/codeMirror/modes.ts @@ -0,0 +1,425 @@ +export interface LanguageDescriptor { + name: string + mode: string + mime: string +} + +const languages: LanguageDescriptor[] = [ + { + name: 'objectivec', + mode: 'clike', + mime: 'text/x-objectivec' + }, + { + name: 'swift', + mode: 'swift', + mime: 'text/x-swift' + }, + { + name: 'c_cpp', + mode: 'clike', + mime: 'text/x-csrc' + }, + { + name: 'c', + mode: 'clike', + mime: 'text/x-csrc' + }, + { + name: 'c++', + mode: 'clike', + mime: 'text/x-c++src' + }, + { + name: 'cmake', + mode: 'cmake', + mime: 'text/x-cmake' + }, + { + name: 'lisp', + mode: 'commonlisp', + mime: 'text/x-common-lisp' + }, + { + name: 'pascal', + mode: 'pascal', + mime: 'text/x-pascal' + }, + { + name: 'eiffel', + mode: 'eiffel', + mime: 'text/x-eiffel' + }, + { + name: 'yaml', + mode: 'yaml', + mime: 'text/x-yaml' + }, + { + name: 'xml', + mode: 'xml', + mime: 'application/xml' + }, + { + name: 'django', + mode: 'django', + mime: 'text/x-django' + }, + { + name: 'clojure', + mode: 'clojure', + mime: 'text/x-clojure' + }, + { + name: 'crystal', + mode: 'crystal', + mime: 'text/x-crystal' + }, + { + name: 'ruby', + mode: 'ruby', + mime: 'text/x-ruby' + }, + { + name: 'python', + mode: 'python', + mime: 'text/x-python' + }, + { + name: 'sh', + mode: 'shell', + mime: 'text/x-sh' + }, + { + /* alias */ name: 'shell', + mode: 'shell', + mime: 'text/x-sh' + }, + { + name: 'less', + mode: 'css', + mime: 'text/css' + }, + { + name: 'php', + mode: 'php', + mime: 'application/x-httpd-php' + }, + { + name: 'json', + mode: 'javascript', + mime: 'application/json' + }, + { + name: 'smarty', + mode: 'smarty', + mime: 'text/x-smarty' + }, + { + name: 'cobol', + mode: 'cobol', + mime: 'text/x-cobol' + }, + { + name: 'go', + mode: 'go', + mime: 'text/x-go' + }, + { + /* alias */ name: 'golang', + mode: 'go', + mime: 'text/x-go' + }, + { + name: 'makefile', + mode: 'shell' /* makefile syntax is not supported by CodeMirror */, + mime: 'text/x-sh' + }, + { + name: 'ocaml', + mode: 'mllike', + mime: 'text/x-ocaml' + }, + { + name: 'textile', + mode: 'textile', + mime: 'text/x-textile' + }, + { + name: 'd', + mode: 'd', + mime: 'text/x-d' + }, + { + name: 'jade', + mode: 'pug', + mime: 'text/x-pug' + }, + { + name: 'lua', + mode: 'lua', + mime: 'text/x-lua' + }, + { + name: 'coffee', + mode: 'coffeescript', + mime: 'text/x-coffeescript' + }, + { + name: 'html', + mode: 'htmlmixed', + mime: 'text/html' + }, + { + name: 'pgsql', + mode: 'sql', + mime: 'text/x-sql' + }, + { + name: 'haskell', + mode: 'haskell', + mime: 'text/x-haskell' + }, + { + name: 'jsp', + mode: 'htmlembedded', + mime: 'application/x-jsp' + }, + { + name: 'tcl', + mode: 'tcl', + mime: 'text/x-tcl' + }, + { + name: 'ini', + mode: 'properties', + mime: 'text/x-properties' + }, + { + name: 'jsoniq', + mode: 'javascript', + mime: 'application/json' + }, + { + name: 'vhdl', + mode: 'vhdl', + mime: 'text/x-vhdl' + }, + { + name: 'verilog', + mode: 'verilog', + mime: 'text/x-systemverilog' + }, + { + name: 'csharp', + mode: 'clike', + mime: 'text/x-csharp' + }, + { + name: 'cs', + mode: 'clike', + mime: 'text/x-csharp' + }, + { + name: 'rust', + mode: 'rust', + mime: 'text/x-rustsrc' + }, + { + name: 'livescript', + mode: 'livescript', + mime: 'text/x-livescript' + }, + { + name: 'jsx', + mode: 'jsx', + mime: 'text/jsx' + }, + { + name: 'protobuf', + mode: 'protobuf', + mime: 'text/x-protobuf' + }, + { + name: 'markdown', + mode: 'gfm', + mime: 'text/x-gfm' + }, + { + name: 'rst', + mode: 'rst', + mime: 'text/x-rst' + }, + { + name: 'LaTeX', + mode: 'stex', + mime: 'text/x-latex' + }, + { + name: 'java', + mode: 'clike', + mime: 'text/x-java' + }, + { + name: 'kotlin', + mode: 'clike', + mime: 'text/x-kotlin' + }, + { + name: 'javascript', + mode: 'javascript', + mime: 'text/javascript' + }, + { + name: 'erlang', + mode: 'erlang', + mime: 'text/x-erlang' + }, + { + name: 'scheme', + mode: 'scheme', + mime: 'text/x-scheme' + }, + { + name: 'sass', + mode: 'sass', + mime: 'text/x-sass' + }, + { + name: 'groovy', + mode: 'groovy', + mime: 'text/x-groovy' + }, + { + name: 'julia', + mode: 'julia', + mime: 'text/x-julia' + }, + { + name: 'haml', + mode: 'haml', + mime: 'text/x-haml' + }, + { + name: 'powershell', + mode: 'powershell', + mime: 'application/x-powershell' + }, + { + name: 'typescript', + mode: 'javascript', + mime: 'application/typescript' + }, + { + name: 'dart', + mode: 'dart', + mime: 'application/dart' + }, + { + name: 'xquery', + mode: 'xquery', + mime: 'application/xquery' + }, + { + name: 'elm', + mode: 'elm', + mime: 'text/x-elm' + }, + { + name: 'plsql', + mode: 'sql', + mime: 'text/x-plsql' + }, + { + name: 'forth', + mode: 'forth', + mime: 'text/x-forth' + }, + { + name: 'scala', + mode: 'clike', + mime: 'text/x-scala' + }, + { + name: 'perl', + mode: 'perl', + mime: 'text/x-perl' + }, + { + name: 'haxe', + mode: 'haxe', + mime: 'text/x-haxe' + }, + { + name: 'rhtml', + mode: 'htmlembedded', + mime: 'application/x-erb' + }, + { + name: 'scss', + mode: 'css', + mime: 'text/x-scss' + }, + { + name: 'sql', + mode: 'sql', + mime: 'text/x-sql' + }, + { + name: 'css', + mode: 'css', + mime: 'text/css' + }, + { + name: 'tex', + mode: 'stex', + mime: 'text/x-stex' + }, + { + name: 'r', + mode: 'r', + mime: 'text/x-rsrc' + }, + { + name: 'diff', + mode: 'diff', + mime: 'text/x-diff' + }, + { + name: 'twig', + mode: 'twig', + mime: 'text/x-twig' + }, + { + name: 'matlab', + mode: 'octave', + mime: 'text/x-octave' + }, + { + name: 'soy_template', + mode: 'soy', + mime: 'text/x-soy' + }, + { + name: 'dockerfile', + mode: 'dockerfile', + mime: 'text/x-dockerfile' + }, + { + name: 'toml', + mode: 'toml', + mime: 'text/x-toml' + }, + { + name: 'pgp', + mode: 'asciiarmor', + mime: 'application/pgp' + }, + { + name: 'Nginx', + mode: 'nginx', + mime: 'text/x-nginx-conf' + } +] + +export default languages diff --git a/packages/desktop/src/renderer/src/codeMirror/overlayMode.ts b/packages/desktop/src/renderer/src/codeMirror/overlayMode.ts new file mode 100644 index 0000000000..0f365ec11f --- /dev/null +++ b/packages/desktop/src/renderer/src/codeMirror/overlayMode.ts @@ -0,0 +1,109 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +// Utility function that allows modes to be combined. The mode given +// as the base argument takes care of most of the normal mode +// functionality, but a second (typically simple) mode is used, which +// can override the style of text. Both modes get to parse all of the +// text, but when both assign a non-null style to a piece of code, the +// overlay wins, unless the combine argument was true and not overridden, +// or state.overlay.combineTokens was true, in which case the styles are +// combined. + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type CodeMirrorLike = any +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type AnyObj = any + +const overlayMode = (CodeMirror: CodeMirrorLike): void => { + CodeMirror.overlayMode = function(base: AnyObj, overlay: AnyObj, combine?: boolean): AnyObj { + return { + startState() { + return { + base: CodeMirror.startState(base), + overlay: CodeMirror.startState(overlay), + basePos: 0, + baseCur: null, + overlayPos: 0, + overlayCur: null, + streamSeen: null + } + }, + + copyState(state: AnyObj) { + return { + base: CodeMirror.copyState(base, state.base), + overlay: CodeMirror.copyState(overlay, state.overlay), + basePos: state.basePos, + baseCur: null, + overlayPos: state.overlayPos, + overlayCur: null + } + }, + + token(stream: AnyObj, state: AnyObj) { + if ( + stream !== state.streamSeen || + Math.min(state.basePos, state.overlayPos) < stream.start + ) { + state.streamSeen = stream + state.basePos = state.overlayPos = stream.start + } + + if (stream.start === state.basePos) { + state.baseCur = base.token(stream, state.base) + state.basePos = stream.pos + } + + if (stream.start === state.overlayPos) { + stream.pos = stream.start + state.overlayCur = overlay.token(stream, state.overlay) + state.overlayPos = stream.pos + } + + stream.pos = Math.min(state.basePos, state.overlayPos) + + // state.overlay.combineTokens always takes precedence over combine, + // unless set to null + if (state.overlayCur === null) { + return state.baseCur + } else if ( + (state.baseCur !== null && state.overlay.combineTokens) || + (combine && state.overlay.combineTokens === null) + ) { + return state.baseCur + ' ' + state.overlayCur + } else return state.overlayCur + }, + + indent: + base.indent && + function(state: AnyObj, textAfter: string) { + return base.indent(state.base, textAfter) + }, + + electricChars: base.electricChars, + + innerMode(state: AnyObj) { + return { + state: state.base, + mode: base + } + }, + + blankLine(state: AnyObj) { + let baseToken + let overlayToken + if (base.blankLine) baseToken = base.blankLine(state.base) + if (overlay.blankLine) overlayToken = overlay.blankLine(state.overlay) + + return overlayToken == null + ? baseToken + : combine && baseToken != null + ? baseToken + ' ' + overlayToken + : overlayToken + } + } + } +} + +export default overlayMode diff --git a/packages/desktop/src/renderer/src/commands/descriptions.ts b/packages/desktop/src/renderer/src/commands/descriptions.ts new file mode 100644 index 0000000000..4e5c7a084c --- /dev/null +++ b/packages/desktop/src/renderer/src/commands/descriptions.ts @@ -0,0 +1,229 @@ +import { t } from '../i18n' + +const COMMAND_KEY_MAP: Record = { + // ============================================ + // # Application Level Commands + // ============================================ + 'mt.hide': 'commands.mt.hide', + 'mt.hide-others': 'commands.mt.hideOthers', + + // ============================================ + // # File Operations + // ============================================ + // File creation and opening + 'file.new-window': 'commands.file.newWindow', + 'file.new-tab': 'commands.file.newTab', + 'file.open-file': 'commands.file.openFile', + 'file.open-folder': 'commands.file.openFolder', + 'file.quick-open': 'commands.file.quickOpen', + 'file.import-file': 'commands.file.importFile', + + // File save and export + 'file.save': 'commands.file.save', + 'file.save-as': 'commands.file.saveAs', + 'file.export-file': 'commands.file.exportFile', + 'file.export-file.pdf': 'commands.file.exportFilePdf', + + // File management + 'file.move-file': 'commands.file.moveFile', + 'file.rename-file': 'commands.file.renameFile', + 'file.toggle-auto-save': 'commands.file.toggleAutoSave', + + // File settings + 'file.change-encoding': 'commands.file.changeEncoding', + 'file.line-ending': 'commands.file.changeLineEnding', + 'file.trailing-newline': 'commands.file.trailingNewline', + 'file.preferences': 'commands.file.preferences', + + // File operations + 'file.print': 'commands.file.print', + 'file.zoom': 'commands.file.zoom', + 'file.check-update': 'commands.file.checkUpdate', + + // File close + 'file.close': 'commands.file.closeTab', + 'file.close-tab': 'commands.file.closeTab', + 'file.close-window': 'commands.file.closeWindow', + 'file.quit': 'commands.file.quit', + + // ============================================ + // # Edit Operations + // ============================================ + // Undo and redo + 'edit.undo': 'commands.edit.undo', + 'edit.redo': 'commands.edit.redo', + + // Clipboard operations + 'edit.cut': 'commands.edit.cut', + 'edit.copy': 'commands.edit.copy', + 'edit.paste': 'commands.edit.paste', + 'edit.copy-as-rich': 'commands.edit.copyAsRich', + 'edit.copy-as-html': 'commands.edit.copyAsHtml', + 'edit.paste-as-plaintext': 'commands.edit.pasteAsPlaintext', + + // Select and copy + 'edit.select-all': 'commands.edit.selectAll', + 'edit.duplicate': 'commands.edit.duplicate', + + // Paragraph operations + 'edit.create-paragraph': 'commands.edit.createParagraph', + 'edit.delete-paragraph': 'commands.edit.deleteParagraph', + + // Find and replace + 'edit.find': 'commands.edit.find', + 'edit.find-next': 'commands.edit.findNext', + 'edit.find-previous': 'commands.edit.findPrevious', + 'edit.replace': 'commands.edit.replace', + 'edit.find-in-folder': 'commands.edit.findInFolder', + + // Other editing features + 'edit.screenshot': 'commands.edit.screenshot', + + // ============================================ + // # Paragraph Formatting + // ============================================ + // Heading levels + 'paragraph.heading-1': 'commands.paragraph.heading1', + 'paragraph.heading-2': 'commands.paragraph.heading2', + 'paragraph.heading-3': 'commands.paragraph.heading3', + 'paragraph.heading-4': 'commands.paragraph.heading4', + 'paragraph.heading-5': 'commands.paragraph.heading5', + 'paragraph.heading-6': 'commands.paragraph.heading6', + 'paragraph.upgrade-heading': 'commands.paragraph.upgradeHeading', + 'paragraph.degrade-heading': 'commands.paragraph.degradeHeading', + + // Block-level elements + 'paragraph.table': 'commands.paragraph.table', + 'paragraph.code-fence': 'commands.paragraph.codeFence', + 'paragraph.quote-block': 'commands.paragraph.quoteBlock', + 'paragraph.math-block': 'commands.paragraph.mathBlock', + 'paragraph.html-block': 'commands.paragraph.htmlBlock', + + // List types + 'paragraph.order-list': 'commands.paragraph.orderList', + 'paragraph.bullet-list': 'commands.paragraph.bulletList', + 'paragraph.task-list': 'commands.paragraph.taskList', + 'paragraph.loose-list-item': 'commands.paragraph.looseListItem', + + // Paragraph types + 'paragraph.paragraph': 'commands.paragraph.paragraph', + 'paragraph.reset-paragraph': 'commands.paragraph.resetParagraph', + + // Separators and special elements + 'paragraph.horizontal-rule': 'commands.paragraph.horizontalRule', + 'paragraph.horizontal-line': 'commands.paragraph.horizontalLine', + 'paragraph.math-formula': 'commands.paragraph.mathFormula', + 'paragraph.front-matter': 'commands.paragraph.frontMatter', + + // ============================================ + // # Text Formatting + // ============================================ + // Basic formatting + 'format.strong': 'commands.format.strong', + 'format.emphasis': 'commands.format.emphasis', + 'format.underline': 'commands.format.underline', + 'format.strike': 'commands.format.strike', + + // Advanced formatting + 'format.highlight': 'commands.format.highlight', + 'format.superscript': 'commands.format.superscript', + 'format.subscript': 'commands.format.subscript', + + // Inline elements + 'format.inline-code': 'commands.format.inlineCode', + 'format.inline-math': 'commands.format.inlineMath', + + // Links and media + 'format.hyperlink': 'commands.format.hyperlink', + 'format.image': 'commands.format.image', + + // Format clearing + 'format.clear-format': 'commands.format.clearFormat', + + // ============================================ + // # Window Management + // ============================================ + // Window controls + 'window.minimize': 'commands.window.minimize', + 'window.close': 'commands.window.close', + 'window.toggle-always-on-top': 'commands.window.toggleAlwaysOnTop', + 'window.toggle-full-screen': 'commands.window.toggleFullScreen', + + // Window zoom + 'window.zoomIn': 'commands.window.zoomIn', + 'window.zoomOut': 'commands.window.zoomOut', + + // Theme settings + 'window.change-theme': 'commands.window.changeTheme', + + // ============================================ + // # View Controls + // ============================================ + // UI toggles + 'view.toggle-sidebar': 'commands.view.toggleSidebar', + 'view.toggle-tabbar': 'commands.view.toggleTabbar', + 'view.toggle-toc': 'commands.view.toggleToc', + + // Edit modes + 'view.toggle-source-code-mode': 'commands.view.toggleSourceCodeMode', + 'view.source-code-mode': 'commands.view.sourceCodeMode', + 'view.toggle-typewriter-mode': 'commands.view.toggleTypewriterMode', + 'view.typewriter-mode': 'commands.view.typewriterMode', + 'view.toggle-focus-mode': 'commands.view.toggleFocusMode', + 'view.focus-mode': 'commands.view.focusMode', + + // View features + 'view.command-palette': 'commands.view.commandPalette', + 'view.actual-size': 'commands.view.actualSize', + 'view.text-direction': 'commands.view.textDirection', + + // Developer tools + 'view.dev-reload': 'commands.view.devReload', + 'view.dev-toggle-developer-tools': 'commands.view.devToggleDeveloperTools', + 'view.toggle-dev-tools': 'commands.view.toggleDevTools', + + // Menu items + 'view.reload-images': 'commands.view.reloadImages', + + // ============================================ + // # Tab Management + // ============================================ + // Tab switching + 'tabs.cycleBackward': 'commands.tabs.cycleBackward', + 'tabs.cycleForward': 'commands.tabs.cycleForward', + 'tabs.switchToLeft': 'commands.tabs.switchToLeft', + 'tabs.switchToRight': 'commands.tabs.switchToRight', + + // Switch tab by index + 'tabs.switchToFirst': 'commands.tabs.switchToFirst', + 'tabs.switchToSecond': 'commands.tabs.switchToSecond', + 'tabs.switchToThird': 'commands.tabs.switchToThird', + 'tabs.switchToFourth': 'commands.tabs.switchToFourth', + 'tabs.switchToFifth': 'commands.tabs.switchToFifth', + 'tabs.switchToSixth': 'commands.tabs.switchToSixth', + 'tabs.switchToSeventh': 'commands.tabs.switchToSeventh', + 'tabs.switchToEighth': 'commands.tabs.switchToEighth', + 'tabs.switchToNinth': 'commands.tabs.switchToNinth', + 'tabs.switchToTenth': 'commands.tabs.switchToTenth', + + // ============================================ + // # Documentation & Help + // ============================================ + 'docs.user-guide': 'commands.docs.userGuide', + 'docs.markdown-syntax': 'commands.docs.markdownSyntax', + + // ============================================ + // # Spell Checker + // ============================================ + 'spellchecker.switch-language': 'commands.spellchecker.switchLanguage' +} + +/** + * Gets the internationalized description text for the given command ID + * @param id - Command ID, in formats such as 'file.save', 'edit.copy', etc. + * @returns Returns the internationalized command description, or the original ID if no description is found + */ +export default (id: string): string => { + // Re-fetch the command description on each call to support dynamic language switching + return id in COMMAND_KEY_MAP ? t(COMMAND_KEY_MAP[id]) : id +} diff --git a/packages/desktop/src/renderer/src/commands/fileEncoding.ts b/packages/desktop/src/renderer/src/commands/fileEncoding.ts new file mode 100644 index 0000000000..fadcf12ffb --- /dev/null +++ b/packages/desktop/src/renderer/src/commands/fileEncoding.ts @@ -0,0 +1,95 @@ +import { ENCODING_NAME_MAP, getEncodingName, type Encoding } from 'common/encoding' +import { delay } from '@/util' +import bus from '../bus' +import getCommandDescriptionById from './descriptions' +import { t } from '../i18n' +import type { EditorState } from '@/store/editor' + +interface EncodingSubcommand { + id: string + description: string +} + +class FileEncodingCommand { + id: string + description: string + placeholder: string + subcommands: EncodingSubcommand[] + subcommandSelectedIndex: number + private _editorState: EditorState + + constructor(editorState: EditorState) { + this.id = 'file.change-encoding' + this.description = getCommandDescriptionById('file.change-encoding') + this.placeholder = t('commandPalette.placeholders.selectOption') + + this.subcommands = [] + this.subcommandSelectedIndex = -1 + + // Reference to editor state. + this._editorState = editorState + } + + run = async(): Promise => { + this.subcommands = [] + this.subcommandSelectedIndex = -1 + + // Load encoding from current tab to highlight it. + const encodingObj = this._getCurrentEncoding() + const { encoding, isBom } = encodingObj + + // NOTE: We support UTF-BOM encodings but don't allow to set them. + if (isBom) { + this.subcommandSelectedIndex = 0 + this.subcommands.push({ + id: `${encoding}-bom`, + description: `${getEncodingName(encodingObj)} - current` + }) + } + + let i = 0 + for (const [key, value] of Object.entries(ENCODING_NAME_MAP)) { + const isTabEncoding = !isBom && key === encoding + const item: EncodingSubcommand = { + id: key, + description: isTabEncoding ? `${value} - current` : value + } + if (isTabEncoding) { + // Highlight current encoding and set it as first entry. + this.subcommandSelectedIndex = i + this.subcommands.unshift(item) + } else { + this.subcommands.push(item) + } + ++i + } + } + + execute = async(): Promise => { + // Timeout to hide the command palette and then show again to prevent issues. + await delay(100) + bus.emit('show-command-palette', this) + } + + executeSubcommand = async(id: string): Promise => { + // NOTE: We support UTF-BOM encodings but don't allow to set them. + if (!id.endsWith('-bom')) { + bus.emit('mt::set-file-encoding', id) + } + } + + unload = (): void => { + this.subcommands = [] + } + + _getCurrentEncoding = (): Encoding => { + const { _editorState } = this + const { currentFile } = _editorState + if (currentFile) { + return currentFile.encoding as Encoding + } + return { encoding: '' } + } +} + +export default FileEncodingCommand diff --git a/packages/desktop/src/renderer/src/commands/index.ts b/packages/desktop/src/renderer/src/commands/index.ts new file mode 100644 index 0000000000..2528d60b7e --- /dev/null +++ b/packages/desktop/src/renderer/src/commands/index.ts @@ -0,0 +1,756 @@ +// List of all static commands that are loaded into command center. +import bus from '../bus' +import { delay, isOsx } from '@/util' +import { isUpdatable } from './utils' +import getCommandDescriptionById from './descriptions' +import { t } from '../i18n' + +export { default as FileEncodingCommand } from './fileEncoding' +export { default as LineEndingCommand } from './lineEnding' +export { default as QuickOpenCommand } from './quickOpen' +export { default as SpellcheckerLanguageCommand } from './spellcheckerLanguage' +export { default as TrailingNewlineCommand } from './trailingNewline' + +// Command shapes here are heterogeneous (some have `execute`, some have +// `subcommands` + `executeSubcommand`, some have `shortcut`, etc.). Mirrors +// the broad `CommandCallback = (...args: any[]) => any` style used in +// src/main/commands/index.ts. +export interface CommandSubcommand { + id: string + description?: string + value?: unknown + execute?: () => void | Promise +} + +export interface CommandDescriptor { + id: string + description?: string + shortcut?: string[] + subcommands?: CommandSubcommand[] + execute?: () => void | Promise + executeSubcommand?: (commandId: string, value?: unknown) => void | Promise +} + +export class RootCommand { + id: string + description: string + subcommands: CommandSubcommand[] + subcommandSelectedIndex: number + + constructor(subcommands: CommandSubcommand[] = []) { + this.id = '#' + this.description = '#' + this.subcommands = subcommands + this.subcommandSelectedIndex = -1 + } + + async run(): Promise {} + async unload(): Promise {} + + // Execute the command. + async execute(): Promise { + throw new Error('Root command.') + } +} + +const focusEditorAndExecute = (fn: () => void): void => { + setTimeout(() => bus.emit('editor-focus'), 10) + setTimeout(() => fn(), 150) +} + +const commands: CommandDescriptor[] = [ + // -------------------------------------------------------------------------- + // File + + { + id: 'file.new-tab', + execute: async() => { + bus.emit('mt::new-untitled-tab', { selected: true, markdown: '' }) + } + }, + { + id: 'file.new-window', + execute: async() => { + window.electron.ipcRenderer.send('mt::cmd-new-editor-window') + } + }, + { + id: 'file.open-file', + execute: async() => { + window.electron.ipcRenderer.send('mt::cmd-open-file') + } + }, + { + id: 'file.open-folder', + execute: async() => { + window.electron.ipcRenderer.send('mt::cmd-open-folder') + } + }, + { + id: 'file.save', + execute: async() => { + bus.emit('mt::editor-ask-file-save') + } + }, + { + id: 'file.save-as', + execute: async() => { + bus.emit('mt::editor-ask-file-save-as') + } + }, + { + id: 'file.print', + execute: async() => { + await delay(50) + bus.emit('showExportDialog', 'print') + } + }, + { + id: 'file.close-tab', + execute: async() => { + bus.emit('mt::editor-close-tab', null) + } + }, + { + id: 'file.close-window', + execute: async() => { + window.electron.ipcRenderer.send('mt::cmd-close-window') + } + }, + + { + id: 'file.toggle-auto-save', + execute: async() => { + window.electron.ipcRenderer.send('mt::cmd-toggle-autosave') + } + }, + { + id: 'file.move-file', + execute: async() => { + bus.emit('mt::editor-move-file', null) + } + }, + { + id: 'file.rename-file', + execute: async() => { + await delay(50) + bus.emit('mt::editor-rename-file', null) + } + }, + { + id: 'file.import-file', + execute: async() => { + window.electron.ipcRenderer.send('mt::cmd-import-file') + } + }, + { + id: 'file.export-file', + subcommands: [ + { + id: 'file.export-file-html', + description: 'Export as HTML', + execute: async() => { + await delay(50) + bus.emit('showExportDialog', 'styledHtml') + } + }, + { + id: 'file.export-file-pdf', + description: 'Export as PDF', + execute: async() => { + await delay(50) + bus.emit('showExportDialog', 'pdf') + } + } + ] + }, + + // -------------------------------------------------------------------------- + // Edit + + { + id: 'edit.undo', + execute: async() => { + focusEditorAndExecute(() => bus.emit('undo', 'undo')) + } + }, + { + id: 'edit.redo', + execute: async() => { + focusEditorAndExecute(() => bus.emit('redo', 'redo')) + } + }, + { + id: 'edit.duplicate', + execute: async() => { + focusEditorAndExecute(() => bus.emit('duplicate', 'duplicate')) + } + }, + { + id: 'edit.create-paragraph', + execute: async() => { + focusEditorAndExecute(() => bus.emit('createParagraph', 'createParagraph')) + } + }, + { + id: 'edit.delete-paragraph', + execute: async() => { + focusEditorAndExecute(() => bus.emit('deleteParagraph', 'deleteParagraph')) + } + }, + { + id: 'edit.find', + execute: async() => { + await delay(150) + bus.emit('find', 'find') + } + }, + // TODO: Find next/previous doesn't work. + // { + // id: 'edit.find-next', + // description: 'Edit: Find Next', + // execute: async () => { + // await delay(150) + // bus.emit('findNext', 'findNext') + // } + // }, { + // id: 'edit.find-previous', + // description: 'Edit: Find Previous', + // execute: async () => { + // await delay(150) + // bus.emit('findPrev', 'findPrev') + // } + // }, + { + id: 'edit.replace', + execute: async() => { + await delay(150) + bus.emit('replace', 'replace') + } + }, + { + id: 'edit.find-in-folder', + execute: async() => { + await delay(150) + bus.emit('mt::editor-edit-action', 'findInFolder') + } + }, + + // -------------------------------------------------------------------------- + // Paragraph + + { + id: 'paragraph.heading-1', + execute: async() => { + focusEditorAndExecute(() => bus.emit('paragraph', 'heading 1')) + } + }, + { + id: 'paragraph.heading-2', + execute: async() => { + focusEditorAndExecute(() => bus.emit('paragraph', 'heading 2')) + } + }, + { + id: 'paragraph.heading-3', + execute: async() => { + focusEditorAndExecute(() => bus.emit('paragraph', 'heading 3')) + } + }, + { + id: 'paragraph.heading-4', + execute: async() => { + focusEditorAndExecute(() => bus.emit('paragraph', 'heading 4')) + } + }, + { + id: 'paragraph.heading-5', + execute: async() => { + focusEditorAndExecute(() => bus.emit('paragraph', 'heading 5')) + } + }, + { + id: 'paragraph.heading-6', + execute: async() => { + focusEditorAndExecute(() => bus.emit('paragraph', 'heading 6')) + } + }, + { + id: 'paragraph.upgrade-heading', + execute: async() => { + focusEditorAndExecute(() => bus.emit('paragraph', 'upgrade heading')) + } + }, + { + id: 'paragraph.degrade-heading', + execute: async() => { + focusEditorAndExecute(() => bus.emit('paragraph', 'degrade heading')) + } + }, + { + id: 'paragraph.table', + execute: async() => { + focusEditorAndExecute(() => bus.emit('paragraph', 'table')) + } + }, + { + id: 'paragraph.code-fence', + execute: async() => { + focusEditorAndExecute(() => bus.emit('paragraph', 'pre')) + } + }, + { + id: 'paragraph.quote-block', + execute: async() => { + focusEditorAndExecute(() => bus.emit('paragraph', 'blockquote')) + } + }, + { + id: 'paragraph.math-formula', + execute: async() => { + focusEditorAndExecute(() => bus.emit('paragraph', 'mathblock')) + } + }, + { + id: 'paragraph.html-block', + execute: async() => { + focusEditorAndExecute(() => bus.emit('paragraph', 'html')) + } + }, + { + id: 'paragraph.order-list', + execute: async() => { + focusEditorAndExecute(() => bus.emit('paragraph', 'ol-bullet')) + } + }, + { + id: 'paragraph.bullet-list', + execute: async() => { + focusEditorAndExecute(() => bus.emit('paragraph', 'ul-bullet')) + } + }, + { + id: 'paragraph.task-list', + execute: async() => { + focusEditorAndExecute(() => bus.emit('paragraph', 'ul-task')) + } + }, + { + id: 'paragraph.loose-list-item', + execute: async() => { + focusEditorAndExecute(() => bus.emit('paragraph', 'loose-list-item')) + } + }, + { + id: 'paragraph.paragraph', + execute: async() => { + focusEditorAndExecute(() => bus.emit('paragraph', 'paragraph')) + } + }, + { + id: 'paragraph.reset-paragraph', + execute: async() => { + focusEditorAndExecute(() => bus.emit('paragraph', 'reset-to-paragraph')) + } + }, + { + id: 'paragraph.horizontal-line', + execute: async() => { + focusEditorAndExecute(() => bus.emit('paragraph', 'hr')) + } + }, + { + id: 'paragraph.front-matter', + execute: async() => { + focusEditorAndExecute(() => bus.emit('paragraph', 'front-matter')) + } + }, + + // -------------------------------------------------------------------------- + // Format + + // NOTE: Focus editor to restore selection and try to apply the commmand. + + { + id: 'format.strong', + execute: async() => { + focusEditorAndExecute(() => bus.emit('format', 'strong')) + } + }, + { + id: 'format.emphasis', + execute: async() => { + focusEditorAndExecute(() => bus.emit('format', 'em')) + } + }, + { + id: 'format.underline', + execute: async() => { + focusEditorAndExecute(() => bus.emit('format', 'u')) + } + }, + { + id: 'format.highlight', + execute: async() => { + focusEditorAndExecute(() => bus.emit('format', 'mark')) + } + }, + { + id: 'format.superscript', + execute: async() => { + focusEditorAndExecute(() => bus.emit('format', 'sup')) + } + }, + { + id: 'format.subscript', + execute: async() => { + focusEditorAndExecute(() => bus.emit('format', 'sub')) + } + }, + { + id: 'format.inline-code', + execute: async() => { + focusEditorAndExecute(() => bus.emit('format', 'inline_code')) + } + }, + { + id: 'format.inline-math', + execute: async() => { + focusEditorAndExecute(() => bus.emit('format', 'inline_math')) + } + }, + { + id: 'format.strike', + execute: async() => { + focusEditorAndExecute(() => bus.emit('format', 'del')) + } + }, + { + id: 'format.hyperlink', + execute: async() => { + focusEditorAndExecute(() => bus.emit('format', 'link')) + } + }, + { + id: 'format.image', + execute: async() => { + focusEditorAndExecute(() => bus.emit('format', 'image')) + } + }, + { + id: 'format.clear-format', + execute: async() => { + focusEditorAndExecute(() => bus.emit('format', 'clear')) + } + }, + + // -------------------------------------------------------------------------- + // Window + + { + id: 'window.minimize', + execute: async() => { + window.electron.windowControl.minimize() + } + }, + { + id: 'window.toggle-always-on-top', + execute: async() => { + window.electron.ipcRenderer.send('mt::window-toggle-always-on-top') + } + }, + { + id: 'window.toggle-full-screen', + execute: async() => { + window.electron.windowControl.toggleFullScreen() + } + }, + + { + id: 'file.zoom', + shortcut: [isOsx ? 'Cmd' : 'Ctrl', 'Scroll'], + subcommands: [ + { + id: 'file.zoom-0', + description: '62.5%', + value: 0.625 + }, + { + id: 'file.zoom-1', + description: '75%', + value: 0.75 + }, + { + id: 'file.zoom-2', + description: '87.5%', + value: 0.875 + }, + { + id: 'file.zoom-3', + description: '100%', + value: 1.0 + }, + { + id: 'file.zoom-4', + description: '112.5%', + value: 1.125 + }, + { + id: 'file.zoom-5', + description: '125%', + value: 1.25 + }, + { + id: 'file.zoom-6', + description: '137.5%', + value: 1.375 + }, + { + id: 'file.zoom-7', + description: '150%', + value: 1.5 + }, + { + id: 'file.zoom-8', + description: '162.5%', + value: 1.625 + }, + { + id: 'file.zoom-9', + description: '175%', + value: 1.75 + }, + { + id: 'file.zoom-10', + description: '187.5%', + value: 1.875 + }, + { + id: 'file.zoom-11', + description: '200%', + value: 2.0 + } + ], + executeSubcommand: async(_, value) => { + bus.emit('mt::window-zoom', value) + } + }, + + // -------------------------------------------------------------------------- + // Window + + { + id: 'window.change-theme', + subcommands: [ + { + id: 'window.change-theme-light', + description: 'Cadmium Light', + value: 'light' + }, + { + id: 'window.change-theme-dark', + description: 'Dark', + value: 'dark' + }, + { + id: 'window.change-theme-graphite', + description: 'Graphite', + value: 'graphite' + }, + { + id: 'window.change-theme-material-dark', + description: 'Material Dark', + value: 'material-dark' + }, + { + id: 'window.change-theme-one-dark', + description: 'One Dark', + value: 'one-dark' + }, + { + id: 'window.change-theme-ulysses', + description: 'Ulysses', + value: 'ulysses' + } + ], + executeSubcommand: async(_, theme) => { + window.electron.ipcRenderer.send('mt::set-user-preference', { theme }) + } + }, + + // -------------------------------------------------------------------------- + // View + + { + id: 'view.source-code-mode', + execute: async() => { + bus.emit('view:toggle-view-entry', 'sourceCode') + } + }, + { + id: 'view.typewriter-mode', + execute: async() => { + focusEditorAndExecute(() => bus.emit('view:toggle-view-entry', 'typewriter')) + } + }, + { + id: 'view.focus-mode', + execute: async() => { + focusEditorAndExecute(() => bus.emit('view:toggle-view-entry', 'focus')) + } + }, + { + id: 'view.toggle-sidebar', + execute: async() => { + bus.emit('view:toggle-layout-entry', 'showSideBar') + } + }, + { + id: 'view.toggle-tabbar', + execute: async() => { + bus.emit('view:toggle-layout-entry', 'showTabBar') + } + }, + + { + id: 'view.text-direction', + subcommands: [ + { + id: 'view.text-direction-ltr', + description: 'Left to Right', + value: 'ltr' + }, + { + id: 'view.text-direction-rtl', + description: 'Right to Left', + value: 'rtl' + } + ], + executeSubcommand: async(_, value) => { + window.electron.ipcRenderer.send('mt::set-user-preference', { textDirection: value }) + } + }, + + // -------------------------------------------------------------------------- + // MarkText + + { + id: 'file.preferences', + execute: async() => { + window.electron.ipcRenderer.send('mt::open-setting-window') + } + }, + { + id: 'file.quit', + execute: async() => { + window.electron.ipcRenderer.send('mt::app-try-quit') + } + }, + { + id: 'docs.user-guide', + execute: async() => { + window.electron.shell.openExternal( + 'https://marktext.me/docs/basics' + ) + } + }, + { + id: 'docs.markdown-syntax', + execute: async() => { + window.electron.shell.openExternal( + 'https://marktext.me/docs/markdown-syntax' + ) + } + }, + + // -------------------------------------------------------------------------- + // Misc + + { + id: 'tabs.cycleForward', + execute: async() => { + bus.emit('mt::tabs-cycle-right') + } + }, + { + id: 'tabs.cycleBackward', + execute: async() => { + bus.emit('mt::tabs-cycle-left') + } + } +] + +// -------------------------------------------------------------------------- +// etc + +if (isUpdatable()) { + commands.push({ + id: 'file.check-update', + description: getCommandDescriptionById('file.check-update'), + execute: async() => { + window.electron.ipcRenderer.send('mt::check-for-update') + } + }) +} + +if (isOsx) { + commands.push({ + id: 'edit.screenshot', + execute: async() => { + window.electron.ipcRenderer.send('mt::make-screenshot') + } + }) +} + +// Function to get commands with updated descriptions +export const getCommandsWithDescriptions = async(): Promise => { + // Update descriptions for all commands + const updateDescriptions = (commandList: Array): void => { + for (const item of commandList) { + const { id } = item + const subcommands = (item as CommandDescriptor).subcommands + // Always update description for commands with ID, regardless of existing description + if (id) { + item.description = getCommandDescriptionById(id) + } + + // Special handling for theme subcommands + if (id === 'window.change-theme' && subcommands && Array.isArray(subcommands)) { + for (const subcommand of subcommands) { + const { value } = subcommand + if (value === 'light') { + subcommand.description = t('menu.theme.cadmiumLight') + } else if (value === 'dark') { + subcommand.description = t('menu.theme.dark') + } else if (value === 'graphite') { + subcommand.description = t('menu.theme.graphiteLight') + } else if (value === 'material-dark') { + subcommand.description = t('menu.theme.materialDark') + } else if (value === 'one-dark') { + subcommand.description = t('menu.theme.oneDark') + } else if (value === 'ulysses') { + subcommand.description = t('menu.theme.ulyssesLight') + } + } + } + + // Also update other subcommands descriptions + if (subcommands && Array.isArray(subcommands)) { + updateDescriptions(subcommands) + } + } + } + + updateDescriptions(commands) + return commands +} + +// Complete all command descriptions for initial load. +for (const item of commands) { + const { id, description } = item + if (id && !description) { + item.description = getCommandDescriptionById(id) + } +} + +export default commands diff --git a/packages/desktop/src/renderer/src/commands/lineEnding.ts b/packages/desktop/src/renderer/src/commands/lineEnding.ts new file mode 100644 index 0000000000..a7b93f37af --- /dev/null +++ b/packages/desktop/src/renderer/src/commands/lineEnding.ts @@ -0,0 +1,75 @@ +import { delay } from '@/util' +import type { EditorState } from '@/store/editor' +import bus from '../bus' +import getCommandDescriptionById from './descriptions' +import { t } from '../i18n' + +const crlfDescription = 'Carriage return and line feed (CRLF)' +const lfDescription = 'Line feed (LF)' + +interface LineEndingSubcommand { + id: string + description: string + value: 'crlf' | 'lf' +} + +class LineEndingCommand { + id: string + description: string + placeholder: string + subcommands: LineEndingSubcommand[] + subcommandSelectedIndex: number + private _editorState: EditorState + + constructor(editorState: EditorState) { + this.id = 'file.line-ending' + this.description = getCommandDescriptionById('file.line-ending') + this.placeholder = t('commandPalette.placeholders.selectOption') + + this.subcommands = [ + { + id: 'file.line-ending-crlf', + description: crlfDescription, + value: 'crlf' + }, + { + id: 'file.line-ending-lf', + description: lfDescription, + value: 'lf' + } + ] + this.subcommandSelectedIndex = -1 + + // Reference to editor state. + this._editorState = editorState + } + + run = async(): Promise => { + const { currentFile } = this._editorState + if (!currentFile) return + const { lineEnding } = currentFile + if (lineEnding === 'crlf') { + this.subcommandSelectedIndex = 0 + this.subcommands[0].description = `${crlfDescription} - current` + this.subcommands[1].description = lfDescription + } else { + this.subcommandSelectedIndex = 1 + this.subcommands[0].description = crlfDescription + this.subcommands[1].description = `${lfDescription} - current` + } + } + + execute = async(): Promise => { + // Timeout to hide the command palette and then show again to prevent issues. + await delay(100) + bus.emit('show-command-palette', this) + } + + executeSubcommand = async(_: string, value: 'crlf' | 'lf'): Promise => { + bus.emit('mt::set-line-ending', value) + } + + unload = (): void => {} +} + +export default LineEndingCommand diff --git a/packages/desktop/src/renderer/src/commands/quickOpen.ts b/packages/desktop/src/renderer/src/commands/quickOpen.ts new file mode 100644 index 0000000000..55caf496d8 --- /dev/null +++ b/packages/desktop/src/renderer/src/commands/quickOpen.ts @@ -0,0 +1,230 @@ +import bus from '../bus' +import { delay } from '@/util' +import FileSearcher from '@/node/fileSearcher' +import type { EditorState } from '@/store/editor' +import getCommandDescriptionById from './descriptions' +import { t } from '../i18n' + +const SPECIAL_CHARS = /[\[\]\\^$.\|\?\*\+\(\)\/]{1}/g // eslint-disable-line no-useless-escape + +interface QuickOpenSubcommand { + id: string + description?: string + title?: string +} + +interface FolderState { + projectTree: { pathname: string } | null +} +type RootState = { editor: EditorState; project: FolderState; [key: string]: unknown } + +type CancelFn = (() => void) | null + +// The quick open command +class QuickOpenCommand { + id: string + description: string + placeholder: string + shortcut: string | null + subcommands: QuickOpenSubcommand[] + subcommandSelectedIndex: number + private _editorState: EditorState + private _folderState: FolderState + private _directorySearcher: FileSearcher + private _cancelFn: CancelFn + + constructor(rootState: RootState) { + this.id = 'file.quick-open' + this.description = getCommandDescriptionById('file.quick-open') + this.placeholder = t('commandPalette.placeholders.searchFileToOpen') + this.shortcut = null + + this.subcommands = [] + this.subcommandSelectedIndex = -1 + + // Reference to folder and editor and project state. + this._editorState = rootState.editor + this._folderState = rootState.project + + this._directorySearcher = new FileSearcher() + this._cancelFn = null + } + + search = async(query: string): Promise => { + // Show opened files when no query given. + if (!query) { + return this.subcommands + } + + const { _cancelFn } = this + if (_cancelFn) { + _cancelFn() + this._cancelFn = null + } + + const timeout = delay(300) + this._cancelFn = () => { + timeout.cancel() + this._cancelFn = null + } + + await timeout + return this._doSearch(query) + } + + run = async(): Promise => { + const { _editorState, _folderState } = this + if (!_folderState.projectTree && _editorState.tabs.length === 0) { + throw new Error(null as unknown as string) + } + + this.subcommands = _editorState.tabs + .map((tab) => tab.pathname) + // Filter untitled tabs + .filter((tabPath: string | null | undefined) => !!tabPath) + .map((pathname: string) => { + const item: QuickOpenSubcommand = { id: pathname } + Object.assign(item, this._getPath(pathname)) + return item + }) + } + + execute = async(): Promise => { + // Timeout to hide the command palette and then show again to prevent issues. + await delay(100) + bus.emit('show-command-palette', this) + } + + executeSubcommand = async(id: string): Promise => { + const { windowId } = window.marktext!.env! + window.electron.ipcRenderer.send('mt::open-file-by-window-id', windowId, id) + } + + unload = (): void => { + this.subcommands = [] + } + + // --- private ------------------------------------------ + + _doSearch = (query: string): QuickOpenSubcommand[] | Promise => { + this._cancelFn = null + const { _editorState, _folderState } = this + const isRootDirOpened = !!_folderState.projectTree + const tabsAvailable = _editorState.tabs.length > 0 + + // Only show opened files if no directory is opened. + if (!isRootDirOpened && !tabsAvailable) { + return [] + } + + const searchResult: string[] = [] + const rootPath: string | null = isRootDirOpened ? _folderState.projectTree!.pathname : null + + // Add files that are not in the current root directory but opened. + if (tabsAvailable) { + const re = new RegExp( + query.replace(SPECIAL_CHARS, (p) => { + if (p === '*') return '.*' + return p === '\\' ? '\\\\' : `\\${p}` + }), + 'i' + ) + + for (const tab of _editorState.tabs) { + const { pathname } = tab + if ( + pathname && + re.test(pathname) && + (!rootPath || !window.fileUtils.isChildOfDirectory(rootPath, pathname)) + ) { + searchResult.push(pathname) + } + } + } + + if (!isRootDirOpened) { + return searchResult.map((pathname) => { + return { + id: pathname, + description: pathname, + title: pathname + } + }) + } + + // Search root directory on disk. + return new Promise((resolve, reject) => { + let canceled = false + const promises: Promise & { cancel?: () => void } = this._directorySearcher + .search([rootPath!], '', { + didMatch: (result: unknown) => { + if (canceled) return + searchResult.push(result as string) + }, + didSearchPaths: (numPathsFound: unknown) => { + // Cancel when more than 30 files were found. User should specify the search query. + if (!canceled && (numPathsFound as number) > 30) { + canceled = true + if (promises.cancel) { + promises.cancel() + } + } + }, + + // Only search markdown files that contain the query string. + inclusions: this._getInclusions(query) + }) + .then(() => { + this._cancelFn = null + resolve( + searchResult.map((pathname) => { + const item: QuickOpenSubcommand = { id: pathname } + Object.assign(item, this._getPath(pathname)) + return item + }) + ) + }) + .catch((error: unknown) => { + this._cancelFn = null + reject(error) + }) + + this._cancelFn = () => { + this._cancelFn = null + canceled = true + if (promises.cancel) { + promises.cancel() + } + } + }) + } + + _getInclusions = (query: string): string[] => { + // NOTE: This will fail on `foo.m` because we search for `foo.m.md`. + if (window.fileUtils.hasMarkdownExtension(query)) { + return [`*${query}`] + } + + const inclusions: string[] = [] + for (let i = 0; i < window.fileUtils.MARKDOWN_INCLUSIONS.length; ++i) { + inclusions[i] = `*${query}` + window.fileUtils.MARKDOWN_INCLUSIONS[i] + } + return inclusions + } + + _getPath = (pathname: string): { title?: string; description: string } => { + const rootPath: string = this._folderState.projectTree!.pathname + if (!window.fileUtils.isChildOfDirectory(rootPath, pathname)) { + return { title: pathname, description: pathname } + } + + const p = window.path.relative(rootPath, pathname) + const item: { title?: string; description: string } = { description: p } + if (p.length > 50) { + item.title = p + } + return item + } +} + +export default QuickOpenCommand diff --git a/packages/desktop/src/renderer/src/commands/spellcheckerLanguage.ts b/packages/desktop/src/renderer/src/commands/spellcheckerLanguage.ts new file mode 100644 index 0000000000..a4bd6aff4f --- /dev/null +++ b/packages/desktop/src/renderer/src/commands/spellcheckerLanguage.ts @@ -0,0 +1,80 @@ +import bus from '../bus' +import notice from '@/services/notification' +import { delay } from '@/util' +import { SpellChecker } from '@/spellchecker' +import { getLanguageName } from '@/spellchecker/languageMap' +import getCommandDescriptionById from './descriptions' +import { t } from '../i18n' + +interface SpellcheckerSubcommand { + id: string + // getLanguageName() can return null for unknown locales. + description: string | null + value: string +} + +// Command to switch the spellchecker language +class SpellcheckerLanguageCommand { + id: string + description: string + placeholder: string + shortcut: string | null + spellchecker: SpellChecker + subcommands: SpellcheckerSubcommand[] + subcommandSelectedIndex: number + + constructor(spellchecker: SpellChecker) { + this.id = 'spellchecker.switch-language' + this.description = getCommandDescriptionById('spellchecker.switch-language') + this.placeholder = t('commandPalette.placeholders.selectLanguage') + this.shortcut = null + + this.spellchecker = spellchecker + + this.subcommands = [] + this.subcommandSelectedIndex = -1 + } + + run = async(): Promise => { + const langs = await SpellChecker.getAvailableDictionaries() + + const finalLangs: string[] = langs.length > 0 ? langs : ['en-US'] + + this.subcommands = finalLangs.map((lang) => { + return { + id: `spellchecker.switch-language-id-${lang}`, + description: getLanguageName(lang), + value: lang + } + }) + const currentLanguage = this.spellchecker.lang + this.subcommandSelectedIndex = this.subcommands.findIndex( + (cmd) => cmd.value === currentLanguage + ) + } + + execute = async(): Promise => { + // Timeout to hide the command palette and then show again to prevent issues. + await delay(100) + bus.emit('show-command-palette', this) + } + + executeSubcommand = async(id: string): Promise => { + const command = this.subcommands.find((cmd) => cmd.id === id) + if (this.spellchecker.isEnabled) { + bus.emit('switch-spellchecker-language', command?.value) + } else { + notice.notify({ + title: 'Spelling', + type: 'warning', + message: 'Cannot change language because spellchecker is disabled.' + }) + } + } + + unload = (): void => { + this.subcommands = [] + } +} + +export default SpellcheckerLanguageCommand diff --git a/packages/desktop/src/renderer/src/commands/trailingNewline.ts b/packages/desktop/src/renderer/src/commands/trailingNewline.ts new file mode 100644 index 0000000000..0916d360bc --- /dev/null +++ b/packages/desktop/src/renderer/src/commands/trailingNewline.ts @@ -0,0 +1,77 @@ +import { delay } from '@/util' +import type { EditorState } from '@/store/editor' +import bus from '../bus' +import getCommandDescriptionById from './descriptions' +import { t } from '../i18n' + +const descriptions = ['Trim all trailing newlines', 'Ensure single newline', 'Disabled'] + +interface TrailingNewlineSubcommand { + id: string + description: string + value: number +} + +class TrailingNewlineCommand { + id: string + description: string + placeholder: string + subcommands: TrailingNewlineSubcommand[] + subcommandSelectedIndex: number + private _editorState: EditorState + + constructor(editorState: EditorState) { + this.id = 'file.trailing-newline' + this.description = getCommandDescriptionById('file.trailing-newline') + this.placeholder = t('commandPalette.placeholders.selectOption') + this.subcommands = [] + this.subcommandSelectedIndex = -1 + + // Reference to editor state. + this._editorState = editorState + } + + run = async(): Promise => { + const { currentFile } = this._editorState + if (!currentFile) return + const { trimTrailingNewline } = currentFile + let index: number = trimTrailingNewline + if (index !== 0 && index !== 1) { + index = 2 + } + + this.subcommands = [ + { + id: 'file.trailing-newline-trim', + description: descriptions[0], + value: 0 + }, + { + id: 'file.trailing-newline-single', + description: descriptions[1], + value: 1 + }, + { + id: 'file.trailing-newline-disabled', + description: descriptions[2], + value: 3 + } + ] + this.subcommands[index].description = `${descriptions[index]} - current` + this.subcommandSelectedIndex = index + } + + execute = async(): Promise => { + // Timeout to hide the command palette and then show again to prevent issues. + await delay(100) + bus.emit('show-command-palette', this) + } + + executeSubcommand = async(_: string, value: number): Promise => { + bus.emit('mt::set-final-newline', value) + } + + unload = (): void => {} +} + +export default TrailingNewlineCommand diff --git a/packages/desktop/src/renderer/src/commands/utils.ts b/packages/desktop/src/renderer/src/commands/utils.ts new file mode 100644 index 0000000000..cc6a97e4fd --- /dev/null +++ b/packages/desktop/src/renderer/src/commands/utils.ts @@ -0,0 +1,6 @@ +/// Check whether the package is updatable at runtime. +// The flag is computed once by main on startup (see boot info) so callers can +// read it synchronously during command-center initialization without a race. +export const isUpdatable = (): boolean => { + return !!(window.electron && window.electron.isUpdatable) +} diff --git a/packages/desktop/src/renderer/src/components/about/index.vue b/packages/desktop/src/renderer/src/components/about/index.vue new file mode 100644 index 0000000000..29d99c1938 --- /dev/null +++ b/packages/desktop/src/renderer/src/components/about/index.vue @@ -0,0 +1,99 @@ + + + + + diff --git a/packages/desktop/src/renderer/src/components/commandPalette/index.vue b/packages/desktop/src/renderer/src/components/commandPalette/index.vue new file mode 100644 index 0000000000..eafd77a557 --- /dev/null +++ b/packages/desktop/src/renderer/src/components/commandPalette/index.vue @@ -0,0 +1,487 @@ + + + + + + diff --git a/packages/desktop/src/renderer/src/components/editorWithTabs/editor.vue b/packages/desktop/src/renderer/src/components/editorWithTabs/editor.vue new file mode 100644 index 0000000000..6d2999ba17 --- /dev/null +++ b/packages/desktop/src/renderer/src/components/editorWithTabs/editor.vue @@ -0,0 +1,2133 @@ + + + + + diff --git a/packages/desktop/src/renderer/src/components/editorWithTabs/index.vue b/packages/desktop/src/renderer/src/components/editorWithTabs/index.vue new file mode 100644 index 0000000000..3bd465eb41 --- /dev/null +++ b/packages/desktop/src/renderer/src/components/editorWithTabs/index.vue @@ -0,0 +1,64 @@ + + + + + diff --git a/packages/desktop/src/renderer/src/components/editorWithTabs/notifications.vue b/packages/desktop/src/renderer/src/components/editorWithTabs/notifications.vue new file mode 100644 index 0000000000..dfa0b00e21 --- /dev/null +++ b/packages/desktop/src/renderer/src/components/editorWithTabs/notifications.vue @@ -0,0 +1,124 @@ + + + + + diff --git a/packages/desktop/src/renderer/src/components/editorWithTabs/sourceCode.vue b/packages/desktop/src/renderer/src/components/editorWithTabs/sourceCode.vue new file mode 100644 index 0000000000..c685c33969 --- /dev/null +++ b/packages/desktop/src/renderer/src/components/editorWithTabs/sourceCode.vue @@ -0,0 +1,438 @@ + + + + + diff --git a/packages/desktop/src/renderer/src/components/editorWithTabs/syntheticHistory.ts b/packages/desktop/src/renderer/src/components/editorWithTabs/syntheticHistory.ts new file mode 100644 index 0000000000..344f86b192 --- /dev/null +++ b/packages/desktop/src/renderer/src/components/editorWithTabs/syntheticHistory.ts @@ -0,0 +1,108 @@ +// Synthetic save-tracking history for the WYSIWYG engine. +// +// The `@muyajs/core` engine's undo/redo history (`getHistory()`) has a +// different shape than the desktop store's `tab.history` (which drives the +// save/dirty indicator). The renderer therefore feeds the store a SYNTHETIC, +// desktop-shaped history whose single stack entry carries an `id` the store +// compares against `tab.lastSavedHistoryId`: when the current id equals the +// saved id the tab is shown as clean, otherwise dirty. +// +// The id MUST satisfy two properties: +// 1. clean <=> the live document content matches the saved-on-disk content. +// 2. monotonic & never-reused: a divergent edit can never collide with the +// saved id, even when it returns the engine undo-stack to the same DEPTH. +// +// An earlier implementation used the undo-stack DEPTH as the id. Depth is +// REUSED across distinct documents at the same stack height, so the sequence +// type A, type B, save, undo B, type C (a NEW divergent edit) +// returned the depth — and therefore the id — to the saved value while the +// document was actually A+C, falsely showing the tab as clean and risking +// data loss on close-without-save (Phase G — G6). +// +// Legacy muyajs avoided this by assigning every pushed history entry a +// MONOTONIC, never-reused id and splicing the redo tail on a new edit. We +// reproduce the exact semantics here, but keyed on the live document content +// rather than on the engine's internal stack objects (the engine regenerates +// its undo operations via invert on every undo/redo, so object identity is not +// stable). Each DISTINCT content snapshot is assigned a fresh monotonic id the +// first time it is seen; revisiting an earlier snapshot (undo/redo back to it) +// reuses that snapshot's original id. Undoing back to the saved content +// therefore restores the saved id (clean); a divergent re-edit produces brand +// new content and hence a brand new id (dirty) — never the saved one. + +// Trailing newlines are NOT meaningful content for save/dirty tracking — the +// store itself normalizes them via `adjustTrailingNewlines`/`trimTrailingNewline` +// before saving. The engine's markdown serialization is also unstable across a +// `setContent` -> edit -> undo round-trip purely in trailing newlines (loading +// `'x\n'` may serialize to `'x\n\n\n'`, while undoing an edit lands on `'x\n'`), +// so the content signature must ignore them or undo-to-saved would never match. +const stripTrailingNewlines = (content: string): string => + content.replace(/[\r\n]+$/, '') + +// A fast, stable 64-bit string hash (FNV-1a) over the trailing-newline-normalized +// content. Used so the content -> id map stores short keys instead of whole +// documents; a collision would map two genuinely different documents to the same +// id and could reintroduce the false-clean it guards against. 64 bits keeps the +// collision probability negligible even for a long editing session with many +// thousands of distinct snapshots (a 32-bit hash hits ~50% collision odds near +// ~77k snapshots via the birthday bound — realistic over a long session — so the +// extra width is worth the BigInt key). +const FNV64_OFFSET = 0xcbf29ce484222325n +const FNV64_PRIME = 0x100000001b3n +const MASK64 = 0xffffffffffffffffn +const hashContent = (content: string): bigint => { + const normalized = stripTrailingNewlines(content) + let hash = FNV64_OFFSET + for (let i = 0; i < normalized.length; i++) { + hash ^= BigInt(normalized.charCodeAt(i)) + hash = (hash * FNV64_PRIME) & MASK64 + } + return hash +} + +export interface IFileHistoryLike { + stack: Array<{ id: number | string; [key: string]: unknown }> + index: number + lastEditIndex: number + lastInitIndex: number +} + +// Per-tab monotonic id allocator. Created once when a tab's content baseline is +// established and reset whenever the engine reloads the document (`setContent` +// clears the engine history), so the baseline id is always 0 — matching the +// store's seeded `lastSavedHistoryId: 0` for a freshly loaded/clean document. +export class SyntheticHistory { + private counter = 0 + private readonly idByContent = new Map() + + constructor(baselineContent: string = '') { + // The freshly-loaded document is its own clean baseline; the store seeds + // `lastSavedHistoryId` to 0, so the baseline content must map to id 0. + this.idByContent.set(hashContent(baselineContent), 0) + } + + // Return the monotonic id for the given content snapshot, assigning a fresh, + // never-reused id the first time a snapshot is seen and reusing the original + // id on every subsequent revisit (undo/redo back to it). + idFor(content: string): number { + const key = hashContent(content) + let id = this.idByContent.get(key) + if (id === undefined) { + id = ++this.counter + this.idByContent.set(key, id) + } + return id + } + + // Build the desktop-shaped synthetic history the store consumes. The store + // only reads `stack[lastEditIndex].id`; the remaining fields keep its + // bookkeeping happy (a single committed edit at index 0). + build(content: string): IFileHistoryLike { + return { + stack: [{ id: this.idFor(content) }], + index: 0, + lastEditIndex: 0, + lastInitIndex: -1 + } + } +} diff --git a/packages/desktop/src/renderer/src/components/editorWithTabs/tabs.vue b/packages/desktop/src/renderer/src/components/editorWithTabs/tabs.vue new file mode 100644 index 0000000000..787daceedf --- /dev/null +++ b/packages/desktop/src/renderer/src/components/editorWithTabs/tabs.vue @@ -0,0 +1,423 @@ + + + + + diff --git a/packages/desktop/src/renderer/src/components/exportSettings/exportOptions.ts b/packages/desktop/src/renderer/src/components/exportSettings/exportOptions.ts new file mode 100644 index 0000000000..70fa9e4b7a --- /dev/null +++ b/packages/desktop/src/renderer/src/components/exportSettings/exportOptions.ts @@ -0,0 +1,84 @@ +import { t } from '@/i18n' +import type { PrefSelectOption } from '@/prefComponents/common/types' + +export const getPageSizeList = (): PrefSelectOption[] => [ + { + label: t('exportSettings.options.pageSizes.a3'), + value: 'A3' + }, + { + label: t('exportSettings.options.pageSizes.a4'), + value: 'A4' + }, + { + label: t('exportSettings.options.pageSizes.a5'), + value: 'A5' + }, + { + label: t('exportSettings.options.pageSizes.legal'), + value: 'Legal' + }, + { + label: t('exportSettings.options.pageSizes.letter'), + value: 'Letter' + }, + { + label: t('exportSettings.options.pageSizes.tabloid'), + value: 'Tabloid' + }, + { + label: t('exportSettings.options.pageSizes.custom'), + value: 'custom' + } +] + +export const getHeaderFooterTypes = (): PrefSelectOption[] => [ + { + label: t('exportSettings.options.headerFooterTypes.none'), + value: 0 + }, + { + label: t('exportSettings.options.headerFooterTypes.singleCell'), + value: 1 + }, + { + label: t('exportSettings.options.headerFooterTypes.threeCells'), + value: 2 + } +] + +export const getHeaderFooterStyles = (): PrefSelectOption[] => [ + { + label: t('exportSettings.options.headerFooterStyles.default'), + value: 0 + }, + { + label: t('exportSettings.options.headerFooterStyles.simple'), + value: 1 + }, + { + label: t('exportSettings.options.headerFooterStyles.styled'), + value: 2 + } +] + +export const getExportThemeList = (): PrefSelectOption[] => [ + { + label: t('exportSettings.options.themes.academic'), + value: 'academic' + }, + { + label: t('exportSettings.options.themes.default'), + value: 'default' + }, + { + label: t('exportSettings.options.themes.liber'), + value: 'liber' + } +] + +// Retained for backward compatibility +export const pageSizeList = getPageSizeList() +export const headerFooterTypes = getHeaderFooterTypes() +export const headerFooterStyles = getHeaderFooterStyles() +export const exportThemeList = getExportThemeList() diff --git a/packages/desktop/src/renderer/src/components/exportSettings/index.vue b/packages/desktop/src/renderer/src/components/exportSettings/index.vue new file mode 100644 index 0000000000..9bb218e557 --- /dev/null +++ b/packages/desktop/src/renderer/src/components/exportSettings/index.vue @@ -0,0 +1,618 @@ + + + + + + diff --git a/packages/desktop/src/renderer/src/components/exportSettings/persistence.ts b/packages/desktop/src/renderer/src/components/exportSettings/persistence.ts new file mode 100644 index 0000000000..2e44a9e53e --- /dev/null +++ b/packages/desktop/src/renderer/src/components/exportSettings/persistence.ts @@ -0,0 +1,27 @@ +// Persists the export dialog's options across sessions (#2287). The dialog's +// options were component-local refs with hardcoded defaults, so every restart +// (and every dialog open) reset them. We store the chosen values in +// localStorage — the same renderer-side persistence the sidebar width uses — +// and restore them when the dialog opens. + +export const EXPORT_SETTINGS_STORAGE_KEY = 'export-settings' + +export function loadExportSettings(): Record { + try { + const raw = localStorage.getItem(EXPORT_SETTINGS_STORAGE_KEY) + if (!raw) return {} + const parsed = JSON.parse(raw) + return parsed && typeof parsed === 'object' ? (parsed as Record) : {} + } catch { + return {} + } +} + +export function saveExportSettings(settings: Record): void { + try { + localStorage.setItem(EXPORT_SETTINGS_STORAGE_KEY, JSON.stringify(settings)) + } catch { + // Storage can be unavailable (private mode / quota); persisting export + // options is best-effort and must never break the export flow. + } +} diff --git a/packages/desktop/src/renderer/src/components/icons/LinkIcon.vue b/packages/desktop/src/renderer/src/components/icons/LinkIcon.vue new file mode 100644 index 0000000000..1ba4656c21 --- /dev/null +++ b/packages/desktop/src/renderer/src/components/icons/LinkIcon.vue @@ -0,0 +1,21 @@ + + + diff --git a/packages/desktop/src/renderer/src/components/import/index.vue b/packages/desktop/src/renderer/src/components/import/index.vue new file mode 100644 index 0000000000..fea428bab3 --- /dev/null +++ b/packages/desktop/src/renderer/src/components/import/index.vue @@ -0,0 +1,136 @@ + + + + + diff --git a/packages/desktop/src/renderer/src/components/loading/index.vue b/packages/desktop/src/renderer/src/components/loading/index.vue new file mode 100644 index 0000000000..fb42617b11 --- /dev/null +++ b/packages/desktop/src/renderer/src/components/loading/index.vue @@ -0,0 +1,95 @@ + + + + + diff --git a/packages/desktop/src/renderer/src/components/recent/index.vue b/packages/desktop/src/renderer/src/components/recent/index.vue new file mode 100644 index 0000000000..9d87c53ee6 --- /dev/null +++ b/packages/desktop/src/renderer/src/components/recent/index.vue @@ -0,0 +1,55 @@ + + + + + diff --git a/packages/desktop/src/renderer/src/components/rename/index.vue b/packages/desktop/src/renderer/src/components/rename/index.vue new file mode 100644 index 0000000000..2e00e53488 --- /dev/null +++ b/packages/desktop/src/renderer/src/components/rename/index.vue @@ -0,0 +1,128 @@ + + + + + + + diff --git a/packages/desktop/src/renderer/src/components/search/index.vue b/packages/desktop/src/renderer/src/components/search/index.vue new file mode 100644 index 0000000000..a9f5d5624c --- /dev/null +++ b/packages/desktop/src/renderer/src/components/search/index.vue @@ -0,0 +1,512 @@ + + + + + diff --git a/packages/desktop/src/renderer/src/components/sideBar/help.ts b/packages/desktop/src/renderer/src/components/sideBar/help.ts new file mode 100644 index 0000000000..d538828f59 --- /dev/null +++ b/packages/desktop/src/renderer/src/components/sideBar/help.ts @@ -0,0 +1,39 @@ +import { + Folder as FilesIcon, + Search as SearchIcon, + Memo as TocIcon, + Setting as SettingIcon +} from '@element-plus/icons-vue' +import { t } from '@/i18n' + +export interface SideBarIconEntry { + id: string + name: () => string + icon: unknown +} + +export const sideBarIcons: SideBarIconEntry[] = [ + { + id: 'files', + name: () => t('sideBar.icons.files'), + icon: FilesIcon + }, + { + id: 'search', + name: () => t('sideBar.icons.search'), + icon: SearchIcon + }, + { + id: 'toc', + name: () => t('sideBar.icons.toc'), + icon: TocIcon + } +] + +export const sideBarBottomIcons: SideBarIconEntry[] = [ + { + id: 'settings', + name: () => t('sideBar.icons.settings'), + icon: SettingIcon + } +] diff --git a/packages/desktop/src/renderer/src/components/sideBar/icon.vue b/packages/desktop/src/renderer/src/components/sideBar/icon.vue new file mode 100644 index 0000000000..76efa467bb --- /dev/null +++ b/packages/desktop/src/renderer/src/components/sideBar/icon.vue @@ -0,0 +1,43 @@ + + + + + diff --git a/packages/desktop/src/renderer/src/components/sideBar/index.vue b/packages/desktop/src/renderer/src/components/sideBar/index.vue new file mode 100644 index 0000000000..753c080d59 --- /dev/null +++ b/packages/desktop/src/renderer/src/components/sideBar/index.vue @@ -0,0 +1,229 @@ + + + + + diff --git a/packages/desktop/src/renderer/src/components/sideBar/search.vue b/packages/desktop/src/renderer/src/components/sideBar/search.vue new file mode 100644 index 0000000000..725e746387 --- /dev/null +++ b/packages/desktop/src/renderer/src/components/sideBar/search.vue @@ -0,0 +1,452 @@ + + + + + diff --git a/packages/desktop/src/renderer/src/components/sideBar/searchResultItem.vue b/packages/desktop/src/renderer/src/components/sideBar/searchResultItem.vue new file mode 100644 index 0000000000..5bc773a72c --- /dev/null +++ b/packages/desktop/src/renderer/src/components/sideBar/searchResultItem.vue @@ -0,0 +1,283 @@ + + + + + diff --git a/packages/desktop/src/renderer/src/components/sideBar/toc.vue b/packages/desktop/src/renderer/src/components/sideBar/toc.vue new file mode 100644 index 0000000000..28601706cf --- /dev/null +++ b/packages/desktop/src/renderer/src/components/sideBar/toc.vue @@ -0,0 +1,147 @@ + + + + + diff --git a/packages/desktop/src/renderer/src/components/sideBar/tree.vue b/packages/desktop/src/renderer/src/components/sideBar/tree.vue new file mode 100644 index 0000000000..0be7487e18 --- /dev/null +++ b/packages/desktop/src/renderer/src/components/sideBar/tree.vue @@ -0,0 +1,485 @@ + + + + + diff --git a/packages/desktop/src/renderer/src/components/sideBar/treeFile.vue b/packages/desktop/src/renderer/src/components/sideBar/treeFile.vue new file mode 100644 index 0000000000..d7a3bb3a1c --- /dev/null +++ b/packages/desktop/src/renderer/src/components/sideBar/treeFile.vue @@ -0,0 +1,149 @@ + + + + + diff --git a/packages/desktop/src/renderer/src/components/sideBar/treeFolder.vue b/packages/desktop/src/renderer/src/components/sideBar/treeFolder.vue new file mode 100644 index 0000000000..422d4f1433 --- /dev/null +++ b/packages/desktop/src/renderer/src/components/sideBar/treeFolder.vue @@ -0,0 +1,183 @@ + + + + + diff --git a/packages/desktop/src/renderer/src/components/sideBar/treeOpenedTab.vue b/packages/desktop/src/renderer/src/components/sideBar/treeOpenedTab.vue new file mode 100644 index 0000000000..c175963dfb --- /dev/null +++ b/packages/desktop/src/renderer/src/components/sideBar/treeOpenedTab.vue @@ -0,0 +1,93 @@ + + + + + diff --git a/packages/desktop/src/renderer/src/components/sideBar/types.ts b/packages/desktop/src/renderer/src/components/sideBar/types.ts new file mode 100644 index 0000000000..e1301e9466 --- /dev/null +++ b/packages/desktop/src/renderer/src/components/sideBar/types.ts @@ -0,0 +1,62 @@ +// Shared shapes for the sideBar component group. +// +// These types are intentionally narrow approximations of the runtime payloads +// produced by the project store (treeCtrl mutations), the ripgrep searcher, +// and the editor TOC builder. They cover the renderer surfaces touched by the +// sideBar SFCs; deeper invariants live alongside the producers. + +import type { IFileState } from '@shared/types/files' + +// --------------------------------------------------------------------------- +// File tree (sideBar/tree.vue, treeFile.vue, treeFolder.vue, project store) +// --------------------------------------------------------------------------- + +export interface TreeFileNode { + id?: string + pathname: string + name: string + birthTime?: number | Date + isDirectory: false + isFile: true + isMarkdown: boolean +} + +export interface TreeFolderNode { + id?: string + pathname: string + name: string + isCollapsed?: boolean + isDirectory: true + isFile: false + isMarkdown: false + folders: TreeFolderNode[] + files: TreeFileNode[] +} + +// The root project tree — same shape as TreeFolderNode in practice, exposed +// under a friendlier alias so SFCs can spell their intent. +export type TreeNode = TreeFolderNode + +// --------------------------------------------------------------------------- +// Search (sideBar/search.vue, searchResultItem.vue) +// --------------------------------------------------------------------------- + +// Range tuple emitted by the ripgrep bridge: [[startLine, startCh], [endLine, endCh]] +export type SearchRange = [[number, number], [number, number]] + +export interface SearchMatch { + lineText: string + range: SearchRange +} + +export interface SearchResult { + filePath: string + matches: SearchMatch[] +} + +// --------------------------------------------------------------------------- +// Tab descriptor (sideBar/treeOpenedTab.vue) +// --------------------------------------------------------------------------- + +// The sideBar consumes the same per-tab state shape as the editor store. +export type TabDescriptor = IFileState diff --git a/packages/desktop/src/renderer/src/components/titleBar/index.vue b/packages/desktop/src/renderer/src/components/titleBar/index.vue new file mode 100644 index 0000000000..57498ec1f3 --- /dev/null +++ b/packages/desktop/src/renderer/src/components/titleBar/index.vue @@ -0,0 +1,498 @@ + + + + + + + diff --git a/packages/desktop/src/renderer/src/components/titleBar/visibility.ts b/packages/desktop/src/renderer/src/components/titleBar/visibility.ts new file mode 100644 index 0000000000..0e8940a0bb --- /dev/null +++ b/packages/desktop/src/renderer/src/components/titleBar/visibility.ts @@ -0,0 +1,3 @@ +export function shouldShowInAppTitleBar(titleBarStyle: string, isOsx: boolean): boolean { + return titleBarStyle !== 'native' || isOsx +} diff --git a/packages/desktop/src/renderer/src/config.ts b/packages/desktop/src/renderer/src/config.ts new file mode 100644 index 0000000000..d89bccc1d1 --- /dev/null +++ b/packages/desktop/src/renderer/src/config.ts @@ -0,0 +1,17 @@ +export const PATH_SEPARATOR: string = window.path.sep + +export const THEME_STYLE_ID = 'ag-theme' +export const COMMON_STYLE_ID = 'ag-common-style' + +export const DEFAULT_EDITOR_FONT_FAMILY = + '"Open Sans", "Clear Sans", "Helvetica Neue", Helvetica, Arial, sans-serif, Segoe UI Emoji, Apple Color Emoji, "Noto Color Emoji"' +export const DEFAULT_CODE_FONT_FAMILY = + '"DejaVu Sans Mono", "Source Code Pro", "Droid Sans Mono", monospace' +export const DEFAULT_STYLE = Object.freeze({ + codeFontFamily: DEFAULT_CODE_FONT_FAMILY, + codeFontSize: '14px', + hideScrollbar: false, + theme: 'light' +}) + +export { railscastsThemes, oneDarkThemes } from '../../common/theme' diff --git a/packages/desktop/src/renderer/src/contextMenu/popupMenu.ts b/packages/desktop/src/renderer/src/contextMenu/popupMenu.ts new file mode 100644 index 0000000000..cbcff354a2 --- /dev/null +++ b/packages/desktop/src/renderer/src/contextMenu/popupMenu.ts @@ -0,0 +1,92 @@ +// Renderer-side helper that turns an array of menu descriptors into a +// serializable template, ships them to the main process to popup an Electron +// menu, and dispatches click events back to per-item click handlers. + +import type { MenuTemplate, MenuTemplateItem, MenuPopupPosition } from '@shared/types/menu' + +// Renderer-side click handler. Receives the optional payload supplied when the +// popup was opened (e.g. a tab id). +export type MenuClickHandler = (payload: unknown) => void + +export interface ContextMenuItem { + id?: string + label?: string + // Loose `string` here lets the menu factories emit plain `'separator'` + // literals without `as const`. The serializer only special-cases 'separator'. + type?: string + accelerator?: string + enabled?: boolean + checked?: boolean + submenu?: ContextMenuItem[] + click?: (...args: unknown[]) => void +} + +let nextId = 1 +const nextItemId = (): string => `mi-${nextId++}` + +const serialize = ( + items: Array, + handlers: Map +): MenuTemplate => { + const out: MenuTemplateItem[] = [] + for (const item of items) { + if (!item) continue + if (item.type === 'separator') { + out.push({ type: 'separator' }) + continue + } + const id = item.id ? `${item.id}-${nextItemId()}` : nextItemId() + handlers.set(id, item.click as MenuClickHandler | undefined) + out.push({ + id, + label: item.label, + type: item.type as MenuTemplateItem['type'], + accelerator: item.accelerator, + enabled: item.enabled !== false, + checked: !!item.checked, + submenu: item.submenu ? serialize(item.submenu, handlers) : undefined + }) + } + return out +} + +export const popupContextMenu = ( + items: Array, + position: MenuPopupPosition, + payload: unknown = {} +): void => { + const handlers = new Map() + const template = serialize(items, handlers) + + let offClick: (() => void) | null = null + let offClosed: (() => void) | null = null + + const cleanup = (): void => { + if (offClick) { + offClick() + offClick = null + } + if (offClosed) { + offClosed() + offClosed = null + } + handlers.clear() + } + + offClick = window.electron.ipcRenderer.on('mt::menu::click', (_e, message) => { + // Main process actually sends `{ windowId, id }` (see src/main/ipc/window.ts); + // the contract `[menuId: string]` is intentionally narrowed at the boundary. + const id = (message as unknown as { id?: string } | undefined)?.id ?? '' + const handler = handlers.get(id) + if (typeof handler === 'function') { + try { + handler(payload) + } catch (err) { + console.error(err) + } + } + }) + offClosed = window.electron.ipcRenderer.on('mt::menu::closed', () => cleanup()) + + window.electron.windowControl.popupMenu(template, position) +} diff --git a/packages/desktop/src/renderer/src/contextMenu/sideBar/actions.ts b/packages/desktop/src/renderer/src/contextMenu/sideBar/actions.ts new file mode 100644 index 0000000000..32701dfcd9 --- /dev/null +++ b/packages/desktop/src/renderer/src/contextMenu/sideBar/actions.ts @@ -0,0 +1,36 @@ +import bus from '../../bus' + +type MenuItemArg = unknown +type BrowserWindowArg = unknown + +export const newFile = (_menuItem?: MenuItemArg, _browserWindow?: BrowserWindowArg): void => { + bus.emit('SIDEBAR::new', 'file') +} + +export const newDirectory = (_menuItem?: MenuItemArg, _browserWindow?: BrowserWindowArg): void => { + bus.emit('SIDEBAR::new', 'directory') +} + +export const copy = (_menuItem?: MenuItemArg, _browserWindow?: BrowserWindowArg): void => { + bus.emit('SIDEBAR::copy-cut', 'copy') +} + +export const cut = (_menuItem?: MenuItemArg, _browserWindow?: BrowserWindowArg): void => { + bus.emit('SIDEBAR::copy-cut', 'cut') +} + +export const paste = (_menuItem?: MenuItemArg, _browserWindow?: BrowserWindowArg): void => { + bus.emit('SIDEBAR::paste') +} + +export const rename = (_menuItem?: MenuItemArg, _browserWindow?: BrowserWindowArg): void => { + bus.emit('SIDEBAR::rename') +} + +export const remove = (_menuItem?: MenuItemArg, _browserWindow?: BrowserWindowArg): void => { + bus.emit('SIDEBAR::remove') +} + +export const showInFolder = (_menuItem?: MenuItemArg, _browserWindow?: BrowserWindowArg): void => { + bus.emit('SIDEBAR::show-in-folder') +} diff --git a/packages/desktop/src/renderer/src/contextMenu/sideBar/index.ts b/packages/desktop/src/renderer/src/contextMenu/sideBar/index.ts new file mode 100644 index 0000000000..f75012b26b --- /dev/null +++ b/packages/desktop/src/renderer/src/contextMenu/sideBar/index.ts @@ -0,0 +1,45 @@ +import { + SEPARATOR, + getNewFile, + getNewDirectory, + getCOPY, + getCUT, + getPASTE, + getRENAME, + getDELETE, + getShowInFolder +} from './menuItems' +import { popupContextMenu, type ContextMenuItem } from '../popupMenu' + +export const showContextMenu = ( + event: { clientX: number; clientY: number }, + hasPathCache: boolean +): void => { + const contextItems: ContextMenuItem[] = [ + getNewFile(), + getNewDirectory(), + SEPARATOR, + getCOPY(), + getCUT(), + getPASTE(), + SEPARATOR, + getRENAME(), + getDELETE(), + SEPARATOR, + getShowInFolder() + ] + + // PASTE entry (index 5) toggles based on the cached source path. + contextItems[5].enabled = hasPathCache + + const items: ContextMenuItem[] = contextItems.map((item) => { + if (!item || item.type === 'separator') return item + const click = item.click + return { + ...item, + click: click ? () => click(null, null) : undefined + } + }) + + popupContextMenu(items, { x: event.clientX, y: event.clientY }) +} diff --git a/packages/desktop/src/renderer/src/contextMenu/sideBar/menuItems.ts b/packages/desktop/src/renderer/src/contextMenu/sideBar/menuItems.ts new file mode 100644 index 0000000000..82f8c815d6 --- /dev/null +++ b/packages/desktop/src/renderer/src/contextMenu/sideBar/menuItems.ts @@ -0,0 +1,83 @@ +import * as contextMenu from './actions' +import { t } from '../../i18n' + +// NOTE: This are mutable fields that may change at runtime. + +export const SEPARATOR = { + type: 'separator' +} + +// Use function form to avoid calling the translation function during module load +export const getNewFile = () => ({ + label: t('contextMenu.sideBar.newFile'), + id: 'newFileMenuItem', + click(_menuItem: unknown, _browserWindow: unknown) { + contextMenu.newFile() + } +}) + +export const getNewDirectory = () => ({ + label: t('contextMenu.sideBar.newDirectory'), + id: 'newDirectoryMenuItem', + click(_menuItem: unknown, _browserWindow: unknown) { + contextMenu.newDirectory() + } +}) + +export const getCOPY = () => ({ + label: t('contextMenu.sideBar.copy'), + id: 'copyMenuItem', + click(_menuItem: unknown, _browserWindow: unknown) { + contextMenu.copy() + } +}) + +export const getCUT = () => ({ + label: t('contextMenu.sideBar.cut'), + id: 'cutMenuItem', + click(_menuItem: unknown, _browserWindow: unknown) { + contextMenu.cut() + } +}) + +export const getPASTE = () => ({ + label: t('contextMenu.sideBar.paste'), + id: 'pasteMenuItem', + click(_menuItem: unknown, _browserWindow: unknown) { + contextMenu.paste() + } +}) + +export const getRENAME = () => ({ + label: t('contextMenu.sideBar.rename'), + id: 'renameMenuItem', + click(_menuItem: unknown, _browserWindow: unknown) { + contextMenu.rename() + } +}) + +export const getDELETE = () => ({ + label: t('contextMenu.sideBar.moveToTrash'), + id: 'deleteMenuItem', + click(_menuItem: unknown, _browserWindow: unknown) { + contextMenu.remove() + } +}) + +export const getShowInFolder = () => ({ + label: t('contextMenu.sideBar.showInFolder'), + id: 'showInFolderMenuItem', + click(_menuItem: unknown, _browserWindow: unknown) { + contextMenu.showInFolder() + } +}) + +// Retained for backward compatibility +export const NEW_FILE = getNewFile() +export const NEW_DIRECTORY = getNewDirectory() +export const COPY = getCOPY() +export const CUT = getCUT() +export const PASTE = getPASTE() +export const RENAME = getRENAME() +export const DELETE = getDELETE() +export const SHOW_IN_FOLDER = getShowInFolder() diff --git a/packages/desktop/src/renderer/src/contextMenu/tabs/actions.ts b/packages/desktop/src/renderer/src/contextMenu/tabs/actions.ts new file mode 100644 index 0000000000..ff7eb45820 --- /dev/null +++ b/packages/desktop/src/renderer/src/contextMenu/tabs/actions.ts @@ -0,0 +1,29 @@ +import bus from '../../bus' + +export const closeThis = (tabId: string): void => { + bus.emit('TABS::close-this', tabId) +} + +export const closeOthers = (tabId: string): void => { + bus.emit('TABS::close-others', tabId) +} + +export const closeSaved = (): void => { + bus.emit('TABS::close-saved') +} + +export const closeAll = (): void => { + bus.emit('TABS::close-all') +} + +export const rename = (tabId: string): void => { + bus.emit('TABS::rename', tabId) +} + +export const copyPath = (tabId: string): void => { + bus.emit('TABS::copy-path', tabId) +} + +export const showInFolder = (tabId: string): void => { + bus.emit('TABS::show-in-folder', tabId) +} diff --git a/packages/desktop/src/renderer/src/contextMenu/tabs/index.ts b/packages/desktop/src/renderer/src/contextMenu/tabs/index.ts new file mode 100644 index 0000000000..da2b290b2e --- /dev/null +++ b/packages/desktop/src/renderer/src/contextMenu/tabs/index.ts @@ -0,0 +1,65 @@ +import { + SEPARATOR, + getCloseThis, + getCloseOthers, + getCloseSaved, + getCloseAll, + getRENAME, + getCopyPath, + getShowInFolder +} from './menuItems' +import { popupContextMenu } from '../popupMenu' + +type MenuItemShape = { + type?: string + click?: (...args: unknown[]) => void + enabled?: boolean + [key: string]: unknown +} + +const wrapClick = (item: MenuItemShape, tabId: string): MenuItemShape => { + if (!item || item.type === 'separator') return item + const click = item.click + return { + ...item, + click: click ? () => click({ _tabId: tabId }, null) : undefined + } +} + +interface ContextMenuClickEvent { + clientX: number + clientY: number +} + +interface TabLike { + id: string + pathname?: string | null +} + +export const showContextMenu = (event: ContextMenuClickEvent, tab: TabLike): void => { + const { pathname } = tab + const closeThis = getCloseThis() + const closeOthers = getCloseOthers() + const closeSaved = getCloseSaved() + const closeAll = getCloseAll() + const rename = getRENAME() + const copyPath = getCopyPath() + const showInFolder = getShowInFolder() + + ;([rename, copyPath, showInFolder] as MenuItemShape[]).forEach((item) => { + item.enabled = !!pathname + }) + + const items = [ + closeThis, + closeOthers, + closeSaved, + closeAll, + SEPARATOR, + rename, + copyPath, + showInFolder + ].map((item) => wrapClick(item as MenuItemShape, tab.id)) + + popupContextMenu(items, { x: event.clientX, y: event.clientY }) +} diff --git a/packages/desktop/src/renderer/src/contextMenu/tabs/menuItems.ts b/packages/desktop/src/renderer/src/contextMenu/tabs/menuItems.ts new file mode 100644 index 0000000000..af9a015fb1 --- /dev/null +++ b/packages/desktop/src/renderer/src/contextMenu/tabs/menuItems.ts @@ -0,0 +1,77 @@ +import * as contextMenu from './actions' +import { t } from '../../i18n' + +// NOTE: This are mutable fields that may change at runtime. + +export const SEPARATOR = { + type: 'separator' +} + +// Use function form to avoid calling the translation function during module load +type TabMenuItem = { _tabId: string; [key: string]: unknown } + +export const getCloseThis = () => ({ + label: t('contextMenu.tabs.close'), + id: 'closeThisTab', + click(menuItem: TabMenuItem, _browserWindow?: unknown) { + contextMenu.closeThis(menuItem._tabId) + } +}) + +export const getCloseOthers = () => ({ + label: t('contextMenu.tabs.closeOthers'), + id: 'closeOtherTabs', + click(menuItem: TabMenuItem, _browserWindow?: unknown) { + contextMenu.closeOthers(menuItem._tabId) + } +}) + +export const getCloseSaved = () => ({ + label: t('contextMenu.tabs.closeSavedTabs'), + id: 'closeSavedTabs', + click(_menuItem: TabMenuItem, _browserWindow?: unknown) { + contextMenu.closeSaved() + }, + enabled: true +}) + +export const getCloseAll = () => ({ + label: t('contextMenu.tabs.closeAllTabs'), + id: 'closeAllTabs', + click(_menuItem: TabMenuItem, _browserWindow?: unknown) { + contextMenu.closeAll() + } +}) + +export const getRENAME = () => ({ + label: t('contextMenu.tabs.rename'), + id: 'renameFile', + click(menuItem: TabMenuItem, _browserWindow?: unknown) { + contextMenu.rename(menuItem._tabId) + } +}) + +export const getCopyPath = () => ({ + label: t('contextMenu.tabs.copyPath'), + id: 'copyPath', + click(menuItem: TabMenuItem, _browserWindow?: unknown) { + contextMenu.copyPath(menuItem._tabId) + } +}) + +export const getShowInFolder = () => ({ + label: t('contextMenu.tabs.showInFolder'), + id: 'showInFolder', + click(menuItem: TabMenuItem, _browserWindow?: unknown) { + contextMenu.showInFolder(menuItem._tabId) + } +}) + +// Retained for backward compatibility +export const CLOSE_THIS = getCloseThis() +export const CLOSE_OTHERS = getCloseOthers() +export const CLOSE_SAVED = getCloseSaved() +export const CLOSE_ALL = getCloseAll() +export const RENAME = getRENAME() +export const COPY_PATH = getCopyPath() +export const SHOW_IN_FOLDER = getShowInFolder() diff --git a/packages/desktop/src/renderer/src/i18n/index.ts b/packages/desktop/src/renderer/src/i18n/index.ts new file mode 100644 index 0000000000..721141b372 --- /dev/null +++ b/packages/desktop/src/renderer/src/i18n/index.ts @@ -0,0 +1,117 @@ +import { createI18n } from 'vue-i18n' +import { compile, type MessageCompiler } from '@intlify/core-base' +import bus from '../bus' +// Directly import translation files +import enTranslations from '../../../../static/locales/en.json' + +// vue-i18n compiles each translation lazily on first use, and its compiler +// throws a SyntaxError on any value it can't parse — e.g. a literal `{{x}}` +// (nested placeholder) or stray linked-message syntax. A single malformed +// translation then crashed the renderer; during HTML/PDF export this surfaced +// as an "Unexpected renderer process error" even though the export itself +// succeeded (issue #4046). Reuse vue-i18n's own compiler so well-formed +// messages keep their `{name}` interpolation, plurals and linked references, +// and fall back to the raw text when compilation fails. +const safeMessageCompiler: MessageCompiler = (message, context) => { + try { + return compile(message, context) + } catch (err) { + if (typeof message === 'string') { + return () => message + } + throw err + } +} + +// vue-i18n's options type intersection between Composition + Legacy modes is +// notoriously difficult to satisfy with mixed shapes; we cast the options once +// at the call site rather than spreading `any` further. +const i18n = createI18n({ + legacy: false, + locale: 'en', // default is en + fallbackLocale: 'en', + messages: { en: enTranslations }, // Load en by default only + // Disable linking to avoid '@' symbols being misinterpreted + modifiers: { + '@': () => '@' + }, + // Disable plural parsing + pluralRules: {}, + // Degrade malformed translations to raw text instead of crashing the renderer. + messageCompiler: safeMessageCompiler + // eslint-disable-next-line @typescript-eslint/no-explicit-any +} as any) + +// Export the translation function - Fix: correctly handle the Vue i18n v9+ global getter +export const t = (key: string, ...args: unknown[]): string => { + // Check if the i18n instance is available + if (!i18n) { + console.warn('⚠️ i18n实例不可用,使用英文fallback') + return key + } + + try { + // Correctly access the global property + if (!i18n.global) { + console.warn('⚠️ i18n.global not ready yet, falling back to EN') + return key + } + + // vue-i18n's `t` is heavily overloaded; the variadic call signature here + // intentionally bypasses the strict overload set. + return (i18n.global.t as (key: string, ...args: unknown[]) => string)(key, ...args) + } catch (error) { + console.error('❌ 翻译函数执行错误:', error) + return key + } +} + +// Cache in-flight translation loads so that concurrent setLanguage() calls +// don't fire duplicate IPCs for the same locale. +const inflightLoads = new Map | undefined>>() + +export const setLanguage = async(locale: string): Promise => { + if (!locale) return + const globalI18n = i18n.global + if (!globalI18n.availableLocales.includes(locale)) { + let pending = inflightLoads.get(locale) + if (!pending) { + pending = Promise.resolve(window.i18nUtils.loadTranslations(locale)).finally(() => + inflightLoads.delete(locale) + ) + inflightLoads.set(locale, pending) + } + const translation = await pending + if (!translation) return // Failed to load locale file + + if (!globalI18n.availableLocales.includes(locale)) { + globalI18n.setLocaleMessage(locale, translation) + console.log(`🌐 Loaded and set new locale: ${locale}`) + } + } + globalI18n.locale.value = locale +} + +// Export the current language getter function +export const getCurrentLanguage = (): string => { + return i18n.global.locale.value +} + +// Export the i18n instance (named and default export) +export { i18n } +export default i18n + +// Listen for language changes +if (window.electron && window.electron.ipcRenderer) { + window.electron.ipcRenderer.on('language-changed', (_event, newLocale) => { + setLanguage(newLocale) + bus.emit('language-changed', newLocale) + }) + + // Request the current language setting at startup + window.electron.ipcRenderer.send('mt::get-current-language') + window.electron.ipcRenderer.on('mt::current-language', (_event, language) => { + setLanguage(language) + bus.emit('language-changed', language) + }) +} diff --git a/packages/desktop/src/renderer/src/main.ts b/packages/desktop/src/renderer/src/main.ts new file mode 100644 index 0000000000..951582df7c --- /dev/null +++ b/packages/desktop/src/renderer/src/main.ts @@ -0,0 +1,62 @@ +import { createApp, type App } from 'vue' +import { createRouter, createWebHashHistory } from 'vue-router' +import bootstrapRenderer from './bootstrap' +import axios from './axios' +import pinia from './store' +import './assets/symbolIcon' + +// Element Plus instead of Element UI for Vue 3 +import ElementPlus from 'element-plus' +import 'element-plus/dist/index.css' +import en from 'element-plus/es/locale/lang/en' + +// I18n translation system +import i18nPlugin from './i18n' + +// something is wrong here! \/ +import services from './services/index' +import routes from './router' +import Main from './Main.vue' + +import './assets/styles/index.css' +import './assets/styles/printService.css' + +// ----------------------------------------------- + +window.marktext = {} +bootstrapRenderer() + +// ----------------------------------------------- +// Be careful when changing code before this line! + +// Create Vue app +const app: App = createApp(Main) + +// Configure Element Plus with locale +app.use(ElementPlus, { + locale: en +}) + +const envType = window.marktext?.env?.type as string | undefined + +const router = createRouter({ + history: createWebHashHistory(), + // it seems like something might have changed in vue-router? it uses the full "file path" instead of + // links like /editor if we use the old createWebHistory() + routes: routes(envType) +}) + +app.use(router) +app.use(pinia) +app.use(i18nPlugin) + +// Configure axios globally +app.config.globalProperties.$http = axios + +// Register services globally +;(services as unknown as Array & { name: string }>).forEach((s) => { + app.config.globalProperties['$' + s.name] = s[s.name] +}) + +// Mount the app +app.mount('#app') diff --git a/packages/desktop/src/renderer/src/node/fileSearcher.ts b/packages/desktop/src/renderer/src/node/fileSearcher.ts new file mode 100644 index 0000000000..718da8e129 --- /dev/null +++ b/packages/desktop/src/renderer/src/node/fileSearcher.ts @@ -0,0 +1,4 @@ +// File-mode searcher; kept as a separate file for backward compatibility with +// existing imports. Delegates to the shared IPC-backed FileSearcher exported +// from ripgrepSearcher.ts. +export { FileSearcher as default } from './ripgrepSearcher' diff --git a/packages/desktop/src/renderer/src/node/paths.ts b/packages/desktop/src/renderer/src/node/paths.ts new file mode 100644 index 0000000000..f2cb128ff7 --- /dev/null +++ b/packages/desktop/src/renderer/src/node/paths.ts @@ -0,0 +1,29 @@ +import EnvPaths from 'common/envPaths' + +const env = (window.electron && window.electron.process && window.electron.process.env) || {} +const exposedPaths = (window.electron && window.electron.paths) || {} + +// Allow a local override (e.g. an optimized ripgrep build). Otherwise use the +// asar-unpacked path resolved by the main process and forwarded via boot info. +const ripgrepBinaryPath: string = env.MARKTEXT_RIPGREP_PATH || exposedPaths.ripgrepBinary || '' + +class RendererPaths extends EnvPaths { + private readonly _ripgrepBinaryPath: string + + /** + * @param userDataPath The user data path. + */ + constructor(userDataPath: string) { + if (!userDataPath) { + throw new Error('No user data path is given.') + } + super(userDataPath) + this._ripgrepBinaryPath = ripgrepBinaryPath + } + + get ripgrepBinaryPath(): string { + return this._ripgrepBinaryPath + } +} + +export default RendererPaths diff --git a/packages/desktop/src/renderer/src/node/ripgrepSearcher.ts b/packages/desktop/src/renderer/src/node/ripgrepSearcher.ts new file mode 100644 index 0000000000..61526551bb --- /dev/null +++ b/packages/desktop/src/renderer/src/node/ripgrepSearcher.ts @@ -0,0 +1,153 @@ +// Thin renderer wrapper over the main-process ripgrep IPC bridge. +// Returns a cancellable thenable with the same public shape as the legacy +// in-renderer searcher (so call sites in search.vue and quickOpen don't need +// to change). + +import { deepClone } from '../util' + +export type RipgrepMode = 'text' | 'files' + +export interface RipgrepSearchOptions { + didMatch?: (payload: unknown) => void + didSearchPaths?: (num: unknown) => void + [key: string]: unknown +} + +export interface CancellableSearch extends Promise { + cancel: () => void +} + +interface StartArgs { + mode: RipgrepMode + directories: unknown + pattern: unknown + options: RipgrepSearchOptions +} + +interface RipgrepPayloadEnvelope { + searchId: string + payload?: unknown + num?: unknown + error?: string +} + +let nextId = 1 +const genId = (): string => `rg-${Date.now()}-${nextId++}` + +const startSearch = ({ mode, directories, pattern, options }: StartArgs): CancellableSearch => { + const searchId = genId() + const didMatch = options.didMatch || ((): void => {}) + const didSearchPaths = options.didSearchPaths || ((): void => {}) + + let offMatch: (() => void) | null = null + let offProgress: (() => void) | null = null + let offDone: (() => void) | null = null + let offError: (() => void) | null = null + let offCancelled: (() => void) | null = null + let cancelled = false + + const cleanup = (): void => { + if (offMatch) offMatch() + if (offProgress) offProgress() + if (offDone) offDone() + if (offError) offError() + if (offCancelled) offCancelled() + offMatch = offProgress = offDone = offError = offCancelled = null + } + + const promise = new Promise((resolve, reject) => { + offMatch = window.ripgrep.onMatch((payload: unknown) => { + const env = payload as RipgrepPayloadEnvelope | null + if (!env || env.searchId !== searchId) return + try { + didMatch(env.payload) + } catch (err) { + console.error(err) + } + }) + offProgress = window.ripgrep.onProgress((payload: unknown) => { + const env = payload as RipgrepPayloadEnvelope | null + if (!env || env.searchId !== searchId) return + try { + didSearchPaths(env.num) + } catch (err) { + console.error(err) + } + }) + offDone = window.ripgrep.onDone((payload: unknown) => { + const env = payload as RipgrepPayloadEnvelope | null + if (!env || env.searchId !== searchId) return + cleanup() + resolve() + }) + offError = window.ripgrep.onError((payload: unknown) => { + const env = payload as RipgrepPayloadEnvelope | null + if (!env || env.searchId !== searchId) return + cleanup() + reject(new Error(env.error || 'Ripgrep search failed')) + }) + offCancelled = window.ripgrep.onCancelled((payload: unknown) => { + const env = payload as RipgrepPayloadEnvelope | null + if (!env || env.searchId !== searchId) return + cleanup() + resolve() + }) + + // Strip non-serializable callbacks before shipping options across IPC. + // Pinia/Vue can hand us reactive Proxies that fail structured clone, so + // do a JSON round-trip on the remaining options to get plain values. + + const { didMatch: _a, didSearchPaths: _b, ...rest } = options + let serializable: unknown + try { + serializable = deepClone(rest) + } catch { + serializable = rest + } + const plainDirectories = Array.isArray(directories) ? directories.map((d) => String(d)) : [] + window.ripgrep + .start({ + searchId, + mode, + directories: plainDirectories, + pattern: typeof pattern === 'string' ? pattern : String(pattern || ''), + options: serializable + }) + .catch((err) => { + cleanup() + reject(err) + }) + }) as CancellableSearch + + promise.cancel = (): void => { + if (cancelled) return + cancelled = true + window.ripgrep.cancel(searchId) + } + return promise +} + +class RipgrepDirectorySearcher { + rgPath: string + + constructor() { + const marktext = window.marktext + this.rgPath = marktext?.paths?.ripgrepBinaryPath || window.rgPath || '' + } + + search(directories: string[], pattern: string, options: RipgrepSearchOptions): CancellableSearch { + return startSearch({ mode: 'text', directories, pattern, options }) + } +} + +export default RipgrepDirectorySearcher + +export class FileSearcher { + search( + directories: string[], + _pattern: string, + options: RipgrepSearchOptions + ): CancellableSearch { + return startSearch({ mode: 'files', directories, pattern: '', options }) + } +} diff --git a/packages/desktop/src/renderer/src/pages/app.vue b/packages/desktop/src/renderer/src/pages/app.vue new file mode 100644 index 0000000000..e7ac49ae95 --- /dev/null +++ b/packages/desktop/src/renderer/src/pages/app.vue @@ -0,0 +1,253 @@ + + + + + diff --git a/packages/desktop/src/renderer/src/pages/preference.vue b/packages/desktop/src/renderer/src/pages/preference.vue new file mode 100644 index 0000000000..5434f4ffba --- /dev/null +++ b/packages/desktop/src/renderer/src/pages/preference.vue @@ -0,0 +1,144 @@ + + + + + diff --git a/packages/desktop/src/renderer/src/prefComponents/common/bool/index.vue b/packages/desktop/src/renderer/src/prefComponents/common/bool/index.vue new file mode 100644 index 0000000000..f7b98a3081 --- /dev/null +++ b/packages/desktop/src/renderer/src/prefComponents/common/bool/index.vue @@ -0,0 +1,138 @@ + + + + + diff --git a/packages/desktop/src/renderer/src/prefComponents/common/compound/index.vue b/packages/desktop/src/renderer/src/prefComponents/common/compound/index.vue new file mode 100644 index 0000000000..7b00167047 --- /dev/null +++ b/packages/desktop/src/renderer/src/prefComponents/common/compound/index.vue @@ -0,0 +1,52 @@ + + + + + diff --git a/packages/desktop/src/renderer/src/prefComponents/common/fontTextBox/bundledFonts.ts b/packages/desktop/src/renderer/src/prefComponents/common/fontTextBox/bundledFonts.ts new file mode 100644 index 0000000000..b10c9229da --- /dev/null +++ b/packages/desktop/src/renderer/src/prefComponents/common/fontTextBox/bundledFonts.ts @@ -0,0 +1,11 @@ +// Fonts MarkText bundles via @font-face (packages/muya/src/assets/styles/index.css). +// They are the editor/code defaults but are NOT installed system fonts, so the +// OS font-list IPC never returns them and the picker couldn't reselect them (#3021). +export const BUNDLED_PROPORTIONAL_FONTS = ['Open Sans'] +export const BUNDLED_MONOSPACE_FONTS = ['DejaVu Sans Mono'] + +export const withBundledFonts = (systemFonts: string[], onlyMonospace = false): string[] => { + const bundled = onlyMonospace ? BUNDLED_MONOSPACE_FONTS : BUNDLED_PROPORTIONAL_FONTS + const missing = bundled.filter(font => !systemFonts.includes(font)) + return [...missing, ...systemFonts] +} diff --git a/packages/desktop/src/renderer/src/prefComponents/common/fontTextBox/index.vue b/packages/desktop/src/renderer/src/prefComponents/common/fontTextBox/index.vue new file mode 100644 index 0000000000..9d3ccd3087 --- /dev/null +++ b/packages/desktop/src/renderer/src/prefComponents/common/fontTextBox/index.vue @@ -0,0 +1,180 @@ + + + + + diff --git a/packages/desktop/src/renderer/src/prefComponents/common/range/index.vue b/packages/desktop/src/renderer/src/prefComponents/common/range/index.vue new file mode 100644 index 0000000000..f1884b49ca --- /dev/null +++ b/packages/desktop/src/renderer/src/prefComponents/common/range/index.vue @@ -0,0 +1,125 @@ + + + + + diff --git a/packages/desktop/src/renderer/src/prefComponents/common/select/index.vue b/packages/desktop/src/renderer/src/prefComponents/common/select/index.vue new file mode 100644 index 0000000000..f13050c1dc --- /dev/null +++ b/packages/desktop/src/renderer/src/prefComponents/common/select/index.vue @@ -0,0 +1,142 @@ + + + + + diff --git a/packages/desktop/src/renderer/src/prefComponents/common/separator/index.vue b/packages/desktop/src/renderer/src/prefComponents/common/separator/index.vue new file mode 100644 index 0000000000..de682e6110 --- /dev/null +++ b/packages/desktop/src/renderer/src/prefComponents/common/separator/index.vue @@ -0,0 +1,11 @@ + + + diff --git a/packages/desktop/src/renderer/src/prefComponents/common/textBox/index.vue b/packages/desktop/src/renderer/src/prefComponents/common/textBox/index.vue new file mode 100644 index 0000000000..af527d47b3 --- /dev/null +++ b/packages/desktop/src/renderer/src/prefComponents/common/textBox/index.vue @@ -0,0 +1,149 @@ + + + + + diff --git a/packages/desktop/src/renderer/src/prefComponents/common/titlebar.vue b/packages/desktop/src/renderer/src/prefComponents/common/titlebar.vue new file mode 100644 index 0000000000..4bb8064951 --- /dev/null +++ b/packages/desktop/src/renderer/src/prefComponents/common/titlebar.vue @@ -0,0 +1,73 @@ + + + + + diff --git a/packages/desktop/src/renderer/src/prefComponents/common/types.ts b/packages/desktop/src/renderer/src/prefComponents/common/types.ts new file mode 100644 index 0000000000..f6bc7ce966 --- /dev/null +++ b/packages/desktop/src/renderer/src/prefComponents/common/types.ts @@ -0,0 +1,30 @@ +/** + * Shared types for the preference page leaf control components. + * + * Each control accepts a description label, optional explanatory copy, an + * external "more info" link, and an `onChange` callback. The value prop + * name varies per control (bool: `bool`, textBox: `input`, others: `value`), + * so individual controls extend the common base with their own value field. + */ + +/** Common props shared by every leaf preference control. */ +export interface PrefControlBaseProps { + /** Label displayed before the input. */ + description?: string + /** External URL opened when the info icon is clicked. */ + more?: string + /** When true, the control renders disabled / under-development. */ + disable?: boolean +} + +/** Generic value-carrying control props. */ +export interface PrefControlProps extends PrefControlBaseProps { + value: T + onChange: (value: T) => void +} + +/** A `{label, value}` option for `` / similar dropdowns. */ +export interface PrefSelectOption { + label: string + value: T +} diff --git a/packages/desktop/src/renderer/src/prefComponents/editor/config.ts b/packages/desktop/src/renderer/src/prefComponents/editor/config.ts new file mode 100644 index 0000000000..ee4eab5ab3 --- /dev/null +++ b/packages/desktop/src/renderer/src/prefComponents/editor/config.ts @@ -0,0 +1,81 @@ +import { ENCODING_NAME_MAP } from 'common/encoding' +import { t } from '../../i18n' +import type { PrefSelectOption } from '../common/types' + +export const tabSizeOptions: PrefSelectOption[] = [ + { + label: '1', + value: 1 + }, + { + label: '2', + value: 2 + }, + { + label: '3', + value: 3 + }, + { + label: '4', + value: 4 + } +] + +export const getEndOfLineOptions = (): PrefSelectOption[] => [ + { + label: t('preferences.editor.fileRepresentation.endOfLine.default'), + value: 'default' + }, + { + label: t('preferences.editor.fileRepresentation.endOfLine.crlf'), + value: 'crlf' + }, + { + label: t('preferences.editor.fileRepresentation.endOfLine.lf'), + value: 'lf' + } +] + +export const getTrimTrailingNewlineOptions = (): PrefSelectOption[] => [ + { + label: t('preferences.editor.fileRepresentation.trailingNewlines.trimAll'), + value: 0 + }, + { + label: t('preferences.editor.fileRepresentation.trailingNewlines.ensureOne'), + value: 1 + }, + { + label: t('preferences.editor.fileRepresentation.trailingNewlines.preserve'), + value: 2 + }, + { + label: t('preferences.editor.fileRepresentation.trailingNewlines.doNothing'), + value: 3 + } +] + +export const getTextDirectionOptions = (): PrefSelectOption[] => [ + { + label: t('preferences.editor.misc.textDirection.ltr'), + value: 'ltr' + }, + { + label: t('preferences.editor.misc.textDirection.rtl'), + value: 'rtl' + } +] + +let defaultEncodingOptions: PrefSelectOption[] | null = null +export const getDefaultEncodingOptions = (): PrefSelectOption[] => { + if (defaultEncodingOptions) { + return defaultEncodingOptions + } + + const options: PrefSelectOption[] = [] + for (const [value, label] of Object.entries(ENCODING_NAME_MAP)) { + options.push({ label, value }) + } + defaultEncodingOptions = options + return defaultEncodingOptions +} diff --git a/packages/desktop/src/renderer/src/prefComponents/editor/index.vue b/packages/desktop/src/renderer/src/prefComponents/editor/index.vue new file mode 100644 index 0000000000..78d023345c --- /dev/null +++ b/packages/desktop/src/renderer/src/prefComponents/editor/index.vue @@ -0,0 +1,251 @@ + + + + + diff --git a/packages/desktop/src/renderer/src/prefComponents/general/config.ts b/packages/desktop/src/renderer/src/prefComponents/general/config.ts new file mode 100644 index 0000000000..c5c329a514 --- /dev/null +++ b/packages/desktop/src/renderer/src/prefComponents/general/config.ts @@ -0,0 +1,139 @@ +import { t } from '../../i18n' +import type { PrefSelectOption } from '../common/types' + +export const getTitleBarStyleOptions = (): PrefSelectOption[] => [ + { + label: t('preferences.general.window.titleBarStyle.custom'), + value: 'custom' + }, + { + label: t('preferences.general.window.titleBarStyle.native'), + value: 'native' + } +] + +export const zoomOptions: PrefSelectOption[] = [ + { + label: '50.0%', + value: 0.5 + }, + { + label: '62.5%', + value: 0.625 + }, + { + label: '75.0%', + value: 0.75 + }, + { + label: '87.5%', + value: 0.875 + }, + { + label: '100.0%', + value: 1.0 + }, + { + label: '112.5%', + value: 1.125 + }, + { + label: '125.0%', + value: 1.25 + }, + { + label: '137.5%', + value: 1.375 + }, + { + label: '150.0%', + value: 1.5 + }, + { + label: '162.5%', + value: 1.625 + }, + { + label: '175.0%', + value: 1.75 + }, + { + label: '187.5%', + value: 1.875 + }, + { + label: '200.0%', + value: 2.0 + } +] + +export const getFileSortByOptions = (): PrefSelectOption[] => [ + { + label: t('preferences.general.sidebar.fileSortBy.creationTime'), + value: 'created' + }, + { + label: t('preferences.general.sidebar.fileSortBy.modificationTime'), + value: 'modified' + }, + { + label: t('preferences.general.sidebar.fileSortBy.filename'), + value: 'title' + } +] + +export const getFileSortOrderOptions = (sortBy: string = 'title'): PrefSelectOption[] => { + if (sortBy === 'title') { + return [ + { label: t('preferences.general.sidebar.fileSortOrder.aToZ'), value: 'asc' }, + { label: t('preferences.general.sidebar.fileSortOrder.zToA'), value: 'desc' } + ] + } + return [ + { label: t('preferences.general.sidebar.fileSortOrder.oldestFirst'), value: 'asc' }, + { label: t('preferences.general.sidebar.fileSortOrder.newestFirst'), value: 'desc' } + ] +} + +export const getLanguageOptions = (): PrefSelectOption[] => [ + { + label: t('preferences.general.misc.language.english'), + value: 'en' + }, + { + label: t('preferences.general.misc.language.chinese'), + value: 'zh-CN' + }, + { + label: t('preferences.general.misc.language.traditionalChinese'), + value: 'zh-TW' + }, + { + label: t('preferences.general.misc.language.spanish'), + value: 'es' + }, + { + label: t('preferences.general.misc.language.french'), + value: 'fr' + }, + { + label: t('preferences.general.misc.language.german'), + value: 'de' + }, + { + label: t('preferences.general.misc.language.japanese'), + value: 'ja' + }, + { + label: t('preferences.general.misc.language.korean'), + value: 'ko' + }, + { + label: t('preferences.general.misc.language.portuguese'), + value: 'pt' + }, + { + label: t('preferences.general.misc.language.turkish'), + value: 'tr' + } +] diff --git a/packages/desktop/src/renderer/src/prefComponents/general/index.vue b/packages/desktop/src/renderer/src/prefComponents/general/index.vue new file mode 100644 index 0000000000..f3b0189138 --- /dev/null +++ b/packages/desktop/src/renderer/src/prefComponents/general/index.vue @@ -0,0 +1,272 @@ + + + + + diff --git a/packages/desktop/src/renderer/src/prefComponents/image/components/folderSetting/index.vue b/packages/desktop/src/renderer/src/prefComponents/image/components/folderSetting/index.vue new file mode 100644 index 0000000000..656a3582eb --- /dev/null +++ b/packages/desktop/src/renderer/src/prefComponents/image/components/folderSetting/index.vue @@ -0,0 +1,121 @@ + + + + + diff --git a/packages/desktop/src/renderer/src/prefComponents/image/components/uploader/index.vue b/packages/desktop/src/renderer/src/prefComponents/image/components/uploader/index.vue new file mode 100644 index 0000000000..b68ea7cbd5 --- /dev/null +++ b/packages/desktop/src/renderer/src/prefComponents/image/components/uploader/index.vue @@ -0,0 +1,1193 @@ + + + + + diff --git a/packages/desktop/src/renderer/src/prefComponents/image/components/uploader/services.ts b/packages/desktop/src/renderer/src/prefComponents/image/components/uploader/services.ts new file mode 100644 index 0000000000..0d069004bb --- /dev/null +++ b/packages/desktop/src/renderer/src/prefComponents/image/components/uploader/services.ts @@ -0,0 +1,37 @@ +// TODO: Remove information from other vue source files into this file. +import { t } from '../../../../i18n' + +export interface UploaderService { + name: string + isGdprCompliant: boolean + privacyUrl: string + tosUrl: string + agreedToLegalNotices: boolean +} + +export type UploaderServiceId = 'picgo' | 'cliScript' + +export const isValidService = (name: string): boolean => { + return Object.prototype.hasOwnProperty.call(getServices(), name) +} + +const getServices = (): Record => ({ + picgo: { + name: t('preferences.image.uploader.services.picgo'), + isGdprCompliant: false, + privacyUrl: '', + tosUrl: 'https://github.com/PicGo/PicGo-Core', + agreedToLegalNotices: true + }, + + cliScript: { + name: t('preferences.image.uploader.services.cliScript'), + isGdprCompliant: true, + privacyUrl: '', + tosUrl: '', + agreedToLegalNotices: true + } +}) + +export { getServices } +export default getServices diff --git a/packages/desktop/src/renderer/src/prefComponents/image/config.ts b/packages/desktop/src/renderer/src/prefComponents/image/config.ts new file mode 100644 index 0000000000..b5e0d564d4 --- /dev/null +++ b/packages/desktop/src/renderer/src/prefComponents/image/config.ts @@ -0,0 +1,17 @@ +import { t } from '../../i18n' +import type { PrefSelectOption } from '../common/types' + +export const getImageActions = (): PrefSelectOption[] => [ + { + label: t('preferences.image.actions.upload'), + value: 'upload' + }, + { + label: t('preferences.image.actions.folder'), + value: 'folder' + }, + { + label: t('preferences.image.actions.path'), + value: 'path' + } +] diff --git a/packages/desktop/src/renderer/src/prefComponents/image/index.vue b/packages/desktop/src/renderer/src/prefComponents/image/index.vue new file mode 100644 index 0000000000..6500c53da1 --- /dev/null +++ b/packages/desktop/src/renderer/src/prefComponents/image/index.vue @@ -0,0 +1,54 @@ + + + + + diff --git a/packages/desktop/src/renderer/src/prefComponents/keybindings/KeybindingConfigurator.ts b/packages/desktop/src/renderer/src/prefComponents/keybindings/KeybindingConfigurator.ts new file mode 100644 index 0000000000..e80bb16647 --- /dev/null +++ b/packages/desktop/src/renderer/src/prefComponents/keybindings/KeybindingConfigurator.ts @@ -0,0 +1,180 @@ +import { isEqualAccelerator } from 'common/keybinding' +import getCommandDescriptionById from '@/commands/descriptions' +import { isOsx } from '@/util' + +const SHORTCUT_TYPE_DEFAULT = 0 +const SHORTCUT_TYPE_USER = 1 + +type ShortcutType = typeof SHORTCUT_TYPE_DEFAULT | typeof SHORTCUT_TYPE_USER + +export interface UiKeybinding { + id: string + description: string + accelerator: string + type: ShortcutType +} + +const getShortcutDescriptionById = (id: string): string => { + const description = getCommandDescriptionById(id) + if (!description) { + return id + } + return description +} + +export default class KeybindingConfigurator { + defaultKeybindings: Map + keybindingList: UiKeybinding[] + isDirty: boolean + + /** + * ctor + */ + constructor(defaultKeybindings: Map, userKeybindings: Map) { + this.defaultKeybindings = defaultKeybindings + this.keybindingList = this._buildUiKeybindingList(defaultKeybindings, userKeybindings) + this.isDirty = false + } + + _buildUiKeybindingList( + defaultKeybindings: Map, + userKeybindings: Map + ): UiKeybinding[] { + const uiKeybindings: UiKeybinding[] = [] + for (const [id] of defaultKeybindings) { + if (!isOsx && id.startsWith('mt.')) { + // Skip MarkText menu that is only available on macOS. + continue + } + uiKeybindings.push(this._toUiKeybinding(id, defaultKeybindings, userKeybindings)) + } + uiKeybindings.sort((a, b) => a.description.localeCompare(b.description)) + return uiKeybindings + } + + _toUiKeybinding( + id: string, + defaultKeybindings: Map, + userKeybindings: Map + ): UiKeybinding { + const description = getShortcutDescriptionById(id) + const userAccelerator = userKeybindings.get(id) + let type: ShortcutType = SHORTCUT_TYPE_DEFAULT + + // Overwrite accelerator if key is present (empty string unset old binding). + let accelerator: string + if (userAccelerator != null) { + type = SHORTCUT_TYPE_USER + accelerator = userAccelerator + } else { + accelerator = defaultKeybindings.get(id) ?? '' + } + return { id, description, accelerator, type } + } + + getKeybindings(): UiKeybinding[] { + return this.keybindingList + } + + // Rebuild the keybinding list to update descriptions on language switch + rebuildKeybindingList(): UiKeybinding[] { + // Save the current user settings + const userKeybindings = this._getUserKeybindingMap() + // Rebuild the list + this.keybindingList = this._buildUiKeybindingList(this.defaultKeybindings, userKeybindings) + return this.keybindingList + } + + async save(): Promise { + if (!this.isDirty) { + return true + } + + const userKeybindings = this._getUserKeybindingMap() + // The main-process handler returns Promise, but the IPC contract + // currently types `ret` as void; rely on the runtime value. + const result = (await window.electron.ipcRenderer.invoke( + 'mt::keybinding-save-user-keybindings', + userKeybindings + )) as unknown as boolean + if (result) { + this.isDirty = false + return true + } + return false + } + + _getUserKeybindingMap(): Map { + const userKeybindings = new Map() + for (const entry of this.keybindingList) { + const { id, accelerator, type } = entry + if (type !== SHORTCUT_TYPE_DEFAULT) { + userKeybindings.set(id, accelerator) + } + } + return userKeybindings + } + + change(id: string, accelerator: string): boolean { + const entry = this.keybindingList.find((entry) => entry.id === id) + if (!entry) { + return false + } + + if (accelerator && this._isDuplicate(accelerator)) { + return false + } + + entry.accelerator = accelerator + entry.type = this._isDefaultBinding(id, accelerator) + ? SHORTCUT_TYPE_DEFAULT + : SHORTCUT_TYPE_USER + this.isDirty = true + return true + } + + unbind(id: string): boolean { + return this.change(id, '') + } + + resetToDefault(id: string): boolean { + const accelerator = this.defaultKeybindings.get(id) + if (accelerator == null) { + // allow empty string + return false + } + return this.change(id, accelerator) + } + + async resetAll(): Promise { + const { defaultKeybindings, keybindingList } = this + for (const entry of keybindingList) { + const defaultAccelerator = defaultKeybindings.get(entry.id) + if (defaultAccelerator) { + entry.accelerator = defaultAccelerator + } else { + entry.accelerator = '' + } + entry.type = SHORTCUT_TYPE_DEFAULT + } + this.isDirty = true + return this.save() + } + + getDefaultAccelerator(id: string): string | undefined { + return this.defaultKeybindings.get(id) + } + + _isDuplicate(accelerator: string): boolean { + return ( + accelerator !== '' && + this.keybindingList.findIndex((entry) => + isEqualAccelerator(entry.accelerator, accelerator) + ) !== -1 + ) + } + + _isDefaultBinding(id: string, accelerator: string): boolean { + return this.defaultKeybindings.get(id) === accelerator + } +} diff --git a/packages/desktop/src/renderer/src/prefComponents/keybindings/index.vue b/packages/desktop/src/renderer/src/prefComponents/keybindings/index.vue new file mode 100644 index 0000000000..8db2deccf2 --- /dev/null +++ b/packages/desktop/src/renderer/src/prefComponents/keybindings/index.vue @@ -0,0 +1,364 @@ + + + + + + diff --git a/packages/desktop/src/renderer/src/prefComponents/keybindings/key-input-dialog.vue b/packages/desktop/src/renderer/src/prefComponents/keybindings/key-input-dialog.vue new file mode 100644 index 0000000000..cd03a37427 --- /dev/null +++ b/packages/desktop/src/renderer/src/prefComponents/keybindings/key-input-dialog.vue @@ -0,0 +1,265 @@ + + + + + + diff --git a/packages/desktop/src/renderer/src/prefComponents/markdown/config.ts b/packages/desktop/src/renderer/src/prefComponents/markdown/config.ts new file mode 100644 index 0000000000..1ef83b0735 --- /dev/null +++ b/packages/desktop/src/renderer/src/prefComponents/markdown/config.ts @@ -0,0 +1,96 @@ +import { t } from '../../i18n' +import type { PrefSelectOption } from '../common/types' + +export const bulletListMarkerOptions: PrefSelectOption[] = [ + { + label: '*', + value: '*' + }, + { + label: '-', + value: '-' + }, + { + label: '+', + value: '+' + } +] + +export const orderListDelimiterOptions: PrefSelectOption[] = [ + { + label: '.', + value: '.' + }, + { + label: ')', + value: ')' + } +] + +export const getPreferHeadingStyleOptions = (): PrefSelectOption[] => [ + { + label: t('preferences.markdown.misc.preferHeadingStyle.atx'), + value: 'atx' + }, + { + label: t('preferences.markdown.misc.preferHeadingStyle.setext'), + value: 'setext' + } +] + +export const getListIndentationOptions = (): PrefSelectOption[] => [ + { + label: t('preferences.markdown.lists.listIndentation.dfm'), + value: 'dfm' + }, + { + label: t('preferences.markdown.lists.listIndentation.tab'), + value: 'tab' + }, + { + label: t('preferences.markdown.lists.listIndentation.oneSpace'), + value: 1 + }, + { + label: t('preferences.markdown.lists.listIndentation.twoSpaces'), + value: 2 + }, + { + label: t('preferences.markdown.lists.listIndentation.threeSpaces'), + value: 3 + }, + { + label: t('preferences.markdown.lists.listIndentation.fourSpaces'), + value: 4 + } +] + +export const getFrontmatterTypeOptions = (): PrefSelectOption[] => [ + { + label: 'YAML', + value: '-' + }, + { + label: 'TOML', + value: '+' + }, + { + label: t('preferences.markdown.extensions.frontmatterType.jsonSemicolon'), + value: ';' + }, + { + label: t('preferences.markdown.extensions.frontmatterType.jsonBrace'), + value: '{' + } +] + +export const getSequenceThemeOptions = (): PrefSelectOption[] => [ + { + label: t('preferences.markdown.diagrams.sequenceTheme.handDrawn'), + value: 'hand' + }, + { + label: t('preferences.markdown.diagrams.sequenceTheme.simple'), + value: 'simple' + } +] diff --git a/packages/desktop/src/renderer/src/prefComponents/markdown/index.vue b/packages/desktop/src/renderer/src/prefComponents/markdown/index.vue new file mode 100644 index 0000000000..bfcd55ff04 --- /dev/null +++ b/packages/desktop/src/renderer/src/prefComponents/markdown/index.vue @@ -0,0 +1,182 @@ + + + + + + + diff --git a/packages/desktop/src/renderer/src/prefComponents/sideBar/config.ts b/packages/desktop/src/renderer/src/prefComponents/sideBar/config.ts new file mode 100644 index 0000000000..41bf94ea35 --- /dev/null +++ b/packages/desktop/src/renderer/src/prefComponents/sideBar/config.ts @@ -0,0 +1,529 @@ +import { + Setting as GeneralIcon, + Edit as EditorIcon, + Document as MarkdownIcon, + Brush as ThemeIcon, + Picture as ImageIcon, + Reading as SpellIcon, + Operation as KeyBindingIcon +} from '@element-plus/icons-vue' + +import preferences from '../../../../main/preferences/schema.json' +import { t } from '../../i18n' + +interface PrefCategory { + name: string + label: string + icon: unknown + path: string +} + +interface PreferenceSchemaEntry { + description: string + enum?: unknown[] + [key: string]: unknown +} + +interface TranslatedSearchEntry { + key: string + category: string + categoryEn: string + preference: string + preferenceEn: string + routeCategory: string + description: string + enum: unknown[] | undefined +} + +interface VueI18nLocale { + value?: string +} + +interface VueI18nGlobal { + locale?: VueI18nLocale | string + t?: (key: string) => string +} + +interface VueI18nGlobalContainer { + global?: VueI18nGlobal | (() => VueI18nGlobal) + t?: (key: string) => string + $i18n?: VueI18nGlobal +} + +declare global { + interface Window { + __VUE_I18N__?: VueI18nGlobalContainer + } +} + +// Function-attached cache shared between getTranslatedSearchContent and +// setupLanguageChangeListener. +interface CachedTranslator { + (): TranslatedSearchEntry[] + lastLanguage?: string +} + +const preferencesSchema = preferences as unknown as Record + +export const getCategory = (): PrefCategory[] => [ + { + name: t('preferences.categories.general'), + label: 'general', + icon: GeneralIcon, + path: '/preference/general' + }, + { + name: t('preferences.categories.editor'), + label: 'editor', + icon: EditorIcon, + path: '/preference/editor' + }, + { + name: t('preferences.categories.markdown'), + label: 'markdown', + icon: MarkdownIcon, + path: '/preference/markdown' + }, + { + name: t('preferences.categories.spelling'), + label: 'spelling', + icon: SpellIcon, + path: '/preference/spelling' + }, + { + name: t('preferences.categories.theme'), + label: 'theme', + icon: ThemeIcon, + path: '/preference/theme' + }, + { + name: t('preferences.categories.image'), + label: 'image', + icon: ImageIcon, + path: '/preference/image' + }, + { + name: t('preferences.categories.keybindings'), + label: 'keybindings', + icon: KeyBindingIcon, + path: '/preference/keybindings' + } +] + +const errMessage = (e: unknown): string => (e instanceof Error ? e.message : String(e)) + +const resolveGlobal = (container: VueI18nGlobalContainer | undefined): VueI18nGlobal | undefined => { + if (!container) return undefined + return typeof container.global === 'function' ? container.global() : container.global +} + +const resolveLocale = (g: VueI18nGlobal | undefined): string => { + if (!g || !g.locale) return 'en' + if (typeof g.locale === 'string') return g.locale + return g.locale.value ?? 'en' +} + +// Creates a reactive translated mapping function +export const getTranslatedSearchContent: CachedTranslator = (() => { + const fn = (() => { + // Generate keys by iterating through each language + const result: TranslatedSearchEntry[] = [] + Object.keys(preferencesSchema).forEach((k) => { + const entry = preferencesSchema[k] + if (!entry) return + const { description, enum: emums } = entry + + if (description.endsWith('--internal')) return + + const [category] = description.split('--') + const categoryName = category ?? '' + + // Map category names + let mappedCategory = categoryName.toLowerCase() + if (categoryName === 'General') mappedCategory = 'general' + else if (categoryName === 'Editor') mappedCategory = 'editor' + else if (categoryName === 'Markdown') mappedCategory = 'markdown' + else if (categoryName === 'Theme') mappedCategory = 'theme' + else if (categoryName === 'Image') mappedCategory = 'image' + else if (categoryName === 'View') mappedCategory = 'view' + else if (categoryName === 'Searcher') mappedCategory = 'searcher' + else if (categoryName === 'Watcher') mappedCategory = 'watcher' + else if (categoryName === 'Spelling') mappedCategory = 'spelling' + else if (categoryName === 'Custom CSS') mappedCategory = 'custom css' + else { + // Handle special category names + mappedCategory = categoryName.toLowerCase().replace(/\s+/g, '-') + } + + // Compute the category for route navigation (only allow existing routes, otherwise fall back to general) + let routeCategory = mappedCategory + const validRoutes = [ + 'general', + 'editor', + 'markdown', + 'spelling', + 'theme', + 'image', + 'keybindings' + ] + if (!validRoutes.includes(routeCategory)) routeCategory = 'general' + + // Try to translate the category and item + const categoryKey = `preferences.search.categories.${mappedCategory}` + const itemKey = `preferences.search.items.${k}` + + // Translate the category name + let translatedCategory = categoryName + const englishCategory = categoryName + try { + translatedCategory = t(categoryKey) + } catch (e) { + console.warn(` ⚠️ Search category translation failed: ${errMessage(e)}`) + // Try fallback to preferences.categories + try { + const fallbackKey = `preferences.categories.${mappedCategory}` + translatedCategory = t(fallbackKey) + } catch (e2) { + console.warn(` ❌ Search category fallback also failed: ${errMessage(e2)}`) + translatedCategory = categoryName + } + } + + // Translate preference description + let translatedPreference = description.split('--')[1] || description + const englishPreference = description.split('--')[1] || description + try { + translatedPreference = t(itemKey) + } catch (e) { + console.warn(` ⚠️ Search item translation failed: ${errMessage(e)}`) + // Try fallback to preferences.items + try { + const fallbackKey = `preferences.items.${k}` + translatedPreference = t(fallbackKey) + } catch (e2) { + console.warn(` ❌ Search item fallback also failed: ${errMessage(e2)}`) + translatedPreference = description.split('--')[1] || description + } + } + + result.push({ + key: k, + category: translatedCategory, + categoryEn: englishCategory, + preference: translatedPreference, + preferenceEn: englishPreference, + routeCategory, + description, + enum: emums + }) + }) + return result + }) as CachedTranslator + return fn +})() + +// Add language change listener +export const setupLanguageChangeListener = (): void => { + // Listen for language change events + const handleLanguageChange = () => { + // Trigger search content refresh + if (window.__VUE_I18N__) { + try { + const g = resolveGlobal(window.__VUE_I18N__) + const currentLanguage = resolveLocale(g) + + // Here we can dispatch a custom event to notify the search component to refresh + window.dispatchEvent( + new CustomEvent('languageChanged', { + detail: { language: currentLanguage } + }) + ) + } catch (e) { + console.warn('⚠️ Failed to get updated language setting:', e) + } + } + } + + // Listen for locale changes in the i18n instance + if (window.__VUE_I18N__) { + try { + const g = resolveGlobal(window.__VUE_I18N__) + if (g && g.locale && typeof g.locale !== 'string' && g.locale.value !== undefined) { + // Use Vue's reactive system to listen for language changes + } + } catch (e) { + console.warn('⚠️ Failed to set up language change listener:', e) + } + } + + // Add a polling fallback mechanism as a backup + setInterval(() => { + try { + if (window.__VUE_I18N__) { + const g = resolveGlobal(window.__VUE_I18N__) + const currentLanguage = resolveLocale(g) + if (currentLanguage !== getTranslatedSearchContent.lastLanguage) { + getTranslatedSearchContent.lastLanguage = currentLanguage + handleLanguageChange() + } + } + } catch { + // Ignore errors and continue checking + } + }, 1000) // Check once per second + + // Record the initial language + try { + if (window.__VUE_I18N__) { + const g = resolveGlobal(window.__VUE_I18N__) + getTranslatedSearchContent.lastLanguage = resolveLocale(g) + } + } catch { + getTranslatedSearchContent.lastLanguage = 'en' + } +} + +// Initialize the language change listener +setupLanguageChangeListener() + +// Add manual refresh function +export const refreshSearchContent = (): TranslatedSearchEntry[] => { + // Clear the language cache to force re-fetch + if (getTranslatedSearchContent.lastLanguage) { + delete getTranslatedSearchContent.lastLanguage + } + + // Trigger the language change event + window.dispatchEvent( + new CustomEvent('languageChanged', { + detail: { language: 'force-refresh' } + }) + ) + + return getTranslatedSearchContent() +} + +// Creates the debug popup (ensures the close button is visible) +function createDebugPopup(): HTMLDivElement { + // Remove any existing popup + const existingPopup = document.getElementById('debugPopup') + if (existingPopup && existingPopup.parentNode) { + existingPopup.parentNode.removeChild(existingPopup) + } + + // Create new popup + const popup = document.createElement('div') + popup.id = 'debugPopup' + popup.style.cssText = ` + position: fixed; + top: 50px; + right: 20px; + width: 400px; + height: 300px; + background: white; + border: 2px solid #333; + padding: 15px; + overflow: auto; + z-index: 10000; + box-shadow: 0 0 10px rgba(0,0,0,0.2); + ` + + // Create the title bar and close button + const titleBar = document.createElement('div') + titleBar.style.cssText = ` + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 10px; + border-bottom: 1px solid #ccc; + padding-bottom: 10px; + ` + + const title = document.createElement('h3') + title.textContent = '🛠️ Debug Info' + title.style.cssText = 'margin: 0; color: #333;' + + const closeButton = document.createElement('button') + closeButton.textContent = '✕ Close' + closeButton.style.cssText = ` + background: #ff4444; + color: white; + border: none; + padding: 5px 10px; + border-radius: 3px; + cursor: pointer; + font-size: 12px; + ` + + // Add close event + closeButton.onclick = () => { + if (popup && popup.parentNode) { + popup.parentNode.removeChild(popup) + } + } + + // Assemble the title bar + titleBar.appendChild(title) + titleBar.appendChild(closeButton) + + // Create the content area + const content = document.createElement('div') + content.id = 'debugContent' + + // Assemble the popup + popup.appendChild(titleBar) + popup.appendChild(content) + + document.body.appendChild(popup) + return popup +} + +// General method to get the i18n instance (fixes API access issues) +function getI18nInstance(): VueI18nGlobal | VueI18nGlobalContainer | null { + if (!window.__VUE_I18N__) { + return null + } + + const i18n = window.__VUE_I18N__ + + // Try different access methods + if (typeof i18n.global === 'function') { + return i18n.global() + } else if (i18n.global && typeof i18n.global.t === 'function') { + return i18n.global + } else if (typeof i18n.t === 'function') { + return i18n + } else if (i18n.$i18n && typeof i18n.$i18n.t === 'function') { + return i18n.$i18n + } + + return null +} + +// Enhanced debug function (fixes API access issues) +export const debugLanguageState = (): void => { + // Ensure the popup exists and is visible + let popup = document.getElementById('debugPopup') as HTMLDivElement | null + if (!popup) { + popup = createDebugPopup() + popup.style.zIndex = '10000' + } + + // Ensure the content area exists + let debugContent = popup.querySelector('#debugContent') as HTMLDivElement | null + if (!debugContent) { + const newContent = document.createElement('div') + newContent.id = 'debugContent' + popup.appendChild(newContent) + debugContent = newContent + } + + // Clear and populate debug information + debugContent.innerHTML = '
Loading debug info...
' + + // Populate debug details + const details = debugContent.querySelector('#debugDetails') as HTMLDivElement | null + if (!details) return + + // Simulate delayed loading + setTimeout(() => { + try { + // Show detailed information about the i18n instance + let debugInfo = '

🔍 i18n instance details:

' + + if (!window.__VUE_I18N__) { + debugInfo += '

❌ __VUE_I18N__ does not exist

' + } else { + const i18n = window.__VUE_I18N__ + debugInfo += ` +

__VUE_I18N__ type: ${typeof i18n}

+

__VUE_I18N__ keys: ${Object.keys(i18n).slice(0, 10).join(', ')}

+

global type: ${typeof i18n.global}

+ ` + + // Safely display global info + try { + if (i18n.global) { + const globalKeys = Object.keys(i18n.global).slice(0, 5) + debugInfo += `

global keys: ${globalKeys.join(', ')}

` + + // Check if translation function is available + if (typeof (i18n.global as VueI18nGlobal).t === 'function') { + debugInfo += '

✅ global.t function available

' + } else { + debugInfo += '

⚠️ global.t function unavailable

' + } + } + } catch (e) { + debugInfo += `

❌ Error checking global: ${errMessage(e)}

` + } + + // Try to get the i18n instance + const i18nInstance = getI18nInstance() + if (i18nInstance) { + debugInfo += '

✅ Successfully got i18n instance

' + + // Get the current language + let currentLanguage = 'unknown' + const inst = i18nInstance as VueI18nGlobal + if (inst.locale && typeof inst.locale !== 'string' && inst.locale.value) { + currentLanguage = inst.locale.value + } else if (typeof inst.locale === 'string') { + currentLanguage = inst.locale + } + + debugInfo += `

🌍 Current language: ${currentLanguage}

` + + // Test translation + try { + const tFn = (i18nInstance as VueI18nGlobal).t + const testTranslation = tFn + ? tFn('preferences.general.window.titleBarStyle.custom') + : '' + debugInfo += `

🔄 Test translation: ${testTranslation}

` + } catch (e) { + debugInfo += `

🔄 Test translation failed: ${errMessage(e)}

` + } + } else { + debugInfo += '

❌ Could not get a valid i18n instance

' + } + } + + details.innerHTML = debugInfo + } catch (e) { + details.innerHTML = `

❌ Debug failed: ${errMessage(e)}

` + } + }, 500) +} +/* +// Add debug buttons to the page (visible in development environment only) +if (typeof document !== 'undefined') { + const isDev = (typeof import.meta !== 'undefined' && import.meta.env && import.meta.env.DEV) || + (typeof process !== 'undefined' && process.env && process.env.NODE_ENV === 'development'); + if (isDev) { + // Ensure the button container exists + const buttonContainer = document.createElement('div'); + buttonContainer.id = 'debugButtonContainer'; + buttonContainer.style.cssText = 'position:fixed;top:10px;right:10px;z-index:999;'; + + // Create the debug button + const debugButton = document.createElement('button'); + debugButton.textContent = '🛠️ Debug'; + debugButton.style.cssText = 'padding:8px 15px;margin:5px;background:#f0f0f0;border:1px solid #ddd;border-radius:4px;cursor:pointer;'; + debugButton.onclick = debugLanguageState; + + // Create the refresh button + const refreshButton = document.createElement('button'); + refreshButton.textContent = '🔁 Refresh'; + refreshButton.style.cssText = 'padding:8px 15px;margin:5px;background:#f0f0f0;border:1px solid #ddd;border-radius:4px;cursor:pointer;'; + refreshButton.onclick = () => window.dispatchEvent(new CustomEvent('languageChanged')); + + // Add buttons to the container + buttonContainer.appendChild(debugButton); + buttonContainer.appendChild(refreshButton); + + // Add to document + document.body.appendChild(buttonContainer); + } +} +*/ diff --git a/packages/desktop/src/renderer/src/prefComponents/sideBar/index.vue b/packages/desktop/src/renderer/src/prefComponents/sideBar/index.vue new file mode 100644 index 0000000000..200a396413 --- /dev/null +++ b/packages/desktop/src/renderer/src/prefComponents/sideBar/index.vue @@ -0,0 +1,277 @@ + + + + diff --git a/packages/desktop/src/renderer/src/prefComponents/spellchecker/index.vue b/packages/desktop/src/renderer/src/prefComponents/spellchecker/index.vue new file mode 100644 index 0000000000..6198e2ff92 --- /dev/null +++ b/packages/desktop/src/renderer/src/prefComponents/spellchecker/index.vue @@ -0,0 +1,290 @@ + + + + + + diff --git a/packages/desktop/src/renderer/src/prefComponents/theme/config.ts b/packages/desktop/src/renderer/src/prefComponents/theme/config.ts new file mode 100644 index 0000000000..5bf884a5b7 --- /dev/null +++ b/packages/desktop/src/renderer/src/prefComponents/theme/config.ts @@ -0,0 +1,44 @@ +export interface ThemeDescriptor { + name: string +} + +export const themes: ReadonlyArray = [ + // Light Themes (alphabetical) + { name: 'ayu-light' }, + { name: 'light' }, + { name: 'catppuccin-latte' }, + { name: 'everforest-light' }, + { name: 'graphite' }, + { name: 'gruvbox-light' }, + { name: 'rose-pine-dawn' }, + { name: 'solarized-light' }, + { name: 'tokyo-night-light' }, + { name: 'ulysses' }, + // Dark Themes (alphabetical) + { name: 'ayu-dark' }, + { name: 'ayu-mirage' }, + { name: 'dark' }, + { name: 'catppuccin-mocha' }, + { name: 'cyberdream' }, + { name: 'dracula' }, + { name: 'everforest-dark' }, + { name: 'gruvbox-dark' }, + { name: 'horizon-dark' }, + { name: 'kanagawa' }, + { name: 'material-dark' }, + { name: 'monokai-pro' }, + { name: 'nightfox' }, + { name: 'nord' }, + { name: 'one-dark' }, + { name: 'oxocarbon-dark' }, + { name: 'palenight' }, + { name: 'rose-pine' }, + { name: 'rose-pine-moon' }, + { name: 'solarized-dark' }, + { name: 'synthwave-84' }, + { name: 'tokyo-night' }, + { name: 'tokyo-night-storm' } +] + +// getAutoSwitchThemeOptions removed - no longer needed +// We now use a boolean toggle for followSystemTheme instead of a dropdown diff --git a/packages/desktop/src/renderer/src/prefComponents/theme/index.vue b/packages/desktop/src/renderer/src/prefComponents/theme/index.vue new file mode 100644 index 0000000000..5a2871b701 --- /dev/null +++ b/packages/desktop/src/renderer/src/prefComponents/theme/index.vue @@ -0,0 +1,487 @@ +