Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
21bf62a
Fix BLE disconnect crash and event listener leaks
makermelissa-piclaw Apr 23, 2026
83ba91c
Merge pull request #475 from makermelissa-piclaw/fix/ble-disconnect-c…
makermelissa Apr 29, 2026
7eda979
Merge branch 'main' of https://github.com/circuitpython/web-editor in…
makermelissa Apr 29, 2026
d3bb94f
Add CircuitPython module highlighting overlay
makermelissa-piclaw Apr 29, 2026
38225c8
Merge remote-tracking branch 'origin/beta' into feature/circuitpython…
makermelissa-piclaw Apr 29, 2026
e00c925
Match adafruit_ libraries by prefix instead of explicit list
makermelissa-piclaw Apr 29, 2026
5350598
Also wildcard-match circuitpython_ libraries
makermelissa-piclaw Apr 29, 2026
c65d600
Sync core module list with upstream shared-bindings
makermelissa-piclaw Apr 29, 2026
b56f519
Clarify neopixel/simpleio comment
makermelissa-piclaw Apr 29, 2026
c57e60c
Use Prec.highest so cp-module color wins over variableName
makermelissa-piclaw Apr 29, 2026
8a712b5
Merge pull request #481 from makermelissa-piclaw/feature/circuitpytho…
makermelissa Apr 29, 2026
1104e36
Merge branch 'main' of https://github.com/circuitpython/web-editor in…
makermelissa May 1, 2026
57a00e0
Merge branch 'beta' of https://github.com/circuitpython/web-editor in…
makermelissa May 1, 2026
31e840c
Support Arduino-style labeled multi-value plotter data
makermelissa-piclaw May 5, 2026
1b26581
Merge branch 'main' of https://github.com/circuitpython/web-editor in…
makermelissa May 5, 2026
04e13f8
Make plotter colors and legend readable on both themes
makermelissa-piclaw May 5, 2026
88e9202
Merge pull request #487 from makermelissa-piclaw/fix/issue-457-multi-…
makermelissa May 6, 2026
3bee588
Suggest firmware updates in device info dialogs (refs #357)
makermelissa-piclaw May 6, 2026
67012d6
Auto-open Device Info dialog after every fresh connect
makermelissa-piclaw May 6, 2026
ffcc069
Fix USB: flip connected state before calling loadEditor
makermelissa-piclaw May 6, 2026
0a843b8
Add syntax highlighting for additional file types
makermelissa-piclaw May 6, 2026
214b6ec
Refresh language plugin on Save As, fix light-theme contrast
makermelissa-piclaw May 6, 2026
4179361
Move language switch into setFilename so Save As actually updates it
makermelissa-piclaw May 6, 2026
1357e69
Merge pull request #489 from makermelissa-piclaw/feature/issue-361-la…
makermelissa May 6, 2026
4db63e2
Merge branch 'main' of https://github.com/circuitpython/web-editor in…
makermelissa May 8, 2026
e329791
Merge pull request #488 from makermelissa-piclaw/fix/issue-357-firmwa…
makermelissa May 19, 2026
4df2094
Merge branch 'main' of https://github.com/circuitpython/web-editor in…
makermelissa May 19, 2026
ae618c0
Merge branch 'beta' of https://github.com/circuitpython/web-editor in…
makermelissa May 19, 2026
243f32a
Initial plan for resolving merge conflicts
Copilot Jun 17, 2026
21b94e4
Resolve merge conflicts between beta and main branches
Copilot Jun 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,7 @@ <h1>Select USB Host Folder</h1>
</tr>
</tbody>
</table>
<div id="firmware-update" class="firmware-update-suggestion-container"></div>
<h3>More network devices<i class="refresh fa-solid fa-sync-alt" title="Refresh Device List"></i></h3>
<div id="devices"></div>
<div class="buttons centered">
Expand Down Expand Up @@ -434,6 +435,7 @@ <h3>More network devices<i class="refresh fa-solid fa-sync-alt" title="Refresh D
</tr>
</tbody>
</table>
<div id="firmware-update" class="firmware-update-suggestion-container"></div>
<div class="buttons centered">
<button class="purple-button ok-button">Close</button>
</div>
Expand Down
221 changes: 221 additions & 0 deletions js/common/circuitpython_highlight.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
// CircuitPython syntax highlighting overlay for CodeMirror 6.
//
// CodeMirror 6 dropped the simple `extra_keywords` mechanism that CM5 had,
// so instead of forking @codemirror/lang-python we layer extra decorations
// on top of the existing Python syntax tree. We walk the tree inside the
// viewport, find identifier nodes whose text matches a CircuitPython name,
// and tag them with a CSS class that the theme can style.

import { ViewPlugin, Decoration } from "@codemirror/view";
import { syntaxTree } from "@codemirror/language";
import { Prec } from "@codemirror/state";

// Core/built-in CircuitPython modules. These are the identifiers that show
// up in `import foo` / `from foo import ...` inside CircuitPython code.
//
// Sourced from the upstream `shared-bindings/` directory in
// adafruit/circuitpython, plus port-specific bindings that are widely used
// (espidf/espnow/espulp on ESP, picodvi/rp2pio on RP2). Standard-Python
// modules that CircuitPython also exposes (math, os, time, random, struct,
// hashlib, ipaddress, locale, __future__) are intentionally omitted — they
// aren't CircuitPython-specific and highlighting them as such would be
// noisy in regular Python code shown in the editor.
//
// Underscore-prefixed internal bindings (_bleio, _eve, _pew, _pixelmap,
// _stage) are also omitted; users access those via the corresponding
// `adafruit_*` libraries which are matched by the prefix wildcard below.
//
// Third-party Adafruit libraries are matched by the `adafruit_` prefix
// instead of being listed individually, and community-bundle libraries by
// the `circuitpython_` prefix, so this set only needs updating when a new
// shared binding lands upstream.
const CIRCUITPYTHON_CORE_MODULES = new Set([
"aesio",
"alarm",
"analogbufio",
"analogio",
"atexit",
"audiobusio",
"audiocore",
"audiodelays",
"audiofilters",
"audiofreeverb",
"audioio",
"audiomixer",
"audiomp3",
"audiopwmio",
"audiospeed",
"aurora_epaper",
"bitbangio",
"bitmapfilter",
"bitmaptools",
"bitops",
"board",
"busdisplay",
"busio",
"camera",
"canio",
"codeop",
"countio",
"digitalio",
"displayio",
"dotclockframebuffer",
"dualbank",
"epaperdisplay",
"espidf",
"espnow",
"espulp",
"floppyio",
"fontio",
"fourwire",
"framebufferio",
"frequencyio",
"getpass",
"gifio",
"gnss",
"i2cdisplaybus",
"i2cioexpander",
"i2ctarget",
"imagecapture",
"is31fl3741",
"jpegio",
"keypad",
"keypad_demux",
"lvfontio",
"max3421e",
"mcp4822",
"mdns",
"memorymap",
"memorymonitor",
"microcontroller",
"mipidsi",
"msgpack",
"neopixel_write",
"nvm",
"onewireio",
"paralleldisplaybus",
"picodvi",
"ps2io",
"pulseio",
"pwmio",
"qrio",
"qspibus",
"rainbowio",
"rclcpy",
"rgbmatrix",
"rotaryio",
"rp2pio",
"rtc",
"sdcardio",
"sdioio",
"sharpdisplay",
"socketpool",
"spitarget",
"ssl",
"storage",
"supervisor",
"synthio",
"terminalio",
"tilepalettemapper",
"touchio",
"traceback",
"uheap",
"ulab",
"usb",
"usb_cdc",
"usb_hid",
"usb_host",
"usb_midi",
"usb_video",
"ustack",
"vectorio",
"warnings",
"watchdog",
"wifi",
"zlib",

// Early Adafruit-maintained libraries that predate the `adafruit_`
// naming convention and shipped without a prefix. Listed explicitly
// because the prefix wildcard below can't catch them.
"neopixel",
"simpleio",
]);

// Returns true when `name` is a CircuitPython module worth highlighting.
// Wildcard-matches anything starting with `adafruit_` (Adafruit-maintained
// libraries) or `circuitpython_` (community bundle libraries) so new
// libraries light up automatically without touching this file. Both
// prefixes are distinctive enough that false positives against ordinary
// Python code are essentially nil.
function isCircuitPythonModule(name) {
if (CIRCUITPYTHON_CORE_MODULES.has(name)) return true;
if (name.startsWith("adafruit_") && name.length > "adafruit_".length) {
return true;
}
if (
name.startsWith("circuitpython_") &&
name.length > "circuitpython_".length
) {
return true;
}
return false;
}

const moduleMark = Decoration.mark({ class: "tok-cp-module" });

// Build the decoration set for the part of the document currently visible.
// Walking only visible ranges keeps this cheap on big files.
function buildDecorations(view) {
const builder = [];
for (const { from, to } of view.visibleRanges) {
syntaxTree(view.state).iterate({
from,
to,
enter(node) {
// We care about identifier-like leaves only. Lezer Python emits
// `VariableName` for bare identifiers (including module names
// in `import foo` and `from foo import ...`). Module names
// accessed as attributes (e.g. `adafruit_io.MQTT`) come in as
// `VariableName` for the leftmost part, then `PropertyName`
// children — we only mark the root reference.
if (node.name !== "VariableName") return;
const text = view.state.doc.sliceString(node.from, node.to);
if (isCircuitPythonModule(text)) {
builder.push(moduleMark.range(node.from, node.to));
}
},
});
}
// Decoration ranges must be sorted by `from`, which they already are
// because we iterate the tree in document order.
return Decoration.set(builder);
}

// ViewPlugin keeps decorations in sync with viewport / document changes.
const circuitpythonHighlightPlugin = ViewPlugin.fromClass(
class {
constructor(view) {
this.decorations = buildDecorations(view);
}
update(update) {
if (
update.docChanged ||
update.viewportChanged ||
syntaxTree(update.startState) !== syntaxTree(update.state)
) {
this.decorations = buildDecorations(update.view);
}
}
},
{
decorations: (v) => v.decorations,
},
);

// Wrap the plugin with Prec.highest so its decoration nests inside the
// classHighlighter span. CodeMirror renders overlapping mark decorations
// as nested spans where higher-precedence decorations end up closer to
// the text. The inner span’s `color` is what the user sees, so making
// `tok-cp-module` the inner class is what lets our pink override the
// underlying `tok-variableName` blue without resorting to !important.
export const circuitpythonHighlight = Prec.highest(circuitpythonHighlightPlugin);
9 changes: 9 additions & 0 deletions js/common/dialogs.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {sleep, isIp, switchDevice} from './utilities.js';
import {renderFirmwareSuggestions} from './firmware-check.js';
import * as focusTrap from 'focus-trap';

const SELECTOR_CLOSE_BUTTON = ".popup-modal__close";
Expand Down Expand Up @@ -357,6 +358,10 @@ class DiscoveryModal extends GenericModal {
this._currentModal.querySelector("#mcuname").textContent = deviceInfo.mcu_name;
this._currentModal.querySelector("#boardid").textContent = deviceInfo.board_id;
this._currentModal.querySelector("#uid").textContent = deviceInfo.uid;
const updateContainer = this._currentModal.querySelector("#firmware-update");
if (updateContainer) {
renderFirmwareSuggestions(updateContainer, deviceInfo);
}
}

async _refreshDevices() {
Expand Down Expand Up @@ -417,6 +422,10 @@ class DeviceInfoModal extends GenericModal {
this._currentModal.querySelector("#mcuname").textContent = deviceInfo.mcu_name;
this._currentModal.querySelector("#boardid").textContent = deviceInfo.board_id;
this._currentModal.querySelector("#uid").textContent = deviceInfo.uid;
const updateContainer = this._currentModal.querySelector("#firmware-update");
if (updateContainer) {
renderFirmwareSuggestions(updateContainer, deviceInfo);
}
}

async open(workflow, documentState) {
Expand Down
Loading