diff --git a/resources/handoff-prompt-ai-agent.md b/resources/handoff-prompt-ai-agent.md new file mode 100644 index 0000000..d1b6e1a --- /dev/null +++ b/resources/handoff-prompt-ai-agent.md @@ -0,0 +1,178 @@ +# Agent Handoff: Obsidian CRBasic Syntax Highlighting Plugin + +## Objective + +Create an Obsidian plugin ("CodeBlock CRBasic") that provides syntax highlighting for CRBasic code in markdown fenced code blocks (` ```crbasic `). CRBasic is the programming language for Campbell Scientific dataloggers. + +## Reference Material + +The token lists (keywords, built-in functions, operators, constants, preprocessor directives) were ported from the **VSCode CRBasic extension** by Campbell Scientific: + +- Marketplace: https://marketplace.visualstudio.com/items?itemName=daiwalkr.cr-basic-ms-vscode +- The extension's VSIX can be downloaded and extracted to get `syntaxes/cr-basic.tmLanguage.json` — a TextMate grammar file containing all token patterns. +- The extension's repo is private GitLab: `https://gitlab.com/cs-global/research-and-development/product-testing-and-compliance/ptg-software/vscode-crbasic-plugin` + +## Architecture + +The plugin needs **two separate highlighting engines** because Obsidian uses different renderers for its two view modes: + +| View Mode | Engine | Approach | +|---|---|---| +| **Edit / Live Preview** | CodeMirror 6 (via HyperMD) | CM6 `ViewPlugin` with `Decoration.mark()` | +| **Reading View** | PrismJS | `registerMarkdownCodeBlockProcessor()` | + +### File Structure + +``` +markdown-codeblock-crbasic/ +├── manifest.json # Obsidian plugin manifest (id: "codeblock-crbasic") +├── package.json # npm config + build scripts +├── tsconfig.json # TypeScript config +├── esbuild.config.mjs # Bundles src/ → main.js (CJS, externals for obsidian/CM6) +├── styles.css # Empty (uses Obsidian's built-in theme tokens) +├── src/ +│ ├── main.ts # Plugin entry — registers CM6 extension + code block processor +│ ├── crbasic-tokens.ts # Shared keyword/function/operator/constant lists +│ ├── tokenizer.ts # Line-level tokenizer shared by both engines +│ └── cm6-crbasic.ts # CM6 ViewPlugin implementation +``` + +### Shared Tokenizer (`tokenizer.ts`) + +A single line-level tokenizer is shared by both the CM6 ViewPlugin and the Reading View processor. It takes a line of text + an offset, and returns an array of `{from, to, type}` tokens. Token types: `keyword`, `builtin`, `operator`, `atom`, `number`, `string`, `comment`, `meta`. + +CRBasic is **case-insensitive** — all keyword matching uses lowercased lookup sets. + +### Token Lists (`crbasic-tokens.ts`) + +All token lists are extracted from the TextMate grammar into a single module. Both engines import from here. Categories: + +- **Keywords** (`keyword.control`): `BeginProg`, `EndProg`, `If`, `Else`, `Scan`, `Sub`, `Public`, `Dim`, `Const`, etc. +- **Preprocessor** (`keyword.control.preprocessor`): `#If`, `#Else`, `#EndIf`, etc. (matched WITH the `#` prefix) +- **Logical operators** (`keyword.operator.logical`): `AND`, `OR`, `NOT`, `MOD`, `XOR`, `IMP`, `INTDV` +- **Constants** (`constant.character` + `constant.language`): `True`, `False`, `As`, `LoggerType`, etc. +- **Built-in functions** (`entity.name.function.*`): ~300 functions across CDM, DataTable, Measurements, Math, FileIO, Internet, PakBus, SDM, Serial, String categories — deduplicated into a flat array. + +## Critical Lessons Learned + +### 1. Obsidian's CM6 uses HyperMD — NOT standard CM6 markdown nodes + +**The problem**: Standard CM6 markdown uses syntax tree nodes named `FencedCode`, `CodeInfo`, `CodeText`. Obsidian's HyperMD fork uses completely different names: `HyperMD-codeblock-begin`, `HyperMD-codeblock-bg`, `hmd-codeblock`, `HyperMD-codeblock-end`, etc. + +**The solution**: Don't use `syntaxTree()` to find code blocks. Instead, scan the document text line-by-line looking for ` ```crbasic ` opening fences and ` ``` ` closing fences. Collect the content lines between them and tokenize. This is robust regardless of Obsidian's internal CM6 node naming. + +```typescript +// Correct approach — text scanning: +for (let i = 1; i <= doc.lines; i++) { + const line = doc.line(i); + if (/^```crbasic\s*$/i.test(line.text.trim())) { + inCrbasicBlock = true; ... + } +} +``` + +### 2. `StreamLanguage.define()` does NOT work for code block highlighting + +**The problem**: `StreamLanguage.define()` creates a full CM6 language. Registering it via `registerEditorExtension(lang.extension)` would try to make it the **top-level** editor language, replacing markdown. There is no public Obsidian API to register a new nested code-block language with CM6's markdown parser. + +**The solution**: Use a `ViewPlugin` that applies `Decoration.mark()` with `cm-*` CSS classes (e.g., `cm-keyword`, `cm-string`, `cm-builtin`). Obsidian themes already style these classes, so no custom CSS is needed. + +### 3. `window.hljs` is NOT available at plugin load time + +**The problem**: Obsidian does not expose `window.hljs` when plugins load. Attempting `window.hljs.registerLanguage()` in `onload()` silently fails. + +**The solution**: Don't use hljs at all. Use `registerMarkdownCodeBlockProcessor("crbasic", callback)` instead. This takes over the rendering of ` ```crbasic ` blocks entirely — you create your own `
` DOM structure with highlighted `` elements.
+
+### 4. Obsidian Reading View uses PrismJS, not highlight.js
+
+**The problem**: Internet sources (and initial assumptions) suggested Obsidian uses highlight.js. It actually uses **PrismJS** for Reading View rendering.
+
+**The solution**: Since we use `registerMarkdownCodeBlockProcessor` (which bypasses PrismJS entirely), we just need our `` elements to use PrismJS-compatible CSS classes so Obsidian themes style them correctly:
+
+```typescript
+// PrismJS class format: TWO separate classes, "token" + type
+// Obsidian's createEl cls parameter takes an ARRAY:
+code.createEl("span", {
+  cls: ["token", "keyword"],   // ✓ correct
+  text: "BeginProg",
+});
+
+// NOT a single string (won't be split into two classes):
+code.createEl("span", {
+  cls: "token keyword",        // ✗ wrong — single class
+  text: "BeginProg",
+});
+```
+
+Token type mapping for PrismJS:
+| Our type | PrismJS classes |
+|---|---|
+| `keyword` | `["token", "keyword"]` |
+| `builtin` | `["token", "function"]` |
+| `operator` | `["token", "operator"]` |
+| `atom` | `["token", "boolean"]` |
+| `number` | `["token", "number"]` |
+| `string` | `["token", "string"]` |
+| `comment` | `["token", "comment"]` |
+| `meta` | `["token", "keyword"]` |
+
+### 5. Reading View DOM structure matters
+
+The `
` and `` elements need specific classes for Obsidian themes to style the code block background and font correctly:
+
+```typescript
+const pre = el.createEl("pre", { cls: ["language-crbasic"] });
+const code = pre.createEl("code", { cls: ["language-crbasic", "is-loaded"] });
+```
+
+The `is-loaded` class on `` is important — Obsidian uses it to determine that code highlighting has been applied.
+
+### 6. CM6 token class names
+
+For Edit mode, use standard CodeMirror CSS classes via `Decoration.mark({ class: "cm-keyword" })`. Obsidian themes already provide colors for: `cm-keyword`, `cm-builtin`, `cm-string`, `cm-comment`, `cm-number`, `cm-operator`, `cm-atom`, `cm-meta`.
+
+### 7. Build configuration
+
+All `@codemirror/*`, `@lezer/*`, `obsidian`, and `electron` packages must be listed as **externals** in esbuild. Obsidian provides these at runtime. Bundling them causes conflicts.
+
+Use `--legacy-peer-deps` when running `npm install` because the `obsidian` npm package has peer dependency conflicts with `@codemirror/state` versions.
+
+### 8. Debug technique
+
+To debug CM6 syntax tree issues, add a command that dumps node names:
+
+```typescript
+this.addCommand({
+  id: "dump-syntax-tree",
+  name: "Dump CM6 syntax tree (debug)",
+  callback: () => {
+    const view = this.app.workspace.getActiveViewOfType(MarkdownView);
+    const cm = (view.editor as any).cm;
+    const tree = syntaxTree(cm.state);
+    const names = new Set();
+    tree.iterate({ enter: (node) => { names.add(node.name); } });
+    console.log("Node names:", [...names].sort());
+  },
+});
+```
+
+Open Obsidian DevTools with `Ctrl+Shift+I`, run the command from the command palette (`Ctrl+P`).
+
+## Build & Deploy
+
+```bash
+cd markdown-codeblock-crbasic
+npm install --legacy-peer-deps
+npm run build
+# Copy main.js, manifest.json, styles.css to:
+# /.obsidian/plugins/codeblock-crbasic/
+```
+
+Reload Obsidian with `Ctrl+R` (with DevTools open) or restart the app.
+
+## What's NOT In Scope
+
+- Autocomplete, snippets, diagnostics, formatting (IDE features from the VSCode extension)
+- Semantic highlighting (requires a full parser)
+- File associations (Obsidian doesn't open `.cr1x` files directly)
+- Only one language alias is registered: `crbasic`
diff --git a/resources/readme-initial-codeblock-crbasic.html b/resources/readme-initial-codeblock-crbasic.html
new file mode 100644
index 0000000..56be185
--- /dev/null
+++ b/resources/readme-initial-codeblock-crbasic.html
@@ -0,0 +1,588 @@
+
+
+
+
+
+CodeBlock CRBasic — Installation Guide
+
+
+
+
+
+

CodeBlock CRBasic

+

Syntax highlighting for CRBasic code blocks in Obsidian.
A simple guide to install and use the plugin.

+
+ +
+ + + + +
+ 1 +

What You Need

+
+

The plugin is made up of 3 small files. That's it — no installer, no app store, just 3 files:

+ +
+
+ 📄 + main.js + The plugin code (~16 KB) +
+
+ 📄 + manifest.json + Tells Obsidian the plugin's name and version +
+
+ 📄 + styles.css + Style sheet (mostly empty — uses your theme's colors) +
+
+ +

These files should have been provided to you alongside this guide. If you're missing any of them, ask the person who shared the plugin with you.

+
+
+ + + + +
+ 2 +

Find Your Obsidian Vault Folder

+
+

Your vault is the folder on your computer where Obsidian stores all your notes. You chose this folder when you first set up Obsidian.

+ +

Not sure where it is? Open Obsidian, then:

+
    +
  1. Click the Settings gear icon (bottom-left corner)
  2. +
  3. Look at the very bottom of the left sidebar — it shows your vault name
  4. +
  5. Click Files & Links in the sidebar
  6. +
  7. The path shown at the top is your vault folder
  8. +
+ +

Or simply: open File Explorer (Windows) or Finder (Mac) and navigate to wherever your notes live.

+ +
+
Tip
+

Inside your vault folder, there's a hidden folder called .obsidian. On Windows, you may need to turn on "Show hidden items" in File Explorer's View menu to see it.

+
+
+
+ + + + +
+ 3 +

Create the Plugin Folder

+
+

Inside your vault, navigate to this path:

+ +
+ Your Vault › + .obsidian › + plugins › + codeblock-crbasic +
+ +

You may need to create some of these folders if they don't exist yet:

+
    +
  • The plugins folder should already exist if you've ever used a community plugin. If it doesn't, create it.
  • +
  • Create a new folder inside plugins called exactly: codeblock-crbasic
  • +
+ +

For example, if your vault is at C:\Users\Me\Documents\MyVault, the full path would be:

+ +
+ C:\Users\Me\Documents\MyVault\.obsidian\plugins\codeblock-crbasic +
+
+
+ + + + +
+ 4 +

Copy the 3 Files

+
+

Copy main.js, manifest.json, and styles.css into the codeblock-crbasic folder you just created.

+ +

When you're done, the folder should look like this:

+ +
+
+ 📁 + codeblock-crbasic/ + +
+
+ 📄 + main.js + +
+
+ 📄 + manifest.json + +
+
+ 📄 + styles.css + +
+
+ +

That's all the files. Nothing else to download or install.

+
+
+ + + + +
+ 5 +

Enable the Plugin in Obsidian

+
+
    +
  1. Restart Obsidian (close it completely and re-open it)
  2. +
  3. Open Settings (gear icon, bottom-left)
  4. +
  5. Click Community plugins in the left sidebar
  6. +
  7. If you see a message about Restricted Mode, click "Turn on community plugins"
  8. +
  9. You should see "CodeBlock CRBasic" in the list of installed plugins
  10. +
  11. Toggle the switch next to it so it turns on (blue/purple)
  12. +
+ +
+
Don't see it?
+

Double-check that all 3 files are in the correct folder, and that the folder is named exactly codeblock-crbasic (no typos, no extra spaces). Then restart Obsidian again.

+
+
+
+ +
+ + + + +
+ 6 +

How to Use It

+
+

In any Obsidian note, create a code block by typing three backticks followed by crbasic, then your code, then three backticks to close:

+ +
+ +
```crbasic
+'CR1000X Example Program
+Public LoggerTemp, BattV
+Public Dest(9)
+
+DataTable (Test,1,-1)
+  DataInterval (0,15,Sec,10)
+  Minimum (1,BattV,FP2,False,False)
+  Sample (1,LoggerTemp,FP2)
+EndTable
+
+BeginProg
+  Scan (1,Sec,0,0)
+    PanelTemp (LoggerTemp,15000)
+    Battery (BattV)
+    CallTable Test
+  NextScan
+EndProg
+```
+
+ +

Once you type this into a note, you'll see the code light up with colors:

+ +
+
'CR1000X Example Program
+Public LoggerTemp, BattV
+Public Dest(9)
+
+DataTable (Test,1,-1)
+  DataInterval (0,15,Sec,10)
+  Minimum (1,BattV,FP2,False,False)
+  Sample (1,LoggerTemp,FP2)
+EndTable
+
+BeginProg
+  Scan (1,Sec,0,0)
+    PanelTemp (LoggerTemp,15000)
+    Battery (BattV)
+    CallTable Test
+  NextScan
+EndProgPreview
+
+ +

What gets colored:

+
    +
  • KeywordsBeginProg, EndProg, Scan, NextScan, Public, Dim, If, Else, etc.
  • +
  • Built-in functionsPanelTemp, Battery, DataTable, DataInterval, VoltDiff, Average, etc.
  • +
  • Numbers1, 15000, 3.14
  • +
  • Strings"M!", "Hello"
  • +
  • Comments — anything after an apostrophe: 'this is a comment
  • +
  • ConstantsTrue, False
  • +
+ +
+
Both views work
+

Syntax highlighting works in both Edit mode (where you type) and Reading mode (the clean preview). Toggle between them with the book/pencil icon in the top-right corner of a note.

+
+
+
+ +
+ + + + +
+ 7 +

Quick Test

+
+

Want to make sure everything is working? Follow this 30-second test:

+ +
    +
  1. Open Obsidian
  2. +
  3. Create a new note (click the "New note" icon or press Ctrl+N)
  4. +
  5. Click the Copy button on the example code above
  6. +
  7. Paste it into the note (Ctrl+V)
  8. +
  9. You should immediately see colored keywords in the code block
  10. +
  11. Switch to Reading mode (click the book icon top-right) — the colors should appear there too
  12. +
+ +

If you see colors — you're all set!

+
+
+ +
+ + + + +
+ ? +

Troubleshooting

+
+ +

No colors showing up at all?

+
    +
  • Make sure the code block starts with exactly ```crbasic (three backticks, then the word crbasic, no space between them)
  • +
  • Make sure the plugin is enabled in Settings → Community plugins
  • +
  • Try restarting Obsidian completely (close and re-open)
  • +
+ +

Plugin doesn't appear in the plugin list?

+
    +
  • The folder must be named exactly codeblock-crbasic
  • +
  • All 3 files (main.js, manifest.json, styles.css) must be directly inside that folder — not in a sub-folder
  • +
  • Make sure Community Plugins / Restricted Mode is turned off
  • +
+ +

Can't find the .obsidian folder?

+
    +
  • Windows: In File Explorer, click View in the top menu, then check "Hidden items"
  • +
  • Mac: In Finder, press Cmd + Shift + . (period) to show hidden files
  • +
  • Linux: Press Ctrl + H in your file manager
  • +
+
+
+ +
+ +
+ CodeBlock CRBasic v1.0.0 — Syntax highlighting for Campbell Scientific CRBasic +
+ + + + +