From e2712ead7823e39f5cd4095d5124654d63686c49 Mon Sep 17 00:00:00 2001
From: Raunak Raj <71929976+bajrangCoder@users.noreply.github.com>
Date: Sat, 21 Feb 2026 18:18:16 +0530
Subject: [PATCH 1/2] feat(plugins): filter marketplace lists to
CodeMirror-compatible plugins
---
src/pages/plugins/plugins.js | 22 ++++++++++++++++-----
src/sidebarApps/extensions/index.js | 30 +++++++++++++++++++++++------
2 files changed, 41 insertions(+), 11 deletions(-)
diff --git a/src/pages/plugins/plugins.js b/src/pages/plugins/plugins.js
index a0f0bcbe9..92c5e433b 100644
--- a/src/pages/plugins/plugins.js
+++ b/src/pages/plugins/plugins.js
@@ -52,6 +52,12 @@ export default function PluginsInclude(updates) {
let isSearching = false;
let currentFilter = null;
const LIMIT = 50;
+ const SUPPORTED_EDITOR = "cm";
+
+ const withSupportedEditor = (url) => {
+ const separator = url.includes("?") ? "&" : "?";
+ return `${url}${separator}supported_editor=${SUPPORTED_EDITOR}`;
+ };
Contextmenu({
toggler: $add,
@@ -337,7 +343,7 @@ export default function PluginsInclude(updates) {
if (!query) return [];
try {
const response = await fetch(
- `${constants.API_BASE}/plugins?name=${query}`,
+ withSupportedEditor(`${constants.API_BASE}/plugins?name=${query}`),
);
const plugins = await response.json();
// Map the plugins to Item elements and return
@@ -418,11 +424,15 @@ export default function PluginsInclude(updates) {
let response;
if (filterState.value === "top_rated") {
response = await fetch(
- `${constants.API_BASE}/plugins?explore=random&page=${page}&limit=${LIMIT}`,
+ withSupportedEditor(
+ `${constants.API_BASE}/plugins?explore=random&page=${page}&limit=${LIMIT}`,
+ ),
);
} else {
response = await fetch(
- `${constants.API_BASE}/plugin?orderBy=${filterState.value}&page=${page}&limit=${LIMIT}`,
+ withSupportedEditor(
+ `${constants.API_BASE}/plugin?orderBy=${filterState.value}&page=${page}&limit=${LIMIT}`,
+ ),
);
}
const items = await response.json();
@@ -461,7 +471,7 @@ export default function PluginsInclude(updates) {
try {
const page = filterState.nextPage;
const response = await fetch(
- `${constants.API_BASE}/plugins?page=${page}&limit=${LIMIT}`,
+ withSupportedEditor(`${constants.API_BASE}/plugins?page=${page}&limit=${LIMIT}`),
);
const data = await response.json();
filterState.nextPage = page + 1;
@@ -557,7 +567,9 @@ export default function PluginsInclude(updates) {
$list.all.setAttribute("empty-msg", strings["loading..."]);
- const response = await fetch(`${constants.API_BASE}/plugins?page=${currentPage}&limit=${LIMIT}`);
+ const response = await fetch(
+ withSupportedEditor(`${constants.API_BASE}/plugins?page=${currentPage}&limit=${LIMIT}`),
+ );
const newPlugins = await response.json();
if (newPlugins.length < LIMIT) {
diff --git a/src/sidebarApps/extensions/index.js b/src/sidebarApps/extensions/index.js
index 01e83bd64..0a396cf9d 100644
--- a/src/sidebarApps/extensions/index.js
+++ b/src/sidebarApps/extensions/index.js
@@ -33,6 +33,12 @@ let isLoading = false;
let currentFilter = null;
let filterHasMore = true;
let isFilterLoading = false;
+const SUPPORTED_EDITOR = "cm";
+
+function withSupportedEditor(url) {
+ const separator = url.includes("?") ? "&" : "?";
+ return `${url}${separator}supported_editor=${SUPPORTED_EDITOR}`;
+}
const $header = (
@@ -140,7 +146,9 @@ async function loadMorePlugins() {
startLoading($explore);
const response = await fetch(
- `${constants.API_BASE}/plugins?page=${currentPage}&limit=${LIMIT}`,
+ withSupportedEditor(
+ `${constants.API_BASE}/plugins?page=${currentPage}&limit=${LIMIT}`,
+ ),
);
const newPlugins = await response.json();
@@ -224,7 +232,9 @@ async function searchPlugin() {
try {
$searchResult.classList.add("loading");
const plugins = await fsOperation(
- Url.join(constants.API_BASE, `plugins?name=${query}`),
+ withSupportedEditor(
+ Url.join(constants.API_BASE, `plugins?name=${query}`),
+ ),
).readFile("json");
installedPlugins = await listInstalledPlugins();
@@ -411,7 +421,9 @@ async function loadExplore() {
hasMore = true;
const response = await fetch(
- `${constants.API_BASE}/plugins?page=${currentPage}&limit=${LIMIT}`,
+ withSupportedEditor(
+ `${constants.API_BASE}/plugins?page=${currentPage}&limit=${LIMIT}`,
+ ),
);
const plugins = await response.json();
@@ -454,11 +466,15 @@ async function getFilteredPlugins(filterState) {
let response;
if (filterState.value === "top_rated") {
response = await fetch(
- `${constants.API_BASE}/plugins?explore=random&page=${page}&limit=${LIMIT}`,
+ withSupportedEditor(
+ `${constants.API_BASE}/plugins?explore=random&page=${page}&limit=${LIMIT}`,
+ ),
);
} else {
response = await fetch(
- `${constants.API_BASE}/plugin?orderBy=${filterState.value}&page=${page}&limit=${LIMIT}`,
+ withSupportedEditor(
+ `${constants.API_BASE}/plugin?orderBy=${filterState.value}&page=${page}&limit=${LIMIT}`,
+ ),
);
}
const items = await response.json();
@@ -497,7 +513,9 @@ async function getFilteredPlugins(filterState) {
try {
const page = filterState.nextPage;
const response = await fetch(
- `${constants.API_BASE}/plugins?page=${page}&limit=${LIMIT}`,
+ withSupportedEditor(
+ `${constants.API_BASE}/plugins?page=${page}&limit=${LIMIT}`,
+ ),
);
const data = await response.json();
filterState.nextPage = page + 1;
From 86656056d1129bf4783c15c94b1105efe67bedae Mon Sep 17 00:00:00 2001
From: Raunak Raj <71929976+bajrangCoder@users.noreply.github.com>
Date: Sat, 21 Feb 2026 18:18:39 +0530
Subject: [PATCH 2/2] feat(editor-theme): add Tomorrow Night and Tomorrow Night
Bright themes
---
src/cm/themes/index.js | 18 +++
src/cm/themes/tomorrowNight.js | 155 ++++++++++++++++++++++++++
src/cm/themes/tomorrowNightBright.js | 158 +++++++++++++++++++++++++++
3 files changed, 331 insertions(+)
create mode 100644 src/cm/themes/tomorrowNight.js
create mode 100644 src/cm/themes/tomorrowNightBright.js
diff --git a/src/cm/themes/index.js b/src/cm/themes/index.js
index a10145e3e..c81be5925 100644
--- a/src/cm/themes/index.js
+++ b/src/cm/themes/index.js
@@ -12,6 +12,10 @@ import solarizedLight, {
} from "./solarizedLight";
import tokyoNight, { config as tokyoNightConfig } from "./tokyoNight";
import tokyoNightDay, { config as tokyoNightDayConfig } from "./tokyoNightDay";
+import tomorrowNight, { config as tomorrowNightConfig } from "./tomorrowNight";
+import tomorrowNightBright, {
+ config as tomorrowNightBrightConfig,
+} from "./tomorrowNightBright";
import vscodeDark, { config as vscodeDarkConfig } from "./vscodeDark";
const oneDarkConfig = {
@@ -207,6 +211,20 @@ addTheme(
() => tokyoNight(),
tokyoNightConfig,
);
+addTheme(
+ tomorrowNightConfig.name,
+ "Tomorrow Night",
+ !!tomorrowNightConfig.dark,
+ () => tomorrowNight(),
+ tomorrowNightConfig,
+);
+addTheme(
+ tomorrowNightBrightConfig.name,
+ "Tomorrow Night Bright",
+ !!tomorrowNightBrightConfig.dark,
+ () => tomorrowNightBright(),
+ tomorrowNightBrightConfig,
+);
addTheme(
monokaiConfig.name,
"Monokai",
diff --git a/src/cm/themes/tomorrowNight.js b/src/cm/themes/tomorrowNight.js
new file mode 100644
index 000000000..ef09700e7
--- /dev/null
+++ b/src/cm/themes/tomorrowNight.js
@@ -0,0 +1,155 @@
+import { HighlightStyle, syntaxHighlighting } from "@codemirror/language";
+import { EditorView } from "@codemirror/view";
+import { tags as t } from "@lezer/highlight";
+
+// Palette adapted from Tomorrow Night (Chris Kempson)
+export const config = {
+ name: "tomorrowNight",
+ dark: true,
+ background: "#1D1F21",
+ foreground: "#C5C8C6",
+ selection: "#373B41",
+ cursor: "#AEAFAD",
+ dropdownBackground: "#1D1F21",
+ dropdownBorder: "#4B4E55",
+ activeLine: "#282A2E",
+ lineNumber: "#4B4E55",
+ lineNumberActive: "#C5C8C6",
+ matchingBracket: "#282A2E",
+ keyword: "#B294BB",
+ storage: "#B294BB",
+ variable: "#CC6666",
+ parameter: "#DE935F",
+ function: "#81A2BE",
+ string: "#B5BD68",
+ constant: "#DE935F",
+ type: "#F0C674",
+ class: "#F0C674",
+ number: "#DE935F",
+ comment: "#969896",
+ heading: "#81A2BE",
+ invalid: "#DF5F5F",
+ regexp: "#CC6666",
+ operator: "#8ABEB7",
+ tag: "#CC6666",
+};
+
+export const tomorrowNightTheme = EditorView.theme(
+ {
+ "&": {
+ color: config.foreground,
+ backgroundColor: config.background,
+ },
+
+ ".cm-content": { caretColor: config.cursor },
+
+ ".cm-cursor, .cm-dropCursor": { borderLeftColor: config.cursor },
+ "&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection":
+ { backgroundColor: config.selection },
+
+ ".cm-panels": {
+ backgroundColor: config.dropdownBackground,
+ color: config.foreground,
+ },
+ ".cm-panels.cm-panels-top": {
+ borderBottom: `1px solid ${config.dropdownBorder}`,
+ },
+ ".cm-panels.cm-panels-bottom": {
+ borderTop: `1px solid ${config.dropdownBorder}`,
+ },
+
+ ".cm-searchMatch": {
+ backgroundColor: config.dropdownBackground,
+ outline: `1px solid ${config.dropdownBorder}`,
+ },
+ ".cm-searchMatch.cm-searchMatch-selected": {
+ backgroundColor: config.selection,
+ },
+
+ ".cm-activeLine": { backgroundColor: config.activeLine },
+ ".cm-selectionMatch": { backgroundColor: config.selection },
+
+ "&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket": {
+ backgroundColor: config.matchingBracket,
+ outline: "none",
+ },
+
+ ".cm-gutters": {
+ backgroundColor: config.background,
+ color: config.foreground,
+ border: "none",
+ },
+ ".cm-activeLineGutter": { backgroundColor: config.background },
+
+ ".cm-lineNumbers .cm-gutterElement": { color: config.lineNumber },
+ ".cm-lineNumbers .cm-activeLineGutter": { color: config.lineNumberActive },
+
+ ".cm-foldPlaceholder": {
+ backgroundColor: "transparent",
+ border: "none",
+ color: config.foreground,
+ },
+ ".cm-tooltip": {
+ border: `1px solid ${config.dropdownBorder}`,
+ backgroundColor: config.dropdownBackground,
+ color: config.foreground,
+ },
+ ".cm-tooltip .cm-tooltip-arrow:before": {
+ borderTopColor: "transparent",
+ borderBottomColor: "transparent",
+ },
+ ".cm-tooltip .cm-tooltip-arrow:after": {
+ borderTopColor: config.foreground,
+ borderBottomColor: config.foreground,
+ },
+ ".cm-tooltip-autocomplete": {
+ "& > ul > li[aria-selected]": {
+ background: config.selection,
+ color: config.foreground,
+ },
+ },
+ },
+ { dark: config.dark },
+);
+
+export const tomorrowNightHighlightStyle = HighlightStyle.define([
+ { tag: t.keyword, color: config.keyword },
+ {
+ tag: [t.name, t.deleted, t.character, t.macroName],
+ color: config.variable,
+ },
+ { tag: [t.propertyName], color: config.function },
+ {
+ tag: [t.processingInstruction, t.string, t.inserted, t.special(t.string)],
+ color: config.string,
+ },
+ { tag: [t.function(t.variableName), t.labelName], color: config.function },
+ {
+ tag: [t.color, t.constant(t.name), t.standard(t.name)],
+ color: config.constant,
+ },
+ { tag: [t.definition(t.name), t.separator], color: config.variable },
+ { tag: [t.className], color: config.class },
+ {
+ tag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace],
+ color: config.number,
+ },
+ { tag: [t.typeName], color: config.type },
+ { tag: [t.operator, t.operatorKeyword], color: config.operator },
+ { tag: [t.url, t.escape, t.regexp, t.link], color: config.regexp },
+ { tag: [t.meta, t.comment], color: config.comment },
+ { tag: t.tagName, color: config.tag },
+ { tag: t.strong, fontWeight: "bold" },
+ { tag: t.emphasis, fontStyle: "italic" },
+ { tag: t.link, textDecoration: "underline" },
+ { tag: t.heading, fontWeight: "bold", color: config.heading },
+ { tag: [t.atom, t.bool, t.special(t.variableName)], color: config.variable },
+ { tag: t.invalid, color: config.invalid },
+ { tag: t.strikethrough, textDecoration: "line-through" },
+]);
+
+export function tomorrowNight() {
+ return [tomorrowNightTheme, syntaxHighlighting(tomorrowNightHighlightStyle)];
+}
+
+export default tomorrowNight;
diff --git a/src/cm/themes/tomorrowNightBright.js b/src/cm/themes/tomorrowNightBright.js
new file mode 100644
index 000000000..7fb6b25bf
--- /dev/null
+++ b/src/cm/themes/tomorrowNightBright.js
@@ -0,0 +1,158 @@
+import { HighlightStyle, syntaxHighlighting } from "@codemirror/language";
+import { EditorView } from "@codemirror/view";
+import { tags as t } from "@lezer/highlight";
+
+// Palette adapted from Tomorrow Night Bright (Chris Kempson)
+export const config = {
+ name: "tomorrowNightBright",
+ dark: true,
+ background: "#000000",
+ foreground: "#DEDEDE",
+ selection: "#424242",
+ cursor: "#9F9F9F",
+ dropdownBackground: "#000000",
+ dropdownBorder: "#343434",
+ activeLine: "#2A2A2A",
+ lineNumber: "#343434",
+ lineNumberActive: "#DEDEDE",
+ matchingBracket: "#2A2A2A",
+ keyword: "#C397D8",
+ storage: "#C397D8",
+ variable: "#D54E53",
+ parameter: "#E78C45",
+ function: "#7AA6DA",
+ string: "#B9CA4A",
+ constant: "#E78C45",
+ type: "#E7C547",
+ class: "#E7C547",
+ number: "#E78C45",
+ comment: "#969896",
+ heading: "#7AA6DA",
+ invalid: "#DF5F5F",
+ regexp: "#D54E53",
+ operator: "#70C0B1",
+ tag: "#D54E53",
+};
+
+export const tomorrowNightBrightTheme = EditorView.theme(
+ {
+ "&": {
+ color: config.foreground,
+ backgroundColor: config.background,
+ },
+
+ ".cm-content": { caretColor: config.cursor },
+
+ ".cm-cursor, .cm-dropCursor": { borderLeftColor: config.cursor },
+ "&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection":
+ { backgroundColor: config.selection },
+
+ ".cm-panels": {
+ backgroundColor: config.dropdownBackground,
+ color: config.foreground,
+ },
+ ".cm-panels.cm-panels-top": {
+ borderBottom: `1px solid ${config.dropdownBorder}`,
+ },
+ ".cm-panels.cm-panels-bottom": {
+ borderTop: `1px solid ${config.dropdownBorder}`,
+ },
+
+ ".cm-searchMatch": {
+ backgroundColor: config.dropdownBackground,
+ outline: `1px solid ${config.dropdownBorder}`,
+ },
+ ".cm-searchMatch.cm-searchMatch-selected": {
+ backgroundColor: config.selection,
+ },
+
+ ".cm-activeLine": { backgroundColor: config.activeLine },
+ ".cm-selectionMatch": { backgroundColor: config.selection },
+
+ "&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket": {
+ backgroundColor: config.matchingBracket,
+ outline: "none",
+ },
+
+ ".cm-gutters": {
+ backgroundColor: config.background,
+ color: config.foreground,
+ border: "none",
+ },
+ ".cm-activeLineGutter": { backgroundColor: config.background },
+
+ ".cm-lineNumbers .cm-gutterElement": { color: config.lineNumber },
+ ".cm-lineNumbers .cm-activeLineGutter": { color: config.lineNumberActive },
+
+ ".cm-foldPlaceholder": {
+ backgroundColor: "transparent",
+ border: "none",
+ color: config.foreground,
+ },
+ ".cm-tooltip": {
+ border: `1px solid ${config.dropdownBorder}`,
+ backgroundColor: config.dropdownBackground,
+ color: config.foreground,
+ },
+ ".cm-tooltip .cm-tooltip-arrow:before": {
+ borderTopColor: "transparent",
+ borderBottomColor: "transparent",
+ },
+ ".cm-tooltip .cm-tooltip-arrow:after": {
+ borderTopColor: config.foreground,
+ borderBottomColor: config.foreground,
+ },
+ ".cm-tooltip-autocomplete": {
+ "& > ul > li[aria-selected]": {
+ background: config.selection,
+ color: config.foreground,
+ },
+ },
+ },
+ { dark: config.dark },
+);
+
+export const tomorrowNightBrightHighlightStyle = HighlightStyle.define([
+ { tag: t.keyword, color: config.keyword },
+ {
+ tag: [t.name, t.deleted, t.character, t.macroName],
+ color: config.variable,
+ },
+ { tag: [t.propertyName], color: config.function },
+ {
+ tag: [t.processingInstruction, t.string, t.inserted, t.special(t.string)],
+ color: config.string,
+ },
+ { tag: [t.function(t.variableName), t.labelName], color: config.function },
+ {
+ tag: [t.color, t.constant(t.name), t.standard(t.name)],
+ color: config.constant,
+ },
+ { tag: [t.definition(t.name), t.separator], color: config.variable },
+ { tag: [t.className], color: config.class },
+ {
+ tag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace],
+ color: config.number,
+ },
+ { tag: [t.typeName], color: config.type },
+ { tag: [t.operator, t.operatorKeyword], color: config.operator },
+ { tag: [t.url, t.escape, t.regexp, t.link], color: config.regexp },
+ { tag: [t.meta, t.comment], color: config.comment },
+ { tag: t.tagName, color: config.tag },
+ { tag: t.strong, fontWeight: "bold" },
+ { tag: t.emphasis, fontStyle: "italic" },
+ { tag: t.link, textDecoration: "underline" },
+ { tag: t.heading, fontWeight: "bold", color: config.heading },
+ { tag: [t.atom, t.bool, t.special(t.variableName)], color: config.variable },
+ { tag: t.invalid, color: config.invalid },
+ { tag: t.strikethrough, textDecoration: "line-through" },
+]);
+
+export function tomorrowNightBright() {
+ return [
+ tomorrowNightBrightTheme,
+ syntaxHighlighting(tomorrowNightBrightHighlightStyle),
+ ];
+}
+
+export default tomorrowNightBright;