monaco-line-locks 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,191 @@
1
+ # monaco-line-locks
2
+
3
+ Lock specific lines in Monaco Editor to prevent editing. A lightweight alternative to constrained-editor-plugin with a simpler mental model.
4
+
5
+ ## Features
6
+
7
+ - **Lock by line number** — blacklist approach (vs editable ranges whitelist)
8
+ - **Prevents edits** via keyboard blocking (not undo-after-the-fact)
9
+ - **Auto-adjusts** locked line positions when document changes
10
+ - **Visual feedback** — CSS decorations for locked lines + gutter icons
11
+ - **React hook** included
12
+ - **No model monkey-patching**
13
+
14
+ ## Install
15
+
16
+ ```bash
17
+ # pnpm (recommended)
18
+ pnpm add monaco-line-locks
19
+
20
+ # npm
21
+ npm install monaco-line-locks
22
+ ```
23
+
24
+ ## Basic Usage
25
+
26
+ ### Vanilla JS
27
+
28
+ ```typescript
29
+ import { LineLockManager } from 'monaco-line-locks'
30
+ import * as monaco from 'monaco-editor'
31
+
32
+ const editor = monaco.editor.create(document.getElementById('editor'), {
33
+ value: '// Line 1\n// Line 2\n// Line 3',
34
+ language: 'javascript',
35
+ })
36
+
37
+ const locks = new LineLockManager(monaco, editor, {
38
+ lockedLines: [2], // Lock line 2
39
+ })
40
+
41
+ // Later: update locks
42
+ locks.setLockedLines([2, 3])
43
+
44
+ // Get current locks
45
+ console.log(locks.getLockedLines()) // [2, 3]
46
+
47
+ // Cleanup
48
+ locks.dispose()
49
+ ```
50
+
51
+ ### React
52
+
53
+ ```tsx
54
+ import { useLineLocks } from 'monaco-line-locks/react'
55
+ import { useState } from 'react'
56
+ import Editor from '@monaco-editor/react'
57
+
58
+ function MyEditor() {
59
+ const [lockedLines, setLockedLines] = useState([3, 5])
60
+
61
+ const handleMount = (editor, monaco) => {
62
+ useLineLocks(editor, monaco, lockedLines, {
63
+ onChange: setLockedLines,
64
+ })
65
+ }
66
+
67
+ return <Editor onMount={handleMount} />
68
+ }
69
+ ```
70
+
71
+ ## CSS
72
+
73
+ Add these classes to your stylesheet:
74
+
75
+ ```css
76
+ /* Yellow background on locked lines */
77
+ .locked-line {
78
+ background: rgba(255, 200, 0, 0.06);
79
+ }
80
+
81
+ /* Lock icon in gutter (requires Codicon font) */
82
+ .locked-deco::before {
83
+ content: '\ea75';
84
+ font-family: 'codicon';
85
+ font-size: 12px;
86
+ color: rgba(255, 200, 0, 0.7);
87
+ }
88
+ ```
89
+
90
+ ## API
91
+
92
+ ### `LineLockManager`
93
+
94
+ | Method | Description |
95
+ | --------------------------------------- | ---------------------------------------------------------- |
96
+ | `constructor(monaco, editor, options?)` | Create manager |
97
+ | `setLockedLines(lines)` | Replace all locked lines |
98
+ | `getLockedLines()` | Get sorted copy of current locks |
99
+ | `hasLockInRange(start, end)` | Check if any line in [start, end] is locked — **O(log L)** |
100
+ | `dispose()` | Cleanup listeners and decorations |
101
+
102
+ ### Options
103
+
104
+ | Option | Type | Default | Description |
105
+ | --------------------------- | --------------------------- | ----------------------- | ----------------------------------------- |
106
+ | `lockedLines` | `number[]` | `[]` | Initial locked line numbers |
107
+ | `decorationClassName` | `string` | `'locked-line'` | CSS class for line background |
108
+ | `decorationGutterClassName` | `string` | `'locked-deco'` | CSS class for gutter icon |
109
+ | `hoverMessage` | `string \| IMarkdownString` | `'This line is locked'` | Message when trying to edit a locked line |
110
+ | `blockKeyboard` | `boolean` | `true` | Block keyboard input on locked lines |
111
+ | `blockMouse` | `boolean` | `false` | Block mouse input on locked lines |
112
+ | `onChange` | `(lines: number[]) => void` | — | Called when locks change due to edits |
113
+
114
+ ## How it works
115
+
116
+ 1. **Keyboard blocking** — `onKeyDown` listener prevents typing/deleting on locked lines via binary search (O(log L))
117
+ 2. **Line tracking** — `onDidChangeModelContent` adjusts lock positions when lines are inserted/deleted
118
+ 3. **Decorations** — Monaco `deltaDecorations` for visual feedback
119
+
120
+ ## Differences from constrained-editor-plugin
121
+
122
+ | | constrained-editor-plugin | monaco-line-locks |
123
+ | ---------- | --------------------------- | ------------------------- |
124
+ | Approach | Editable ranges (whitelist) | Locked lines (blacklist) |
125
+ | Validation | Undo after invalid edit | Prevent before it happens |
126
+ | Model | Monkey-patched | Clean |
127
+ | Columns | Full range support | Line-level only |
128
+ | Size | ~15KB | ~2KB |
129
+
130
+ ## Development
131
+
132
+ ```bash
133
+ # Clone the repo
134
+ git clone https://github.com/yourusername/monaco-line-locks.git
135
+ cd monaco-line-locks
136
+
137
+ # Install dependencies
138
+ pnpm install
139
+
140
+ # Run tests
141
+ pnpm test
142
+
143
+ # Type check
144
+ pnpm exec tsc --noEmit
145
+
146
+ # Check for unused code
147
+ pnpm knip
148
+
149
+ # Build for publish
150
+ pnpm run build
151
+ ```
152
+
153
+ ## Examples
154
+
155
+ ### Basic (Vanilla JS)
156
+
157
+ ```bash
158
+ # Build the library first
159
+ pnpm run build
160
+
161
+ # Serve the example (browsers block file:// URLs for ES modules)
162
+ cd examples
163
+ pnpm install
164
+ pnpm run serve:basic
165
+ # Open http://localhost:3000
166
+ ```
167
+
168
+ ### React
169
+
170
+ ```bash
171
+ cd examples
172
+ pnpm install
173
+ pnpm run serve:react
174
+ # Open http://localhost:5173
175
+ ```
176
+
177
+ ## Contributing
178
+
179
+ Issues and PRs welcome. Focus areas:
180
+
181
+ - Mouse/paste blocking
182
+ - Multi-cursor support
183
+ - Performance benchmarks
184
+
185
+ ## Changelog
186
+
187
+ See [CHANGELOG.md](./CHANGELOG.md)
188
+
189
+ ## License
190
+
191
+ MIT
@@ -0,0 +1,61 @@
1
+ import type * as monacoEditor from 'monaco-editor';
2
+ export interface LineLockOptions {
3
+ /** Initial locked line numbers (1-indexed) */
4
+ lockedLines?: number[];
5
+ /** CSS class for the line background decoration */
6
+ decorationClassName?: string;
7
+ /** CSS class for the gutter decoration (lock icon) */
8
+ decorationGutterClassName?: string;
9
+ /** Message shown when trying to edit a locked line (default: "This line is locked") */
10
+ hoverMessage?: string | monacoEditor.IMarkdownString;
11
+ /** Whether to block keyboard input on locked lines (default: true) */
12
+ blockKeyboard?: boolean;
13
+ /** Whether to block mouse input on locked lines (default: false) */
14
+ blockMouse?: boolean;
15
+ /** Called whenever locked lines change due to document edits */
16
+ onChange?: (lines: number[]) => void;
17
+ }
18
+ /**
19
+ * Variables:
20
+ * L = total number of lines in the document
21
+ * S = number of lines in the current selection (S ≤ L)
22
+ * D = number of deleted lines in a change (D ≤ L)
23
+ * C = number of changes in a batch
24
+ */
25
+ export declare class LineLockManager {
26
+ private monaco;
27
+ private editor;
28
+ private options;
29
+ private lockedLinesSet;
30
+ private lockedLinesSorted;
31
+ private decorations;
32
+ private disposables;
33
+ constructor(monaco: typeof monacoEditor, editor: monacoEditor.editor.IStandaloneCodeEditor, options?: LineLockOptions);
34
+ /** Filter lines outside the current model's bounds */
35
+ private clampLines;
36
+ /** O(L log L) — sorts at most L locked lines */
37
+ private sortLines;
38
+ /** O(L) — registers listeners and applies decorations (at most L locked lines) */
39
+ private init;
40
+ /** O(log L) per keystroke — binary search over at most L locked lines */
41
+ private setupKeyboardBlocking;
42
+ /** O(1) — registers the content change listener */
43
+ private setupLineTracking;
44
+ /** O(C·(D + L) + L log L) = O(C·L + L log L) — worst case */
45
+ private adjustLocks;
46
+ /** O(L) — applies Monaco deltaDecorations for at most L locked lines */
47
+ private updateDecorations;
48
+ /** O(L log L) — replace all locked lines (at most L) and update decorations */
49
+ setLockedLines(lines: number[]): void;
50
+ /** O(log L) — check if any line in [start, end] is locked (binary search over ≤ L lines) */
51
+ hasLockInRange(start: number, end: number): boolean;
52
+ /** O(log L) — binary search over at most L locked lines */
53
+ private lowerBound;
54
+ /** Show message overlay at cursor position using Monaco's MessageController */
55
+ private showLockMessage;
56
+ /** O(L) — copy at most L locked lines */
57
+ getLockedLines(): number[];
58
+ /** O(L) — clean up listeners + decorations (at most L locked lines) */
59
+ dispose(): void;
60
+ }
61
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,YAAY,MAAM,eAAe,CAAA;AAElD,MAAM,WAAW,eAAe;IAC9B,8CAA8C;IAC9C,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;IACtB,mDAAmD;IACnD,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,sDAAsD;IACtD,yBAAyB,CAAC,EAAE,MAAM,CAAA;IAClC,uFAAuF;IACvF,YAAY,CAAC,EAAE,MAAM,GAAG,YAAY,CAAC,eAAe,CAAA;IACpD,sEAAsE;IACtE,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,oEAAoE;IACpE,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,gEAAgE;IAChE,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,CAAA;CACrC;AAED;;;;;;GAMG;AAEH,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAAqB;IACnC,OAAO,CAAC,MAAM,CAA2C;IACzD,OAAO,CAAC,OAAO,CAA2B;IAC1C,OAAO,CAAC,cAAc,CAAa;IACnC,OAAO,CAAC,iBAAiB,CAAU;IACnC,OAAO,CAAC,WAAW,CAAU;IAC7B,OAAO,CAAC,WAAW,CAAgB;gBAEjC,MAAM,EAAE,OAAO,YAAY,EAC3B,MAAM,EAAE,YAAY,CAAC,MAAM,CAAC,qBAAqB,EACjD,OAAO,GAAE,eAAoB;IAuB/B,sDAAsD;IACtD,OAAO,CAAC,UAAU;IAOlB,gDAAgD;IAChD,OAAO,CAAC,SAAS;IAIjB,kFAAkF;IAClF,OAAO,CAAC,IAAI;IAQZ,yEAAyE;IACzE,OAAO,CAAC,qBAAqB;IA0D7B,mDAAmD;IACnD,OAAO,CAAC,iBAAiB;IAQzB,6DAA6D;IAC7D,OAAO,CAAC,WAAW;IA+CnB,wEAAwE;IACxE,OAAO,CAAC,iBAAiB;IAoBzB,+EAA+E;IAC/E,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE;IAO9B,4FAA4F;IAC5F,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO;IAKnD,2DAA2D;IAC3D,OAAO,CAAC,UAAU;IAWlB,+EAA+E;IAC/E,OAAO,CAAC,eAAe;IAgBvB,yCAAyC;IACzC,cAAc,IAAI,MAAM,EAAE;IAI1B,uEAAuE;IACvE,OAAO;CAUR"}
package/dist/index.js ADDED
@@ -0,0 +1,241 @@
1
+ /**
2
+ * Variables:
3
+ * L = total number of lines in the document
4
+ * S = number of lines in the current selection (S ≤ L)
5
+ * D = number of deleted lines in a change (D ≤ L)
6
+ * C = number of changes in a batch
7
+ */
8
+ export class LineLockManager {
9
+ monaco;
10
+ editor;
11
+ options;
12
+ lockedLinesSet;
13
+ lockedLinesSorted;
14
+ decorations;
15
+ disposables;
16
+ constructor(monaco, editor, options = {}) {
17
+ this.monaco = monaco;
18
+ this.editor = editor;
19
+ this.options = {
20
+ lockedLines: [],
21
+ decorationClassName: 'locked-line',
22
+ decorationGutterClassName: 'locked-deco',
23
+ hoverMessage: 'This line is locked',
24
+ blockKeyboard: true,
25
+ blockMouse: false,
26
+ onChange: () => { },
27
+ ...options,
28
+ };
29
+ const clamped = this.clampLines(this.options.lockedLines);
30
+ this.lockedLinesSet = new Set(clamped);
31
+ this.lockedLinesSorted = this.sortLines(clamped);
32
+ this.decorations = [];
33
+ this.disposables = [];
34
+ this.init();
35
+ }
36
+ /** Filter lines outside the current model's bounds */
37
+ clampLines(lines) {
38
+ const model = this.editor.getModel();
39
+ if (!model)
40
+ return lines;
41
+ const max = model.getLineCount();
42
+ return lines.filter((l) => l >= 1 && l <= max);
43
+ }
44
+ /** O(L log L) — sorts at most L locked lines */
45
+ sortLines(lines) {
46
+ return [...lines].sort((a, b) => a - b);
47
+ }
48
+ /** O(L) — registers listeners and applies decorations (at most L locked lines) */
49
+ init() {
50
+ if (this.options.blockKeyboard) {
51
+ this.setupKeyboardBlocking();
52
+ }
53
+ this.setupLineTracking();
54
+ this.updateDecorations();
55
+ }
56
+ /** O(log L) per keystroke — binary search over at most L locked lines */
57
+ setupKeyboardBlocking() {
58
+ const NAV = [
59
+ this.monaco.KeyCode.LeftArrow,
60
+ this.monaco.KeyCode.RightArrow,
61
+ this.monaco.KeyCode.UpArrow,
62
+ this.monaco.KeyCode.DownArrow,
63
+ this.monaco.KeyCode.Home,
64
+ this.monaco.KeyCode.End,
65
+ this.monaco.KeyCode.PageUp,
66
+ this.monaco.KeyCode.PageDown,
67
+ this.monaco.KeyCode.Escape,
68
+ ];
69
+ const SAFE = [
70
+ this.monaco.KeyCode.KeyA,
71
+ this.monaco.KeyCode.KeyC,
72
+ this.monaco.KeyCode.KeyF,
73
+ this.monaco.KeyCode.KeyH,
74
+ this.monaco.KeyCode.KeyZ,
75
+ ];
76
+ const isNav = (k) => NAV.includes(k);
77
+ const isSafe = (k, ctrl) => ctrl && SAFE.includes(k);
78
+ const disposable = this.editor.onKeyDown((e) => {
79
+ const locks = this.lockedLinesSet;
80
+ if (!locks.size)
81
+ return;
82
+ const sel = this.editor.getSelection();
83
+ if (!sel)
84
+ return;
85
+ let start = sel.startLineNumber;
86
+ let end = sel.endLineNumber;
87
+ if (e.keyCode === this.monaco.KeyCode.Backspace) {
88
+ const pos = this.editor.getPosition();
89
+ if (pos?.column === 1)
90
+ start = Math.max(1, start - 1);
91
+ }
92
+ if (e.keyCode === this.monaco.KeyCode.Delete) {
93
+ const pos = this.editor.getPosition();
94
+ const model = this.editor.getModel();
95
+ if (pos && model && pos.column >= model.getLineMaxColumn(pos.lineNumber)) {
96
+ end = Math.min(model.getLineCount(), end + 1);
97
+ }
98
+ }
99
+ if (this.hasLockInRange(start, end)) {
100
+ if (isNav(e.keyCode) || isSafe(e.keyCode, e.ctrlKey || e.metaKey))
101
+ return;
102
+ this.showLockMessage();
103
+ e.preventDefault();
104
+ e.browserEvent.preventDefault();
105
+ e.browserEvent.stopPropagation();
106
+ return;
107
+ }
108
+ });
109
+ this.disposables.push(() => disposable.dispose());
110
+ }
111
+ /** O(1) — registers the content change listener */
112
+ setupLineTracking() {
113
+ const disposable = this.editor.onDidChangeModelContent((e) => {
114
+ if (e.isFlush)
115
+ return;
116
+ this.adjustLocks(e);
117
+ });
118
+ this.disposables.push(() => disposable.dispose());
119
+ }
120
+ /** O(C·(D + L) + L log L) = O(C·L + L log L) — worst case */
121
+ adjustLocks(e) {
122
+ const locks = this.lockedLinesSet;
123
+ if (!locks.size)
124
+ return;
125
+ let modified = false;
126
+ for (const change of [...e.changes].sort((a, b) => b.range.startLineNumber - a.range.startLineNumber)) {
127
+ const { startLineNumber: start, endLineNumber: end } = change.range;
128
+ const deleted = end - start;
129
+ const inserted = change.text.split('\n').length - 1;
130
+ const delta = inserted - deleted;
131
+ if (!delta && !deleted)
132
+ continue;
133
+ // Remove locks for deleted lines — O(deleted) with Set
134
+ for (let i = 0; i < deleted; i++) {
135
+ if (locks.delete(start + i)) {
136
+ modified = true;
137
+ }
138
+ }
139
+ // Shift locks after the change range — O(locks)
140
+ if (delta) {
141
+ const toShift = [];
142
+ for (const line of locks) {
143
+ if (line >= start + deleted) {
144
+ toShift.push(line);
145
+ }
146
+ }
147
+ for (const line of toShift) {
148
+ locks.delete(line);
149
+ }
150
+ for (const line of toShift) {
151
+ locks.add(line + delta);
152
+ }
153
+ if (toShift.length)
154
+ modified = true;
155
+ }
156
+ }
157
+ if (modified) {
158
+ this.lockedLinesSorted = this.sortLines([...locks]);
159
+ this.updateDecorations();
160
+ this.options.onChange([...this.lockedLinesSorted]);
161
+ }
162
+ }
163
+ /** O(L) — applies Monaco deltaDecorations for at most L locked lines */
164
+ updateDecorations() {
165
+ const model = this.editor.getModel();
166
+ if (!model)
167
+ return;
168
+ this.decorations = model.deltaDecorations(this.decorations, []);
169
+ if (this.lockedLinesSorted.length) {
170
+ const decos = this.lockedLinesSorted.map((line) => ({
171
+ range: new this.monaco.Range(line, 1, line, 1),
172
+ options: {
173
+ isWholeLine: true,
174
+ className: this.options.decorationClassName,
175
+ linesDecorationsClassName: this.options.decorationGutterClassName,
176
+ stickiness: this.monaco.editor.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
177
+ },
178
+ }));
179
+ this.decorations = model.deltaDecorations([], decos);
180
+ }
181
+ }
182
+ /** O(L log L) — replace all locked lines (at most L) and update decorations */
183
+ setLockedLines(lines) {
184
+ const clamped = this.clampLines(lines);
185
+ this.lockedLinesSet = new Set(clamped);
186
+ this.lockedLinesSorted = this.sortLines(clamped);
187
+ this.updateDecorations();
188
+ }
189
+ /** O(log L) — check if any line in [start, end] is locked (binary search over ≤ L lines) */
190
+ hasLockInRange(start, end) {
191
+ const idx = this.lowerBound(this.lockedLinesSorted, start);
192
+ return idx < this.lockedLinesSorted.length && this.lockedLinesSorted[idx] <= end;
193
+ }
194
+ /** O(log L) — binary search over at most L locked lines */
195
+ lowerBound(arr, target) {
196
+ let lo = 0;
197
+ let hi = arr.length;
198
+ while (lo < hi) {
199
+ const mid = (lo + hi) >> 1;
200
+ if (arr[mid] < target)
201
+ lo = mid + 1;
202
+ else
203
+ hi = mid;
204
+ }
205
+ return lo;
206
+ }
207
+ /** Show message overlay at cursor position using Monaco's MessageController */
208
+ showLockMessage() {
209
+ try {
210
+ const ctrl = this.editor.getContribution('editor.contrib.messageController');
211
+ if (ctrl) {
212
+ const msg = this.options.hoverMessage
213
+ ? typeof this.options.hoverMessage === 'string'
214
+ ? this.options.hoverMessage
215
+ : this.options.hoverMessage.value
216
+ : undefined;
217
+ if (msg)
218
+ ctrl.showMessage(msg, this.editor.getPosition());
219
+ }
220
+ }
221
+ catch {
222
+ // MessageController may not be available in some Monaco builds
223
+ }
224
+ }
225
+ /** O(L) — copy at most L locked lines */
226
+ getLockedLines() {
227
+ return [...this.lockedLinesSorted];
228
+ }
229
+ /** O(L) — clean up listeners + decorations (at most L locked lines) */
230
+ dispose() {
231
+ this.disposables.forEach((d) => d());
232
+ this.disposables = [];
233
+ const model = this.editor.getModel();
234
+ if (model) {
235
+ this.decorations = model.deltaDecorations(this.decorations, []);
236
+ }
237
+ this.lockedLinesSet.clear();
238
+ this.lockedLinesSorted = [];
239
+ }
240
+ }
241
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAmBA;;;;;;GAMG;AAEH,MAAM,OAAO,eAAe;IAClB,MAAM,CAAqB;IAC3B,MAAM,CAA2C;IACjD,OAAO,CAA2B;IAClC,cAAc,CAAa;IAC3B,iBAAiB,CAAU;IAC3B,WAAW,CAAU;IACrB,WAAW,CAAgB;IACnC,YACE,MAA2B,EAC3B,MAAiD,EACjD,UAA2B,EAAE;QAE7B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;QACpB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;QACpB,IAAI,CAAC,OAAO,GAAG;YACb,WAAW,EAAE,EAAE;YACf,mBAAmB,EAAE,aAAa;YAClC,yBAAyB,EAAE,aAAa;YACxC,YAAY,EAAE,qBAAqB;YACnC,aAAa,EAAE,IAAI;YACnB,UAAU,EAAE,KAAK;YACjB,QAAQ,EAAE,GAAG,EAAE,GAAE,CAAC;YAClB,GAAG,OAAO;SACX,CAAA;QACD,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAA;QACzD,IAAI,CAAC,cAAc,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAA;QACtC,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAA;QAChD,IAAI,CAAC,WAAW,GAAG,EAAE,CAAA;QACrB,IAAI,CAAC,WAAW,GAAG,EAAE,CAAA;QAErB,IAAI,CAAC,IAAI,EAAE,CAAA;IACb,CAAC;IAED,sDAAsD;IAC9C,UAAU,CAAC,KAAe;QAChC,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAA;QACpC,IAAI,CAAC,KAAK;YAAE,OAAO,KAAK,CAAA;QACxB,MAAM,GAAG,GAAG,KAAK,CAAC,YAAY,EAAE,CAAA;QAChC,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAA;IAChD,CAAC;IAED,gDAAgD;IACxC,SAAS,CAAC,KAAe;QAC/B,OAAO,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAA;IACzC,CAAC;IAED,kFAAkF;IAC1E,IAAI;QACV,IAAI,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC;YAC/B,IAAI,CAAC,qBAAqB,EAAE,CAAA;QAC9B,CAAC;QACD,IAAI,CAAC,iBAAiB,EAAE,CAAA;QACxB,IAAI,CAAC,iBAAiB,EAAE,CAAA;IAC1B,CAAC;IAED,yEAAyE;IACjE,qBAAqB;QAC3B,MAAM,GAAG,GAAG;YACV,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS;YAC7B,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,UAAU;YAC9B,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO;YAC3B,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS;YAC7B,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI;YACxB,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG;YACvB,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM;YAC1B,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ;YAC5B,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM;SAC3B,CAAA;QACD,MAAM,IAAI,GAAG;YACX,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI;YACxB,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI;YACxB,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI;YACxB,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI;YACxB,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI;SACzB,CAAA;QAED,MAAM,KAAK,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAA;QAC5C,MAAM,MAAM,GAAG,CAAC,CAAS,EAAE,IAAa,EAAE,EAAE,CAAC,IAAI,IAAI,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAA;QAErE,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE;YAC7C,MAAM,KAAK,GAAG,IAAI,CAAC,cAAc,CAAA;YACjC,IAAI,CAAC,KAAK,CAAC,IAAI;gBAAE,OAAM;YAEvB,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,CAAA;YACtC,IAAI,CAAC,GAAG;gBAAE,OAAM;YAEhB,IAAI,KAAK,GAAG,GAAG,CAAC,eAAe,CAAA;YAC/B,IAAI,GAAG,GAAG,GAAG,CAAC,aAAa,CAAA;YAE3B,IAAI,CAAC,CAAC,OAAO,KAAK,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;gBAChD,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAA;gBACrC,IAAI,GAAG,EAAE,MAAM,KAAK,CAAC;oBAAE,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,CAAA;YACvD,CAAC;YACD,IAAI,CAAC,CAAC,OAAO,KAAK,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;gBAC7C,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAA;gBACrC,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAA;gBACpC,IAAI,GAAG,IAAI,KAAK,IAAI,GAAG,CAAC,MAAM,IAAI,KAAK,CAAC,gBAAgB,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;oBACzE,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,YAAY,EAAE,EAAE,GAAG,GAAG,CAAC,CAAC,CAAA;gBAC/C,CAAC;YACH,CAAC;YAED,IAAI,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,CAAC;gBACpC,IAAI,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,OAAO,CAAC;oBAAE,OAAM;gBACzE,IAAI,CAAC,eAAe,EAAE,CAAA;gBACtB,CAAC,CAAC,cAAc,EAAE,CAAA;gBAClB,CAAC,CAAC,YAAY,CAAC,cAAc,EAAE,CAAA;gBAC/B,CAAC,CAAC,YAAY,CAAC,eAAe,EAAE,CAAA;gBAChC,OAAM;YACR,CAAC;QACH,CAAC,CAAC,CAAA;QAEF,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC,CAAA;IACnD,CAAC;IAED,mDAAmD;IAC3C,iBAAiB;QACvB,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC,uBAAuB,CAAC,CAAC,CAAC,EAAE,EAAE;YAC3D,IAAI,CAAC,CAAC,OAAO;gBAAE,OAAM;YACrB,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAA;QACrB,CAAC,CAAC,CAAA;QACF,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC,CAAA;IACnD,CAAC;IAED,6DAA6D;IACrD,WAAW,CAAC,CAAgD;QAClE,MAAM,KAAK,GAAG,IAAI,CAAC,cAAc,CAAA;QACjC,IAAI,CAAC,KAAK,CAAC,IAAI;YAAE,OAAM;QAEvB,IAAI,QAAQ,GAAG,KAAK,CAAA;QAEpB,KAAK,MAAM,MAAM,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CACtC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,eAAe,CAC5D,EAAE,CAAC;YACF,MAAM,EAAE,eAAe,EAAE,KAAK,EAAE,aAAa,EAAE,GAAG,EAAE,GAAG,MAAM,CAAC,KAAK,CAAA;YACnE,MAAM,OAAO,GAAG,GAAG,GAAG,KAAK,CAAA;YAC3B,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAA;YACnD,MAAM,KAAK,GAAG,QAAQ,GAAG,OAAO,CAAA;YAChC,IAAI,CAAC,KAAK,IAAI,CAAC,OAAO;gBAAE,SAAQ;YAEhC,uDAAuD;YACvD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,EAAE,CAAC,EAAE,EAAE,CAAC;gBACjC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC,EAAE,CAAC;oBAC5B,QAAQ,GAAG,IAAI,CAAA;gBACjB,CAAC;YACH,CAAC;YAED,gDAAgD;YAChD,IAAI,KAAK,EAAE,CAAC;gBACV,MAAM,OAAO,GAAa,EAAE,CAAA;gBAC5B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;oBACzB,IAAI,IAAI,IAAI,KAAK,GAAG,OAAO,EAAE,CAAC;wBAC5B,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;oBACpB,CAAC;gBACH,CAAC;gBACD,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;oBAC3B,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;gBACpB,CAAC;gBACD,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;oBAC3B,KAAK,CAAC,GAAG,CAAC,IAAI,GAAG,KAAK,CAAC,CAAA;gBACzB,CAAC;gBACD,IAAI,OAAO,CAAC,MAAM;oBAAE,QAAQ,GAAG,IAAI,CAAA;YACrC,CAAC;QACH,CAAC;QAED,IAAI,QAAQ,EAAE,CAAC;YACb,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAA;YACnD,IAAI,CAAC,iBAAiB,EAAE,CAAA;YACxB,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,GAAG,IAAI,CAAC,iBAAiB,CAAC,CAAC,CAAA;QACpD,CAAC;IACH,CAAC;IAED,wEAAwE;IAChE,iBAAiB;QACvB,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAA;QACpC,IAAI,CAAC,KAAK;YAAE,OAAM;QAElB,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC,gBAAgB,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC,CAAA;QAE/D,IAAI,IAAI,CAAC,iBAAiB,CAAC,MAAM,EAAE,CAAC;YAClC,MAAM,KAAK,GAAG,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;gBAClD,KAAK,EAAE,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;gBAC9C,OAAO,EAAE;oBACP,WAAW,EAAE,IAAI;oBACjB,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,mBAAmB;oBAC3C,yBAAyB,EAAE,IAAI,CAAC,OAAO,CAAC,yBAAyB;oBACjE,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,sBAAsB,CAAC,2BAA2B;iBAClF;aACF,CAAC,CAAC,CAAA;YACH,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC,gBAAgB,CAAC,EAAE,EAAE,KAAK,CAAC,CAAA;QACtD,CAAC;IACH,CAAC;IAED,+EAA+E;IAC/E,cAAc,CAAC,KAAe;QAC5B,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAA;QACtC,IAAI,CAAC,cAAc,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAA;QACtC,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAA;QAChD,IAAI,CAAC,iBAAiB,EAAE,CAAA;IAC1B,CAAC;IAED,4FAA4F;IAC5F,cAAc,CAAC,KAAa,EAAE,GAAW;QACvC,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,iBAAiB,EAAE,KAAK,CAAC,CAAA;QAC1D,OAAO,GAAG,GAAG,IAAI,CAAC,iBAAiB,CAAC,MAAM,IAAI,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,IAAI,GAAG,CAAA;IAClF,CAAC;IAED,2DAA2D;IACnD,UAAU,CAAC,GAAa,EAAE,MAAc;QAC9C,IAAI,EAAE,GAAG,CAAC,CAAA;QACV,IAAI,EAAE,GAAG,GAAG,CAAC,MAAM,CAAA;QACnB,OAAO,EAAE,GAAG,EAAE,EAAE,CAAC;YACf,MAAM,GAAG,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,CAAA;YAC1B,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,MAAM;gBAAE,EAAE,GAAG,GAAG,GAAG,CAAC,CAAA;;gBAC9B,EAAE,GAAG,GAAG,CAAA;QACf,CAAC;QACD,OAAO,EAAE,CAAA;IACX,CAAC;IAED,+EAA+E;IACvE,eAAe;QACrB,IAAI,CAAC;YACH,MAAM,IAAI,GAAI,IAAI,CAAC,MAAc,CAAC,eAAe,CAAC,kCAAkC,CAAC,CAAA;YACrF,IAAI,IAAI,EAAE,CAAC;gBACT,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY;oBACnC,CAAC,CAAC,OAAO,IAAI,CAAC,OAAO,CAAC,YAAY,KAAK,QAAQ;wBAC7C,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY;wBAC3B,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,KAAK;oBACnC,CAAC,CAAC,SAAS,CAAA;gBACb,IAAI,GAAG;oBAAE,IAAI,CAAC,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAA;YAC3D,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,+DAA+D;QACjE,CAAC;IACH,CAAC;IAED,yCAAyC;IACzC,cAAc;QACZ,OAAO,CAAC,GAAG,IAAI,CAAC,iBAAiB,CAAC,CAAA;IACpC,CAAC;IAED,uEAAuE;IACvE,OAAO;QACL,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAA;QACpC,IAAI,CAAC,WAAW,GAAG,EAAE,CAAA;QACrB,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAA;QACpC,IAAI,KAAK,EAAE,CAAC;YACV,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC,gBAAgB,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC,CAAA;QACjE,CAAC;QACD,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,CAAA;QAC3B,IAAI,CAAC,iBAAiB,GAAG,EAAE,CAAA;IAC7B,CAAC;CACF"}
@@ -0,0 +1,24 @@
1
+ import { LineLockOptions } from './index.js';
2
+ import type * as monacoEditor from 'monaco-editor';
3
+ /**
4
+ * React hook to manage line locks in a Monaco editor.
5
+ *
6
+ * @example
7
+ * ```tsx
8
+ * import { useLineLocks } from 'monaco-line-locks/react'
9
+ *
10
+ * function Editor() {
11
+ * const [lockedLines, setLockedLines] = useState([3, 5])
12
+ * const [editor, setEditor] = useState(null)
13
+ * const [monaco, setMonaco] = useState(null)
14
+ *
15
+ * useLineLocks(editor, monaco, lockedLines, {
16
+ * onChange: setLockedLines,
17
+ * })
18
+ *
19
+ * return <MonacoEditor onMount={(ed, mon) => { setEditor(ed); setMonaco(mon) }} />
20
+ * }
21
+ * ```
22
+ */
23
+ export declare function useLineLocks(editor: monacoEditor.editor.IStandaloneCodeEditor | null, monaco: typeof monacoEditor | null, lockedLines: number[], options?: LineLockOptions): void;
24
+ //# sourceMappingURL=react.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.ts"],"names":[],"mappings":"AACA,OAAO,EAAmB,eAAe,EAAE,MAAM,YAAY,CAAA;AAC7D,OAAO,KAAK,KAAK,YAAY,MAAM,eAAe,CAAA;AAElD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,YAAY,CAAC,MAAM,CAAC,qBAAqB,GAAG,IAAI,EACxD,MAAM,EAAE,OAAO,YAAY,GAAG,IAAI,EAClC,WAAW,EAAE,MAAM,EAAE,EACrB,OAAO,CAAC,EAAE,eAAe,QAuB1B"}
package/dist/react.js ADDED
@@ -0,0 +1,43 @@
1
+ import { useEffect, useRef } from 'react';
2
+ import { LineLockManager } from './index.js';
3
+ /**
4
+ * React hook to manage line locks in a Monaco editor.
5
+ *
6
+ * @example
7
+ * ```tsx
8
+ * import { useLineLocks } from 'monaco-line-locks/react'
9
+ *
10
+ * function Editor() {
11
+ * const [lockedLines, setLockedLines] = useState([3, 5])
12
+ * const [editor, setEditor] = useState(null)
13
+ * const [monaco, setMonaco] = useState(null)
14
+ *
15
+ * useLineLocks(editor, monaco, lockedLines, {
16
+ * onChange: setLockedLines,
17
+ * })
18
+ *
19
+ * return <MonacoEditor onMount={(ed, mon) => { setEditor(ed); setMonaco(mon) }} />
20
+ * }
21
+ * ```
22
+ */
23
+ export function useLineLocks(editor, monaco, lockedLines, options) {
24
+ const managerRef = useRef(null);
25
+ // Initialize manager when editor/monaco become available
26
+ useEffect(() => {
27
+ if (!editor || !monaco)
28
+ return;
29
+ managerRef.current = new LineLockManager(monaco, editor, {
30
+ ...options,
31
+ lockedLines,
32
+ });
33
+ return () => {
34
+ managerRef.current?.dispose();
35
+ managerRef.current = null;
36
+ };
37
+ }, [editor, monaco]); // eslint-disable-line react-hooks/exhaustive-deps
38
+ // Update locked lines when they change externally
39
+ useEffect(() => {
40
+ managerRef.current?.setLockedLines(lockedLines);
41
+ }, [lockedLines]);
42
+ }
43
+ //# sourceMappingURL=react.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react.js","sourceRoot":"","sources":["../src/react.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,OAAO,CAAA;AACzC,OAAO,EAAE,eAAe,EAAmB,MAAM,YAAY,CAAA;AAG7D;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,YAAY,CAC1B,MAAwD,EACxD,MAAkC,EAClC,WAAqB,EACrB,OAAyB;IAEzB,MAAM,UAAU,GAAG,MAAM,CAAyB,IAAI,CAAC,CAAA;IAEvD,yDAAyD;IACzD,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM;YAAE,OAAM;QAE9B,UAAU,CAAC,OAAO,GAAG,IAAI,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE;YACvD,GAAG,OAAO;YACV,WAAW;SACZ,CAAC,CAAA;QAEF,OAAO,GAAG,EAAE;YACV,UAAU,CAAC,OAAO,EAAE,OAAO,EAAE,CAAA;YAC7B,UAAU,CAAC,OAAO,GAAG,IAAI,CAAA;QAC3B,CAAC,CAAA;IACH,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA,CAAC,kDAAkD;IAEvE,kDAAkD;IAClD,SAAS,CAAC,GAAG,EAAE;QACb,UAAU,CAAC,OAAO,EAAE,cAAc,CAAC,WAAW,CAAC,CAAA;IACjD,CAAC,EAAE,CAAC,WAAW,CAAC,CAAC,CAAA;AACnB,CAAC"}
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "monaco-line-locks",
3
+ "version": "0.1.1",
4
+ "description": "Lock specific lines in Monaco Editor to prevent editing",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ },
13
+ "./react": {
14
+ "import": "./dist/react.js",
15
+ "types": "./dist/react.d.ts"
16
+ }
17
+ },
18
+ "files": [
19
+ "dist"
20
+ ],
21
+ "keywords": [
22
+ "monaco-editor",
23
+ "readonly",
24
+ "locked-lines",
25
+ "code-editor",
26
+ "restrictions"
27
+ ],
28
+ "author": "",
29
+ "license": "MIT",
30
+ "peerDependencies": {
31
+ "monaco-editor": ">=0.30.0",
32
+ "react": ">=16.8.0"
33
+ },
34
+ "devDependencies": {
35
+ "@types/react": "^19.2.15",
36
+ "knip": "^6.14.2",
37
+ "monaco-editor": "^0.55.0",
38
+ "prettier": "^3.8.3",
39
+ "react": "^19.2.6",
40
+ "typescript": "^5.0.0",
41
+ "vitest": "^4.1.7"
42
+ },
43
+ "scripts": {
44
+ "build": "tsc",
45
+ "dev": "tsc --watch",
46
+ "test": "vitest run",
47
+ "test:watch": "vitest",
48
+ "knip": "knip",
49
+ "fmt": "prettier --write .",
50
+ "example:react": "pnpm run build && cd examples/react && rm -rf node_modules && pnpm install && pnpm run dev",
51
+ "example:basic": "pnpm run build && python3 -m http.server 3000"
52
+ }
53
+ }