mirror of
https://github.com/utleysam/codeblock-crbasic.git
synced 2026-07-22 07:32:57 +00:00
Initial commit resources folder
This commit is contained in:
parent
114cdcb29b
commit
2f98849242
2 changed files with 766 additions and 0 deletions
178
resources/handoff-prompt-ai-agent.md
Normal file
178
resources/handoff-prompt-ai-agent.md
Normal file
|
|
@ -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 `<pre><code>` DOM structure with highlighted `<span>` 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 `<span>` 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 `<pre>` and `<code>` 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 `<code>` 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<string>();
|
||||
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:
|
||||
# <vault>/.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`
|
||||
588
resources/readme-initial-codeblock-crbasic.html
Normal file
588
resources/readme-initial-codeblock-crbasic.html
Normal file
|
|
@ -0,0 +1,588 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>CodeBlock CRBasic — Installation Guide</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg: #1e1e2e;
|
||||
--surface: #262637;
|
||||
--border: #3a3a52;
|
||||
--text: #e0e0e8;
|
||||
--text-muted: #9898b0;
|
||||
--accent: #b48ead;
|
||||
--accent2: #88c0d0;
|
||||
--green: #a3be8c;
|
||||
--orange: #d08770;
|
||||
--yellow: #ebcb8b;
|
||||
--red: #bf616a;
|
||||
--code-bg: #1a1a28;
|
||||
}
|
||||
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
line-height: 1.7;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* ── Header ─────────────────────────────────── */
|
||||
header {
|
||||
background: linear-gradient(135deg, #2e2e48 0%, #1e1e2e 100%);
|
||||
border-bottom: 1px solid var(--border);
|
||||
padding: 3rem 2rem 2.5rem;
|
||||
text-align: center;
|
||||
}
|
||||
header h1 {
|
||||
font-size: 2.2rem;
|
||||
font-weight: 700;
|
||||
color: var(--accent);
|
||||
margin-bottom: 0.4rem;
|
||||
}
|
||||
header p {
|
||||
color: var(--text-muted);
|
||||
font-size: 1.05rem;
|
||||
max-width: 540px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* ── Main container ─────────────────────────── */
|
||||
main {
|
||||
max-width: 760px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem 1.5rem 4rem;
|
||||
}
|
||||
|
||||
/* ── Steps ──────────────────────────────────── */
|
||||
.step {
|
||||
margin-bottom: 2.5rem;
|
||||
}
|
||||
.step-number {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 50%;
|
||||
background: var(--accent);
|
||||
color: #1e1e2e;
|
||||
font-weight: 700;
|
||||
font-size: 1.1rem;
|
||||
margin-right: 0.6rem;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.step h2 {
|
||||
display: inline;
|
||||
font-size: 1.35rem;
|
||||
vertical-align: middle;
|
||||
color: var(--text);
|
||||
}
|
||||
.step-body {
|
||||
margin-top: 0.8rem;
|
||||
margin-left: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
/* ── Text ───────────────────────────────────── */
|
||||
p { margin-bottom: 0.9rem; }
|
||||
|
||||
strong { color: var(--accent2); font-weight: 600; }
|
||||
|
||||
a { color: var(--accent2); text-decoration: underline; }
|
||||
a:hover { color: var(--accent); }
|
||||
|
||||
/* ── Lists ──────────────────────────────────── */
|
||||
ul, ol {
|
||||
margin: 0.5rem 0 1rem 1.5rem;
|
||||
}
|
||||
li { margin-bottom: 0.35rem; }
|
||||
|
||||
/* ── Code ───────────────────────────────────── */
|
||||
code {
|
||||
font-family: "Cascadia Code", "Fira Code", "JetBrains Mono", Consolas, monospace;
|
||||
background: var(--code-bg);
|
||||
padding: 0.15em 0.4em;
|
||||
border-radius: 4px;
|
||||
font-size: 0.92em;
|
||||
color: var(--yellow);
|
||||
}
|
||||
|
||||
pre {
|
||||
background: var(--code-bg);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
padding: 1.1rem 1.3rem;
|
||||
overflow-x: auto;
|
||||
margin: 0.8rem 0 1.2rem;
|
||||
position: relative;
|
||||
}
|
||||
pre code {
|
||||
background: none;
|
||||
padding: 0;
|
||||
font-size: 0.88rem;
|
||||
line-height: 1.65;
|
||||
color: var(--text);
|
||||
}
|
||||
pre .label {
|
||||
position: absolute;
|
||||
top: 0.45rem;
|
||||
right: 0.7rem;
|
||||
font-size: 0.7rem;
|
||||
color: var(--text-muted);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
/* Syntax colors inside example */
|
||||
.kw { color: #c678dd; }
|
||||
.fn { color: #61afef; }
|
||||
.num { color: #d19a66; }
|
||||
.str { color: #98c379; }
|
||||
.cmt { color: #7f848e; font-style: italic; }
|
||||
.const { color: #e5c07b; }
|
||||
|
||||
/* ── File list box ──────────────────────────── */
|
||||
.file-list {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
padding: 1rem 1.3rem;
|
||||
margin: 0.8rem 0 1.2rem;
|
||||
}
|
||||
.file-list .file-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0.45rem 0;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
.file-list .file-row:last-child { border-bottom: none; }
|
||||
.file-icon {
|
||||
width: 28px;
|
||||
text-align: center;
|
||||
margin-right: 0.7rem;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
.file-name {
|
||||
font-family: "Cascadia Code", Consolas, monospace;
|
||||
font-size: 0.92rem;
|
||||
color: var(--accent2);
|
||||
flex: 1;
|
||||
}
|
||||
.file-desc {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.82rem;
|
||||
}
|
||||
|
||||
/* ── Path box ───────────────────────────────── */
|
||||
.path-box {
|
||||
background: var(--code-bg);
|
||||
border: 1px dashed var(--border);
|
||||
border-radius: 8px;
|
||||
padding: 0.9rem 1.2rem;
|
||||
margin: 0.8rem 0 1.2rem;
|
||||
font-family: "Cascadia Code", Consolas, monospace;
|
||||
font-size: 0.88rem;
|
||||
color: var(--yellow);
|
||||
word-break: break-all;
|
||||
}
|
||||
.path-box .path-dim { color: var(--text-muted); }
|
||||
.path-box .path-highlight { color: var(--green); font-weight: 600; }
|
||||
|
||||
/* ── Callout / tip box ──────────────────────── */
|
||||
.callout {
|
||||
border-left: 4px solid var(--accent);
|
||||
background: var(--surface);
|
||||
border-radius: 0 8px 8px 0;
|
||||
padding: 0.9rem 1.2rem;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
.callout.tip { border-left-color: var(--green); }
|
||||
.callout.warn { border-left-color: var(--orange); }
|
||||
.callout-title {
|
||||
font-weight: 700;
|
||||
font-size: 0.85rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.04em;
|
||||
margin-bottom: 0.3rem;
|
||||
}
|
||||
.callout.tip .callout-title { color: var(--green); }
|
||||
.callout.warn .callout-title { color: var(--orange); }
|
||||
|
||||
/* ── Copy button ────────────────────────────── */
|
||||
.copy-wrap { position: relative; }
|
||||
.copy-btn {
|
||||
position: absolute;
|
||||
top: 0.5rem;
|
||||
right: 0.6rem;
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 5px;
|
||||
color: var(--text-muted);
|
||||
font-size: 0.72rem;
|
||||
padding: 0.25rem 0.55rem;
|
||||
cursor: pointer;
|
||||
transition: background 0.15s, color 0.15s;
|
||||
z-index: 1;
|
||||
font-family: inherit;
|
||||
}
|
||||
.copy-btn:hover { background: var(--border); color: var(--text); }
|
||||
.copy-btn.copied { color: var(--green); }
|
||||
|
||||
/* ── Divider ────────────────────────────────── */
|
||||
hr {
|
||||
border: none;
|
||||
border-top: 1px solid var(--border);
|
||||
margin: 2.5rem 0;
|
||||
}
|
||||
|
||||
/* ── Footer ─────────────────────────────────── */
|
||||
footer {
|
||||
text-align: center;
|
||||
color: var(--text-muted);
|
||||
font-size: 0.82rem;
|
||||
padding: 2rem 1rem;
|
||||
border-top: 1px solid var(--border);
|
||||
}
|
||||
|
||||
/* ── Responsive ─────────────────────────────── */
|
||||
@media (max-width: 600px) {
|
||||
header { padding: 2rem 1rem 1.5rem; }
|
||||
header h1 { font-size: 1.6rem; }
|
||||
main { padding: 1.5rem 1rem 3rem; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<header>
|
||||
<h1>CodeBlock CRBasic</h1>
|
||||
<p>Syntax highlighting for CRBasic code blocks in Obsidian.<br>A simple guide to install and use the plugin.</p>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
|
||||
<!-- ════════════════════════════════════════════ -->
|
||||
<!-- WHAT YOU NEED -->
|
||||
<!-- ════════════════════════════════════════════ -->
|
||||
<div class="step">
|
||||
<span class="step-number">1</span>
|
||||
<h2>What You Need</h2>
|
||||
<div class="step-body">
|
||||
<p>The plugin is made up of <strong>3 small files</strong>. That's it — no installer, no app store, just 3 files:</p>
|
||||
|
||||
<div class="file-list">
|
||||
<div class="file-row">
|
||||
<span class="file-icon">📄</span>
|
||||
<span class="file-name">main.js</span>
|
||||
<span class="file-desc">The plugin code (~16 KB)</span>
|
||||
</div>
|
||||
<div class="file-row">
|
||||
<span class="file-icon">📄</span>
|
||||
<span class="file-name">manifest.json</span>
|
||||
<span class="file-desc">Tells Obsidian the plugin's name and version</span>
|
||||
</div>
|
||||
<div class="file-row">
|
||||
<span class="file-icon">📄</span>
|
||||
<span class="file-name">styles.css</span>
|
||||
<span class="file-desc">Style sheet (mostly empty — uses your theme's colors)</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p>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.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ════════════════════════════════════════════ -->
|
||||
<!-- FIND YOUR VAULT -->
|
||||
<!-- ════════════════════════════════════════════ -->
|
||||
<div class="step">
|
||||
<span class="step-number">2</span>
|
||||
<h2>Find Your Obsidian Vault Folder</h2>
|
||||
<div class="step-body">
|
||||
<p>Your <strong>vault</strong> is the folder on your computer where Obsidian stores all your notes. You chose this folder when you first set up Obsidian.</p>
|
||||
|
||||
<p>Not sure where it is? Open Obsidian, then:</p>
|
||||
<ol>
|
||||
<li>Click the <strong>Settings</strong> gear icon (bottom-left corner)</li>
|
||||
<li>Look at the very bottom of the left sidebar — it shows your vault name</li>
|
||||
<li>Click <strong>Files & Links</strong> in the sidebar</li>
|
||||
<li>The path shown at the top is your vault folder</li>
|
||||
</ol>
|
||||
|
||||
<p>Or simply: open <strong>File Explorer</strong> (Windows) or <strong>Finder</strong> (Mac) and navigate to wherever your notes live.</p>
|
||||
|
||||
<div class="callout tip">
|
||||
<div class="callout-title">Tip</div>
|
||||
<p>Inside your vault folder, there's a hidden folder called <code>.obsidian</code>. On Windows, you may need to turn on <strong>"Show hidden items"</strong> in File Explorer's <strong>View</strong> menu to see it.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ════════════════════════════════════════════ -->
|
||||
<!-- CREATE THE PLUGIN FOLDER -->
|
||||
<!-- ════════════════════════════════════════════ -->
|
||||
<div class="step">
|
||||
<span class="step-number">3</span>
|
||||
<h2>Create the Plugin Folder</h2>
|
||||
<div class="step-body">
|
||||
<p>Inside your vault, navigate to this path:</p>
|
||||
|
||||
<div class="path-box">
|
||||
<span class="path-dim">Your Vault</span> ›
|
||||
<span class="path-dim">.obsidian</span> ›
|
||||
<span class="path-dim">plugins</span> ›
|
||||
<span class="path-highlight">codeblock-crbasic</span>
|
||||
</div>
|
||||
|
||||
<p>You may need to <strong>create</strong> some of these folders if they don't exist yet:</p>
|
||||
<ul>
|
||||
<li>The <code>plugins</code> folder should already exist if you've ever used a community plugin. If it doesn't, create it.</li>
|
||||
<li>Create a new folder inside <code>plugins</code> called exactly: <code>codeblock-crbasic</code></li>
|
||||
</ul>
|
||||
|
||||
<p>For example, if your vault is at <code>C:\Users\Me\Documents\MyVault</code>, the full path would be:</p>
|
||||
|
||||
<div class="path-box">
|
||||
C:\Users\Me\Documents\MyVault\.obsidian\plugins\<span class="path-highlight">codeblock-crbasic</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ════════════════════════════════════════════ -->
|
||||
<!-- COPY THE FILES -->
|
||||
<!-- ════════════════════════════════════════════ -->
|
||||
<div class="step">
|
||||
<span class="step-number">4</span>
|
||||
<h2>Copy the 3 Files</h2>
|
||||
<div class="step-body">
|
||||
<p>Copy <code>main.js</code>, <code>manifest.json</code>, and <code>styles.css</code> into the <code>codeblock-crbasic</code> folder you just created.</p>
|
||||
|
||||
<p>When you're done, the folder should look like this:</p>
|
||||
|
||||
<div class="file-list">
|
||||
<div class="file-row">
|
||||
<span class="file-icon">📁</span>
|
||||
<span class="file-name">codeblock-crbasic/</span>
|
||||
<span class="file-desc"></span>
|
||||
</div>
|
||||
<div class="file-row" style="padding-left: 1.5rem;">
|
||||
<span class="file-icon">📄</span>
|
||||
<span class="file-name">main.js</span>
|
||||
<span class="file-desc"></span>
|
||||
</div>
|
||||
<div class="file-row" style="padding-left: 1.5rem;">
|
||||
<span class="file-icon">📄</span>
|
||||
<span class="file-name">manifest.json</span>
|
||||
<span class="file-desc"></span>
|
||||
</div>
|
||||
<div class="file-row" style="padding-left: 1.5rem;">
|
||||
<span class="file-icon">📄</span>
|
||||
<span class="file-name">styles.css</span>
|
||||
<span class="file-desc"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p>That's all the files. Nothing else to download or install.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ════════════════════════════════════════════ -->
|
||||
<!-- ENABLE THE PLUGIN -->
|
||||
<!-- ════════════════════════════════════════════ -->
|
||||
<div class="step">
|
||||
<span class="step-number">5</span>
|
||||
<h2>Enable the Plugin in Obsidian</h2>
|
||||
<div class="step-body">
|
||||
<ol>
|
||||
<li><strong>Restart Obsidian</strong> (close it completely and re-open it)</li>
|
||||
<li>Open <strong>Settings</strong> (gear icon, bottom-left)</li>
|
||||
<li>Click <strong>Community plugins</strong> in the left sidebar</li>
|
||||
<li>If you see a message about <strong>Restricted Mode</strong>, click <strong>"Turn on community plugins"</strong></li>
|
||||
<li>You should see <strong>"CodeBlock CRBasic"</strong> in the list of installed plugins</li>
|
||||
<li>Toggle the <strong>switch</strong> next to it so it turns <strong>on</strong> (blue/purple)</li>
|
||||
</ol>
|
||||
|
||||
<div class="callout warn">
|
||||
<div class="callout-title">Don't see it?</div>
|
||||
<p>Double-check that all 3 files are in the correct folder, and that the folder is named exactly <code>codeblock-crbasic</code> (no typos, no extra spaces). Then restart Obsidian again.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<!-- ════════════════════════════════════════════ -->
|
||||
<!-- HOW TO USE IT -->
|
||||
<!-- ════════════════════════════════════════════ -->
|
||||
<div class="step">
|
||||
<span class="step-number">6</span>
|
||||
<h2>How to Use It</h2>
|
||||
<div class="step-body">
|
||||
<p>In any Obsidian note, create a <strong>code block</strong> by typing three backticks followed by <code>crbasic</code>, then your code, then three backticks to close:</p>
|
||||
|
||||
<div class="copy-wrap">
|
||||
<button class="copy-btn" onclick="copyExample(this)">Copy</button>
|
||||
<pre><code id="example-src">```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
|
||||
```</code></pre>
|
||||
</div>
|
||||
|
||||
<p>Once you type this into a note, you'll see the code light up with colors:</p>
|
||||
|
||||
<div class="copy-wrap">
|
||||
<pre><code><span class="cmt">'CR1000X Example Program</span>
|
||||
<span class="kw">Public</span> LoggerTemp, BattV
|
||||
<span class="kw">Public</span> Dest(<span class="num">9</span>)
|
||||
|
||||
<span class="fn">DataTable</span> (Test,<span class="num">1</span>,<span class="num">-1</span>)
|
||||
<span class="fn">DataInterval</span> (<span class="num">0</span>,<span class="num">15</span>,Sec,<span class="num">10</span>)
|
||||
<span class="fn">Minimum</span> (<span class="num">1</span>,BattV,FP2,<span class="const">False</span>,<span class="const">False</span>)
|
||||
<span class="fn">Sample</span> (<span class="num">1</span>,LoggerTemp,FP2)
|
||||
<span class="kw">EndTable</span>
|
||||
|
||||
<span class="kw">BeginProg</span>
|
||||
<span class="kw">Scan</span> (<span class="num">1</span>,Sec,<span class="num">0</span>,<span class="num">0</span>)
|
||||
<span class="fn">PanelTemp</span> (LoggerTemp,<span class="num">15000</span>)
|
||||
<span class="fn">Battery</span> (BattV)
|
||||
<span class="kw">CallTable</span> Test
|
||||
<span class="kw">NextScan</span>
|
||||
<span class="kw">EndProg</span></code><span class="label">Preview</span></pre>
|
||||
</div>
|
||||
|
||||
<p><strong>What gets colored:</strong></p>
|
||||
<ul>
|
||||
<li><span style="color:#c678dd;">■</span> <strong>Keywords</strong> — <code>BeginProg</code>, <code>EndProg</code>, <code>Scan</code>, <code>NextScan</code>, <code>Public</code>, <code>Dim</code>, <code>If</code>, <code>Else</code>, etc.</li>
|
||||
<li><span style="color:#61afef;">■</span> <strong>Built-in functions</strong> — <code>PanelTemp</code>, <code>Battery</code>, <code>DataTable</code>, <code>DataInterval</code>, <code>VoltDiff</code>, <code>Average</code>, etc.</li>
|
||||
<li><span style="color:#d19a66;">■</span> <strong>Numbers</strong> — <code>1</code>, <code>15000</code>, <code>3.14</code></li>
|
||||
<li><span style="color:#98c379;">■</span> <strong>Strings</strong> — <code>"M!"</code>, <code>"Hello"</code></li>
|
||||
<li><span style="color:#7f848e;">■</span> <strong>Comments</strong> — anything after an apostrophe: <code>'this is a comment</code></li>
|
||||
<li><span style="color:#e5c07b;">■</span> <strong>Constants</strong> — <code>True</code>, <code>False</code></li>
|
||||
</ul>
|
||||
|
||||
<div class="callout tip">
|
||||
<div class="callout-title">Both views work</div>
|
||||
<p>Syntax highlighting works in both <strong>Edit mode</strong> (where you type) and <strong>Reading mode</strong> (the clean preview). Toggle between them with the book/pencil icon in the top-right corner of a note.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<!-- ════════════════════════════════════════════ -->
|
||||
<!-- QUICK TEST -->
|
||||
<!-- ════════════════════════════════════════════ -->
|
||||
<div class="step">
|
||||
<span class="step-number">7</span>
|
||||
<h2>Quick Test</h2>
|
||||
<div class="step-body">
|
||||
<p>Want to make sure everything is working? Follow this 30-second test:</p>
|
||||
|
||||
<ol>
|
||||
<li>Open Obsidian</li>
|
||||
<li>Create a new note (click the <strong>"New note"</strong> icon or press <strong>Ctrl+N</strong>)</li>
|
||||
<li>Click the <strong>Copy</strong> button on the example code above</li>
|
||||
<li>Paste it into the note (<strong>Ctrl+V</strong>)</li>
|
||||
<li>You should immediately see colored keywords in the code block</li>
|
||||
<li>Switch to <strong>Reading mode</strong> (click the book icon top-right) — the colors should appear there too</li>
|
||||
</ol>
|
||||
|
||||
<p>If you see colors — you're all set!</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<!-- ════════════════════════════════════════════ -->
|
||||
<!-- TROUBLESHOOTING -->
|
||||
<!-- ════════════════════════════════════════════ -->
|
||||
<div class="step">
|
||||
<span class="step-number">?</span>
|
||||
<h2>Troubleshooting</h2>
|
||||
<div class="step-body">
|
||||
|
||||
<p><strong>No colors showing up at all?</strong></p>
|
||||
<ul>
|
||||
<li>Make sure the code block starts with exactly <code>```crbasic</code> (three backticks, then the word crbasic, no space between them)</li>
|
||||
<li>Make sure the plugin is <strong>enabled</strong> in Settings → Community plugins</li>
|
||||
<li>Try restarting Obsidian completely (close and re-open)</li>
|
||||
</ul>
|
||||
|
||||
<p><strong>Plugin doesn't appear in the plugin list?</strong></p>
|
||||
<ul>
|
||||
<li>The folder must be named exactly <code>codeblock-crbasic</code></li>
|
||||
<li>All 3 files (<code>main.js</code>, <code>manifest.json</code>, <code>styles.css</code>) must be directly inside that folder — not in a sub-folder</li>
|
||||
<li>Make sure Community Plugins / Restricted Mode is turned off</li>
|
||||
</ul>
|
||||
|
||||
<p><strong>Can't find the <code>.obsidian</code> folder?</strong></p>
|
||||
<ul>
|
||||
<li><strong>Windows:</strong> In File Explorer, click <strong>View</strong> in the top menu, then check <strong>"Hidden items"</strong></li>
|
||||
<li><strong>Mac:</strong> In Finder, press <strong>Cmd + Shift + .</strong> (period) to show hidden files</li>
|
||||
<li><strong>Linux:</strong> Press <strong>Ctrl + H</strong> in your file manager</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
CodeBlock CRBasic v1.0.0 — Syntax highlighting for Campbell Scientific CRBasic
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
function copyExample(btn) {
|
||||
var text = document.getElementById('example-src').textContent;
|
||||
if (navigator.clipboard) {
|
||||
navigator.clipboard.writeText(text).then(function() {
|
||||
btn.textContent = 'Copied!';
|
||||
btn.classList.add('copied');
|
||||
setTimeout(function() {
|
||||
btn.textContent = 'Copy';
|
||||
btn.classList.remove('copied');
|
||||
}, 2000);
|
||||
});
|
||||
} else {
|
||||
// Fallback for older browsers / file:// protocol
|
||||
var ta = document.createElement('textarea');
|
||||
ta.value = text;
|
||||
ta.style.position = 'fixed';
|
||||
ta.style.left = '-9999px';
|
||||
document.body.appendChild(ta);
|
||||
ta.select();
|
||||
try {
|
||||
document.execCommand('copy');
|
||||
btn.textContent = 'Copied!';
|
||||
btn.classList.add('copied');
|
||||
setTimeout(function() {
|
||||
btn.textContent = 'Copy';
|
||||
btn.classList.remove('copied');
|
||||
}, 2000);
|
||||
} catch(e) {}
|
||||
document.body.removeChild(ta);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Reference in a new issue