@octanejs/monaco-editor 0.0.2 → 0.0.4
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 +104 -17
- package/UPSTREAM.md +116 -8
- package/package.json +16 -5
- package/src/{DiffEditor.tsrx → DiffEditor/DiffEditor.tsrx} +9 -7
- package/src/DiffEditor/DiffEditor.tsrx.d.ts +6 -0
- package/src/DiffEditor/index.ts +2 -0
- package/src/DiffEditor/types.ts +31 -0
- package/src/{Editor.tsrx → Editor/Editor.tsrx} +8 -7
- package/src/{Editor.tsrx.d.ts → Editor/Editor.tsrx.d.ts} +1 -1
- package/src/Editor/index.ts +2 -0
- package/src/Editor/types.ts +36 -0
- package/src/Loading/Loading.tsrx +13 -0
- package/src/Loading/Loading.tsrx.d.ts +9 -0
- package/src/Loading/index.ts +2 -0
- package/src/Loading/styles.ts +11 -0
- package/src/MonacoContainer/MonacoContainer.tsrx +22 -0
- package/src/MonacoContainer/MonacoContainer.tsrx.d.ts +6 -0
- package/src/MonacoContainer/index.ts +2 -0
- package/src/MonacoContainer/styles.ts +15 -0
- package/src/MonacoContainer/types.ts +15 -0
- package/src/hooks/useMonaco/index.ts +1 -0
- package/src/{useMonaco.tsrx → hooks/useMonaco/useMonaco.tsrx} +4 -2
- package/src/hooks/useMonaco/useMonaco.tsrx.d.ts +4 -0
- package/src/index.ts +17 -19
- package/src/model.ts +5 -0
- package/src/types.ts +0 -61
- package/src/utils/index.ts +4 -0
- package/src/Container.tsrx +0 -43
- package/src/Container.tsrx.d.ts +0 -16
- package/src/DiffEditor.tsrx.d.ts +0 -5
- package/src/useMonaco.tsrx.d.ts +0 -3
package/README.md
CHANGED
|
@@ -1,30 +1,117 @@
|
|
|
1
|
-
#
|
|
1
|
+
# `@octanejs/monaco-editor`
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
`@monaco-editor/
|
|
5
|
-
|
|
3
|
+
Octane bindings for [Monaco Editor](https://microsoft.github.io/monaco-editor/),
|
|
4
|
+
ported from [`@monaco-editor/react`](https://github.com/suren-atoyan/monaco-react)
|
|
5
|
+
`4.7.0` (commit `eb120e66`).
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
pnpm add @octanejs/monaco-editor monaco-editor
|
|
8
|
+
pnpm add @octanejs/monaco-editor monaco-editor octane
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
```tsrx
|
|
12
|
-
import Editor, { loader } from '@octanejs/monaco-editor';
|
|
13
|
-
import * as monaco from 'monaco-editor';
|
|
14
|
-
|
|
15
|
-
loader.config({ monaco });
|
|
12
|
+
import Editor, { DiffEditor, useMonaco, loader } from '@octanejs/monaco-editor';
|
|
16
13
|
|
|
17
|
-
|
|
14
|
+
function App() @{
|
|
18
15
|
<Editor
|
|
19
16
|
height="400px"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
onChange={(value) =>
|
|
17
|
+
defaultLanguage="typescript"
|
|
18
|
+
defaultValue="// hello"
|
|
19
|
+
onChange={(value) => console.log(value)}
|
|
23
20
|
/>
|
|
24
21
|
}
|
|
25
22
|
```
|
|
26
23
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
24
|
+
## Compatibility
|
|
25
|
+
|
|
26
|
+
| Upstream | Octane |
|
|
27
|
+
| --- | --- |
|
|
28
|
+
| `loading?: ReactNode` | `loading?: OctaneNode` |
|
|
29
|
+
| Internal `_ref` on MonacoContainer | Octane `ref` on the host div (internal only) |
|
|
30
|
+
| React `memo` | Octane `memo` |
|
|
31
|
+
| Library `onChange` | **Same name** (not DOM `onInput`) |
|
|
32
|
+
| Blind model dispose on unmount | **Ownership-aware dispose** — only models created by the binding are disposed; shared or external models are left intact |
|
|
33
|
+
|
|
34
|
+
See `UPSTREAM.md` for the full export crosswalk and `status.json` for the
|
|
35
|
+
binding scorecard.
|
|
36
|
+
|
|
37
|
+
## Loader and workers
|
|
38
|
+
|
|
39
|
+
The package re-exports `@monaco-editor/loader`. Workers and CSS stay in the
|
|
40
|
+
consuming app.
|
|
41
|
+
|
|
42
|
+
Pin the same `monaco-editor` minor your peer range resolves to (workspace
|
|
43
|
+
catalog / oracle: **`0.55.1`**).
|
|
44
|
+
|
|
45
|
+
### Default (recommended): npm `monaco-editor` + Vite workers
|
|
46
|
+
|
|
47
|
+
monaco-editor **0.55.1** exports `"./*": "./*"`, so worker imports use the
|
|
48
|
+
full `esm/vs/...` paths (unlike 0.56+, which maps shorthand subpaths).
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
import * as monaco from 'monaco-editor';
|
|
52
|
+
import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker';
|
|
53
|
+
import jsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker';
|
|
54
|
+
import cssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker';
|
|
55
|
+
import htmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker';
|
|
56
|
+
import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker';
|
|
57
|
+
import { loader } from '@octanejs/monaco-editor';
|
|
58
|
+
import 'monaco-editor/min/vs/editor/editor.main.css';
|
|
59
|
+
|
|
60
|
+
globalThis.MonacoEnvironment = {
|
|
61
|
+
getWorker(_id, label) {
|
|
62
|
+
switch (label) {
|
|
63
|
+
case 'json':
|
|
64
|
+
return new jsonWorker();
|
|
65
|
+
case 'css':
|
|
66
|
+
case 'scss':
|
|
67
|
+
case 'less':
|
|
68
|
+
return new cssWorker();
|
|
69
|
+
case 'html':
|
|
70
|
+
case 'handlebars':
|
|
71
|
+
case 'razor':
|
|
72
|
+
return new htmlWorker();
|
|
73
|
+
case 'typescript':
|
|
74
|
+
case 'javascript':
|
|
75
|
+
return new tsWorker();
|
|
76
|
+
default:
|
|
77
|
+
return new editorWorker();
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
loader.config({ monaco });
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Give the editor a non-zero height (default `height`/`width` are `100%`, and
|
|
86
|
+
`automaticLayout` is on).
|
|
87
|
+
|
|
88
|
+
### Alternate: CDN AMD build
|
|
89
|
+
|
|
90
|
+
Apps that intentionally avoid bundling Monaco can keep the loader CDN paths:
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
import { loader } from '@octanejs/monaco-editor';
|
|
94
|
+
|
|
95
|
+
loader.config({
|
|
96
|
+
paths: {
|
|
97
|
+
vs: 'https://cdn.jsdelivr.net/npm/monaco-editor@0.55.1/min/vs',
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## SSR / Hydrate
|
|
103
|
+
|
|
104
|
+
Do not call `loader.init()` on the server. `Editor` / `DiffEditor` render a
|
|
105
|
+
loading shell during SSR; after `hydrateRoot`, effects create the editor. Prefer
|
|
106
|
+
client-only mount or Octane `lazy` when the page does not need the shell in HTML.
|
|
107
|
+
|
|
108
|
+
## Multi-model
|
|
109
|
+
|
|
110
|
+
`path` / `defaultPath` create models via `monaco.Uri.parse`. View state is kept
|
|
111
|
+
in a process-global `WeakMap` keyed by model identity when `saveViewState` is
|
|
112
|
+
true (upstream 4.7.0 uses a path-string `Map`). Models the binding creates are
|
|
113
|
+
tracked for ownership-aware disposal on unmount.
|
|
114
|
+
|
|
115
|
+
## Example
|
|
116
|
+
|
|
117
|
+
Working consumer recipe: [`examples/monaco-playground`](../../examples/monaco-playground).
|
package/UPSTREAM.md
CHANGED
|
@@ -1,10 +1,118 @@
|
|
|
1
|
-
# Upstream
|
|
1
|
+
# Upstream @monaco-editor/react audit
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
- Source: https://github.com/suren-atoyan/monaco-react
|
|
6
|
-
- License: MIT
|
|
7
|
-
- Loader: `@monaco-editor/loader@1.7.0`
|
|
3
|
+
This port targets `@monaco-editor/react@4.7.0` at git commit
|
|
4
|
+
`eb120e66378471315620fe5339b73ba003f199ad` (recorded in `upstream/COMMIT`).
|
|
8
5
|
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
- repository: `https://github.com/suren-atoyan/monaco-react`
|
|
7
|
+
- package version: `4.7.0`
|
|
8
|
+
- vendored commit: `eb120e66378471315620fe5339b73ba003f199ad`
|
|
9
|
+
- loader: `@monaco-editor/loader@1.7.0` (framework-neutral; imported, not vendored)
|
|
10
|
+
- monaco oracle: `monaco-editor@0.55.1` (catalog / Echo validation pin)
|
|
11
|
+
- license: MIT, Copyright (c) 2018 Suren Atoyan
|
|
12
|
+
|
|
13
|
+
## Audit artifacts
|
|
14
|
+
|
|
15
|
+
React-parity evidence lives under `audit/` and is regenerated with:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pnpm --filter @octanejs/monaco-editor parity:generate
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
| File | Role |
|
|
22
|
+
| --- | --- |
|
|
23
|
+
| `audit/react-parity.json` | Bounded parity manifest (lanes, divergences, integrity) |
|
|
24
|
+
| `audit/adapted-runtime.json` | Adapted upstream case inventory |
|
|
25
|
+
| `audit/pristine-runtime.json` | Six vendored upstream RTL spec identities |
|
|
26
|
+
| `audit/pristine-wrapper-runtime.json` | Wrapper identity for `tests/upstream-original.test.ts` |
|
|
27
|
+
| `audit/differential-runtime.json` | React/Octane differential case inventory |
|
|
28
|
+
| `audit/pristine-types.json` / `audit/adapted-types.json` | Per-group type assertion inventories |
|
|
29
|
+
| `audit/type-transformations.json` | Permitted type-lane transforms ledger |
|
|
30
|
+
| `audit/test-classifications.json` | Disposition for every test and typetest file |
|
|
31
|
+
|
|
32
|
+
Pristine runtime specs run against **React 19.2.7** via `tests/upstream-pristine.vitest.config.ts` and `scripts/run-pristine-upstream.mjs`.
|
|
33
|
+
|
|
34
|
+
## Immutable React oracle
|
|
35
|
+
|
|
36
|
+
Differential and pristine typetest lanes compile fixtures against pinned React
|
|
37
|
+
packages (exact versions):
|
|
38
|
+
|
|
39
|
+
| Package | Version |
|
|
40
|
+
| --- | --- |
|
|
41
|
+
| `react` | `19.2.7` |
|
|
42
|
+
| `react-dom` | `19.2.7` |
|
|
43
|
+
| `@types/react` | `19.2.7` |
|
|
44
|
+
|
|
45
|
+
Advertised peers match upstream: `monaco-editor >= 0.25.0 < 1`, plus Octane
|
|
46
|
+
instead of React.
|
|
47
|
+
|
|
48
|
+
## Source boundary
|
|
49
|
+
|
|
50
|
+
Vendored byte-exact under `upstream/` (prettier-ignored, unpublished):
|
|
51
|
+
|
|
52
|
+
- `upstream/src/**` — React binding source + Vitest specs + snapshots
|
|
53
|
+
- `upstream/LICENSE`, `upstream/package.json`, `upstream/COMMIT`
|
|
54
|
+
|
|
55
|
+
Everything under `src/` is re-implemented against Octane hooks / `.tsrx`.
|
|
56
|
+
`@monaco-editor/loader` is imported unchanged (not vendored).
|
|
57
|
+
|
|
58
|
+
## Export crosswalk
|
|
59
|
+
|
|
60
|
+
Every runtime and type export of `src/index.ts` at the pin.
|
|
61
|
+
|
|
62
|
+
| Export | Kind | Disposition | Evidence |
|
|
63
|
+
| --- | --- | --- | --- |
|
|
64
|
+
| `Editor` (also `default`) | component | ported | `tests/upstream/editor-shell.test.ts`, `tests/runtime/editor.test.ts`, differential, browser, SSR, hydration |
|
|
65
|
+
| `DiffEditor` | component | ported | `tests/upstream/diff-editor-shell.test.ts`, `tests/runtime/editor.test.ts`, differential, browser, SSR |
|
|
66
|
+
| `loader` | re-export | reused verbatim (`@monaco-editor/loader`) | shell tests + browser harness |
|
|
67
|
+
| `useMonaco` | hook | ported | `tests/runtime/use-monaco.test.ts`, differential |
|
|
68
|
+
| `OnMount` / `BeforeMount` / `OnChange` / `OnValidate` / `EditorProps` | types | ported (`ReactNode`→`OctaneNode`) | typetests |
|
|
69
|
+
| `MonacoDiffEditor` / `DiffOnMount` / `DiffBeforeMount` / `DiffEditorProps` | types | ported | typetests |
|
|
70
|
+
| `Monaco` | type | ported | typetests |
|
|
71
|
+
| `Theme` | type | ported | typetests |
|
|
72
|
+
|
|
73
|
+
### Intentional divergences
|
|
74
|
+
|
|
75
|
+
| Surface | Upstream (4.7.0) | Octane port |
|
|
76
|
+
| --- | --- | --- |
|
|
77
|
+
| JSX dialect | `.tsx` / React hooks | `.tsrx` / `@jsxImportSource octane`; ship source, never compiler output |
|
|
78
|
+
| `loading` | `ReactNode` | `OctaneNode` (string still accepted at runtime) |
|
|
79
|
+
| MonacoContainer ref | private `_ref` prop on container div | Octane `ref` as an ordinary prop on the host div |
|
|
80
|
+
| Model disposal | `editorRef.current!.getModel()?.dispose()` on unmount when `keepCurrentModel` is false — disposes whatever model is attached, including externally created models | **Ownership-aware dispose** via `WeakSet` (binding-created models) and `WeakMap` lease counts; only binding-owned models are disposed, and superseded owned path models are cleaned up on unmount |
|
|
81
|
+
| View state storage | process-global `Map` keyed by **path string** (`const viewStates = new Map()` in `upstream/src/Editor/Editor.tsx`) | process-global `WeakMap` keyed by **model identity** (`modelViewStates` in `src/Editor/Editor.tsrx`) so kept models retain view state after path changes |
|
|
82
|
+
| `'use client'` | present on upstream Editor/DiffEditor | omitted; browser-only behavior via effects |
|
|
83
|
+
| `CSSProperties` | React `CSSProperties` in style modules | plain `Record<string, Record<string, string \| number>>` style records |
|
|
84
|
+
| StrictMode double-invoke | upstream dev behavior | N/A — not tested |
|
|
85
|
+
|
|
86
|
+
## Test disposition
|
|
87
|
+
|
|
88
|
+
| Upstream file | Disposition |
|
|
89
|
+
| --- | --- |
|
|
90
|
+
| `src/Loading/index.spec.tsx` | **ported** → `tests/upstream/loading.test.ts` (+ `.tsrx` fixture) |
|
|
91
|
+
| `src/MonacoContainer/index.spec.tsx` | **ported** → `tests/upstream/monaco-container.test.ts` (passes `ref=`, not `_ref`) |
|
|
92
|
+
| `src/Editor/index.spec.tsx` | **ported** → `tests/upstream/editor-shell.test.ts` (loader mocked) |
|
|
93
|
+
| `src/DiffEditor/index.spec.tsx` | **ported** → `tests/upstream/diff-editor-shell.test.ts` |
|
|
94
|
+
| `**/__snapshots__/*.snap` | Adapted to container HTML / style assertions — RTL render-object snapshots are harness noise |
|
|
95
|
+
|
|
96
|
+
Upstream ships **no type tests**. Both type lanes are port-authored.
|
|
97
|
+
|
|
98
|
+
## Port-authored test classification
|
|
99
|
+
|
|
100
|
+
| File / directory | Classification | Pairing |
|
|
101
|
+
| --- | --- | --- |
|
|
102
|
+
| `tests/upstream/*.test.ts` | adapted upstream | re-authors the four upstream RTL snapshot specs only |
|
|
103
|
+
| `tests/runtime/*.test.ts` | Octane-only runtime contract | unpaired — lifecycle, loader race, useMonaco, public surface against monaco doubles |
|
|
104
|
+
| `tests/differential/parity.test.ts` | React/Octane differential | pinned `@monaco-editor/react@4.7.0` |
|
|
105
|
+
| `tests/ssr/ssr.test.ts` | Octane-only framework contract | unpaired — upstream ships no SSR suite |
|
|
106
|
+
| `tests/hydration/hydration.test.ts` | Octane-only framework contract | unpaired — hydration adoption is Octane's |
|
|
107
|
+
| `tests/browser/editor.browser.test.ts` | Octane-only browser contract | unpaired — real Monaco + Vite `?worker` harness |
|
|
108
|
+
| `tests/harness/negative-controls.test.ts` | harness negative controls | unpaired — inventory titles / divergence citation; not adapted-lane evidence |
|
|
109
|
+
| `typetests/{pristine,adapted}/types.test-d.ts` | port-authored type lanes | paired with each other through `typetests/assertions.md` |
|
|
110
|
+
|
|
111
|
+
## Evidence lanes
|
|
112
|
+
|
|
113
|
+
- Adapted upstream snapshot ports (Loading / MonacoContainer / Editor / DiffEditor shells)
|
|
114
|
+
- Port-authored runtime lifecycle/useMonaco/loader-race suites (mocked loader + monaco doubles)
|
|
115
|
+
- React/Octane differential with pinned `@monaco-editor/react@4.7.0` (loading-shell + held-init useMonaco pending `step`s)
|
|
116
|
+
- SSR loading shell (`tests/ssr`) and hydration DOM adoption (`tests/hydration`)
|
|
117
|
+
- Package Chromium browser + npm workers (`tests/browser`), including language/theme sync and controlled-value remount
|
|
118
|
+
- Example consumer: [`examples/monaco-playground`](../../examples/monaco-playground)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@octanejs/monaco-editor",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -37,18 +37,29 @@
|
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
39
|
"monaco-editor": ">=0.25.0 <1",
|
|
40
|
-
"octane": "0.1.
|
|
40
|
+
"octane": "0.1.36"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@monaco-editor/react": "4.7.0",
|
|
44
44
|
"@testing-library/jest-dom": "^6.9.1",
|
|
45
|
+
"@testing-library/react": "16.3.2",
|
|
46
|
+
"@tsrx/react": "^0.2.56",
|
|
47
|
+
"@types/react": "19.2.7",
|
|
48
|
+
"@types/react-dom": "19.2.3",
|
|
49
|
+
"@vitejs/plugin-react": "^6.0.3",
|
|
50
|
+
"esbuild": "^0.28.1",
|
|
45
51
|
"monaco-editor": "0.55.1",
|
|
52
|
+
"playwright": "^1.61.1",
|
|
53
|
+
"react": "19.2.7",
|
|
54
|
+
"react-dom": "19.2.7",
|
|
55
|
+
"vite": "^8.1.5",
|
|
46
56
|
"vitest": "^4.1.10",
|
|
47
|
-
"@octanejs/testing-library": "0.1.
|
|
48
|
-
"octane": "0.1.
|
|
57
|
+
"@octanejs/testing-library": "0.1.33",
|
|
58
|
+
"octane": "0.1.36"
|
|
49
59
|
},
|
|
50
60
|
"scripts": {
|
|
51
61
|
"test": "vitest run --root ../.. --project monaco-editor",
|
|
52
|
-
"typecheck": "tsrx-tsc --noEmit -p tsconfig.json"
|
|
62
|
+
"typecheck": "tsrx-tsc --noEmit -p tsconfig.json",
|
|
63
|
+
"parity:generate": "node ./scripts/generate-parity-manifest.mjs"
|
|
53
64
|
}
|
|
54
65
|
}
|
|
@@ -2,10 +2,11 @@ import loader from '@monaco-editor/loader';
|
|
|
2
2
|
import type { editor } from 'monaco-editor';
|
|
3
3
|
import { memo, useEffect, useRef, useState } from 'octane';
|
|
4
4
|
|
|
5
|
-
import
|
|
6
|
-
import { isLoaderCancelation } from '
|
|
7
|
-
import { getOrCreateModel, releaseModels } from '
|
|
8
|
-
import type {
|
|
5
|
+
import MonacoContainer from '../MonacoContainer';
|
|
6
|
+
import { isLoaderCancelation } from '../loader';
|
|
7
|
+
import { getOrCreateModel, releaseModels } from '../model';
|
|
8
|
+
import type { Monaco } from '../types';
|
|
9
|
+
import type { DiffEditorProps, MonacoDiffEditor } from './types';
|
|
9
10
|
|
|
10
11
|
const noop = (): void => {};
|
|
11
12
|
const EMPTY_OPTIONS: editor.IDiffEditorConstructionOptions = {};
|
|
@@ -231,15 +232,16 @@ function DiffEditorComponent(props: DiffEditorProps) @{
|
|
|
231
232
|
if (isEditorReady && monaco) monaco.editor.setTheme(theme);
|
|
232
233
|
}, [theme]);
|
|
233
234
|
|
|
234
|
-
<
|
|
235
|
+
<MonacoContainer
|
|
235
236
|
width={width}
|
|
236
237
|
height={height}
|
|
237
238
|
isEditorReady={isEditorReady}
|
|
238
239
|
loading={loading}
|
|
239
|
-
|
|
240
|
+
ref={containerRef}
|
|
240
241
|
className={className}
|
|
241
242
|
wrapperProps={wrapperProps}
|
|
242
243
|
/>
|
|
243
244
|
}
|
|
244
245
|
|
|
245
|
-
|
|
246
|
+
const DiffEditor = memo(DiffEditorComponent);
|
|
247
|
+
export default DiffEditor;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { OctaneNode } from 'octane';
|
|
2
|
+
import type { editor } from 'monaco-editor';
|
|
3
|
+
|
|
4
|
+
import type { Monaco, Theme } from '../types';
|
|
5
|
+
import type { WrapperProps } from '../MonacoContainer/types';
|
|
6
|
+
|
|
7
|
+
export type MonacoDiffEditor = editor.IStandaloneDiffEditor;
|
|
8
|
+
|
|
9
|
+
export type DiffBeforeMount = (monaco: Monaco) => void;
|
|
10
|
+
export type DiffOnMount = (editor: MonacoDiffEditor, monaco: Monaco) => void;
|
|
11
|
+
|
|
12
|
+
export interface DiffEditorProps {
|
|
13
|
+
original?: string;
|
|
14
|
+
modified?: string;
|
|
15
|
+
language?: string;
|
|
16
|
+
originalLanguage?: string;
|
|
17
|
+
modifiedLanguage?: string;
|
|
18
|
+
originalModelPath?: string;
|
|
19
|
+
modifiedModelPath?: string;
|
|
20
|
+
keepCurrentOriginalModel?: boolean;
|
|
21
|
+
keepCurrentModifiedModel?: boolean;
|
|
22
|
+
theme?: Theme | string;
|
|
23
|
+
loading?: OctaneNode;
|
|
24
|
+
options?: editor.IDiffEditorConstructionOptions;
|
|
25
|
+
width?: number | string;
|
|
26
|
+
height?: number | string;
|
|
27
|
+
className?: string;
|
|
28
|
+
wrapperProps?: WrapperProps;
|
|
29
|
+
beforeMount?: DiffBeforeMount;
|
|
30
|
+
onMount?: DiffOnMount;
|
|
31
|
+
}
|
|
@@ -2,10 +2,11 @@ import loader from '@monaco-editor/loader';
|
|
|
2
2
|
import type { IDisposable, editor } from 'monaco-editor';
|
|
3
3
|
import { memo, useEffect, useRef, useState } from 'octane';
|
|
4
4
|
|
|
5
|
-
import
|
|
6
|
-
import { isLoaderCancelation } from '
|
|
7
|
-
import { getOrCreateModel, releaseModels } from '
|
|
8
|
-
import type {
|
|
5
|
+
import MonacoContainer from '../MonacoContainer';
|
|
6
|
+
import { isLoaderCancelation } from '../loader';
|
|
7
|
+
import { getOrCreateModel, releaseModels } from '../model';
|
|
8
|
+
import type { Monaco } from '../types';
|
|
9
|
+
import type { EditorProps, MonacoEditor } from './types';
|
|
9
10
|
|
|
10
11
|
const noop = (): void => {};
|
|
11
12
|
const EMPTY_OPTIONS: editor.IStandaloneEditorConstructionOptions = {};
|
|
@@ -255,16 +256,16 @@ function EditorComponent(props: EditorProps) @{
|
|
|
255
256
|
if (isEditorReady && monaco) monaco.editor.setTheme(theme);
|
|
256
257
|
}, [theme]);
|
|
257
258
|
|
|
258
|
-
<
|
|
259
|
+
<MonacoContainer
|
|
259
260
|
width={width}
|
|
260
261
|
height={height}
|
|
261
262
|
isEditorReady={isEditorReady}
|
|
262
263
|
loading={loading}
|
|
263
|
-
|
|
264
|
+
ref={containerRef}
|
|
264
265
|
className={className}
|
|
265
266
|
wrapperProps={wrapperProps}
|
|
266
267
|
/>
|
|
267
268
|
}
|
|
268
269
|
|
|
269
|
-
|
|
270
|
+
const Editor = memo(EditorComponent);
|
|
270
271
|
export default Editor;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { OctaneNode } from 'octane';
|
|
2
|
+
import type { editor } from 'monaco-editor';
|
|
3
|
+
|
|
4
|
+
import type { Monaco, Theme } from '../types';
|
|
5
|
+
import type { WrapperProps } from '../MonacoContainer/types';
|
|
6
|
+
|
|
7
|
+
export type MonacoEditor = editor.IStandaloneCodeEditor;
|
|
8
|
+
|
|
9
|
+
export type BeforeMount = (monaco: Monaco) => void;
|
|
10
|
+
export type OnMount = (editor: MonacoEditor, monaco: Monaco) => void;
|
|
11
|
+
export type OnChange = (value: string | undefined, event: editor.IModelContentChangedEvent) => void;
|
|
12
|
+
export type OnValidate = (markers: editor.IMarker[]) => void;
|
|
13
|
+
|
|
14
|
+
export interface EditorProps {
|
|
15
|
+
defaultValue?: string;
|
|
16
|
+
defaultLanguage?: string;
|
|
17
|
+
defaultPath?: string;
|
|
18
|
+
value?: string;
|
|
19
|
+
language?: string;
|
|
20
|
+
path?: string;
|
|
21
|
+
theme?: Theme | string;
|
|
22
|
+
line?: number;
|
|
23
|
+
loading?: OctaneNode;
|
|
24
|
+
options?: editor.IStandaloneEditorConstructionOptions;
|
|
25
|
+
overrideServices?: editor.IEditorOverrideServices;
|
|
26
|
+
saveViewState?: boolean;
|
|
27
|
+
keepCurrentModel?: boolean;
|
|
28
|
+
width?: number | string;
|
|
29
|
+
height?: number | string;
|
|
30
|
+
className?: string;
|
|
31
|
+
wrapperProps?: WrapperProps;
|
|
32
|
+
beforeMount?: BeforeMount;
|
|
33
|
+
onMount?: OnMount;
|
|
34
|
+
onChange?: OnChange;
|
|
35
|
+
onValidate?: OnValidate;
|
|
36
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { OctaneNode } from 'octane';
|
|
2
|
+
|
|
3
|
+
import styles from './styles';
|
|
4
|
+
|
|
5
|
+
export type LoadingProps = {
|
|
6
|
+
children?: OctaneNode;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
function Loading(props: LoadingProps = {}) @{
|
|
10
|
+
<div style={styles.container}>{props.children}</div>
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export default Loading;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import Loading from '../Loading';
|
|
2
|
+
import styles from './styles';
|
|
3
|
+
import type { ContainerProps } from './types';
|
|
4
|
+
|
|
5
|
+
// Upstream used a separate `_ref` prop to avoid React forwardRef. Octane takes
|
|
6
|
+
// `ref` as an ordinary prop on the host div instead.
|
|
7
|
+
|
|
8
|
+
function MonacoContainer(props: ContainerProps) @{
|
|
9
|
+
const { width, height, isEditorReady, loading, ref, className, wrapperProps = {} } = props;
|
|
10
|
+
<section style={{ ...styles.wrapper, width, height }} {...wrapperProps}>
|
|
11
|
+
@if (!isEditorReady) {
|
|
12
|
+
<Loading>{loading}</Loading>
|
|
13
|
+
}
|
|
14
|
+
<div
|
|
15
|
+
ref={ref}
|
|
16
|
+
style={{ ...styles.fullWidth, ...(!isEditorReady ? styles.hide : {}) }}
|
|
17
|
+
class={className}
|
|
18
|
+
/>
|
|
19
|
+
</section>
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export default MonacoContainer;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const styles: Record<string, Record<string, string | number>> = {
|
|
2
|
+
wrapper: {
|
|
3
|
+
display: 'flex',
|
|
4
|
+
position: 'relative',
|
|
5
|
+
textAlign: 'initial',
|
|
6
|
+
},
|
|
7
|
+
fullWidth: {
|
|
8
|
+
width: '100%',
|
|
9
|
+
},
|
|
10
|
+
hide: {
|
|
11
|
+
display: 'none',
|
|
12
|
+
},
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export default styles;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { OctaneNode } from 'octane';
|
|
2
|
+
import type { Octane } from 'octane/jsx-runtime';
|
|
3
|
+
|
|
4
|
+
export type WrapperProps = Omit<Octane.JSX.IntrinsicElements['section'], 'children'>;
|
|
5
|
+
|
|
6
|
+
export type ContainerProps = {
|
|
7
|
+
width: number | string;
|
|
8
|
+
height: number | string;
|
|
9
|
+
isEditorReady: boolean;
|
|
10
|
+
loading: OctaneNode;
|
|
11
|
+
/** Octane host ref (upstream used private `_ref`). */
|
|
12
|
+
ref: { current: HTMLDivElement | null };
|
|
13
|
+
className?: string;
|
|
14
|
+
wrapperProps?: WrapperProps;
|
|
15
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default, useMonaco } from './useMonaco.tsrx';
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import loader from '@monaco-editor/loader';
|
|
2
2
|
import { useEffect, useState } from 'octane';
|
|
3
3
|
|
|
4
|
-
import { isLoaderCancelation } from '
|
|
5
|
-
import type { Monaco } from '
|
|
4
|
+
import { isLoaderCancelation } from '../../loader';
|
|
5
|
+
import type { Monaco } from '../../types';
|
|
6
6
|
|
|
7
7
|
export function useMonaco(): Monaco | null {
|
|
8
8
|
const [monaco, setMonaco] = useState<Monaco | null>(loader.__getMonacoInstance());
|
|
@@ -28,3 +28,5 @@ export function useMonaco(): Monaco | null {
|
|
|
28
28
|
|
|
29
29
|
return monaco;
|
|
30
30
|
}
|
|
31
|
+
|
|
32
|
+
export default useMonaco;
|
package/src/index.ts
CHANGED
|
@@ -1,19 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
export {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
export
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
WrapperProps,
|
|
19
|
-
} from './types';
|
|
1
|
+
import loader from '@monaco-editor/loader';
|
|
2
|
+
export { loader };
|
|
3
|
+
|
|
4
|
+
import DiffEditor from './DiffEditor';
|
|
5
|
+
export * from './DiffEditor/types';
|
|
6
|
+
export { DiffEditor };
|
|
7
|
+
|
|
8
|
+
import useMonaco from './hooks/useMonaco';
|
|
9
|
+
export { useMonaco };
|
|
10
|
+
|
|
11
|
+
import Editor from './Editor';
|
|
12
|
+
export * from './Editor/types';
|
|
13
|
+
export { Editor };
|
|
14
|
+
export default Editor;
|
|
15
|
+
|
|
16
|
+
export type { Monaco, Theme } from './types';
|
|
17
|
+
export type { WrapperProps } from './MonacoContainer/types';
|
package/src/model.ts
CHANGED
|
@@ -5,6 +5,11 @@ import type { Monaco } from './types';
|
|
|
5
5
|
const bindingOwnedModels = new WeakSet<editor.ITextModel>();
|
|
6
6
|
const modelLeaseCounts = new WeakMap<editor.ITextModel, number>();
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* Ownership-aware model helper. Upstream's utils/getOrCreateModel returns or
|
|
10
|
+
* creates a model by path; this port additionally tracks lease counts so shared
|
|
11
|
+
* and externally-created models are not disposed by the wrong owner.
|
|
12
|
+
*/
|
|
8
13
|
export function getOrCreateModel(
|
|
9
14
|
monaco: Monaco,
|
|
10
15
|
value: string,
|
package/src/types.ts
CHANGED
|
@@ -1,65 +1,4 @@
|
|
|
1
|
-
import type { OctaneNode } from 'octane';
|
|
2
|
-
import type { Octane } from 'octane/jsx-runtime';
|
|
3
|
-
import type { editor } from 'monaco-editor';
|
|
4
1
|
import type * as monaco from 'monaco-editor';
|
|
5
2
|
|
|
6
3
|
export type Monaco = typeof monaco;
|
|
7
4
|
export type Theme = 'vs-dark' | 'light';
|
|
8
|
-
|
|
9
|
-
export type MonacoEditor = editor.IStandaloneCodeEditor;
|
|
10
|
-
export type MonacoDiffEditor = editor.IStandaloneDiffEditor;
|
|
11
|
-
|
|
12
|
-
export type BeforeMount = (monaco: Monaco) => void;
|
|
13
|
-
export type OnMount = (editor: MonacoEditor, monaco: Monaco) => void;
|
|
14
|
-
export type OnChange = (value: string | undefined, event: editor.IModelContentChangedEvent) => void;
|
|
15
|
-
export type OnValidate = (markers: editor.IMarker[]) => void;
|
|
16
|
-
|
|
17
|
-
export type DiffBeforeMount = (monaco: Monaco) => void;
|
|
18
|
-
export type DiffOnMount = (editor: MonacoDiffEditor, monaco: Monaco) => void;
|
|
19
|
-
|
|
20
|
-
export type WrapperProps = Omit<Octane.JSX.IntrinsicElements['section'], 'children'>;
|
|
21
|
-
|
|
22
|
-
export interface EditorProps {
|
|
23
|
-
defaultValue?: string;
|
|
24
|
-
defaultLanguage?: string;
|
|
25
|
-
defaultPath?: string;
|
|
26
|
-
value?: string;
|
|
27
|
-
language?: string;
|
|
28
|
-
path?: string;
|
|
29
|
-
theme?: Theme | string;
|
|
30
|
-
line?: number;
|
|
31
|
-
loading?: OctaneNode;
|
|
32
|
-
options?: editor.IStandaloneEditorConstructionOptions;
|
|
33
|
-
overrideServices?: editor.IEditorOverrideServices;
|
|
34
|
-
saveViewState?: boolean;
|
|
35
|
-
keepCurrentModel?: boolean;
|
|
36
|
-
width?: number | string;
|
|
37
|
-
height?: number | string;
|
|
38
|
-
className?: string;
|
|
39
|
-
wrapperProps?: WrapperProps;
|
|
40
|
-
beforeMount?: BeforeMount;
|
|
41
|
-
onMount?: OnMount;
|
|
42
|
-
onChange?: OnChange;
|
|
43
|
-
onValidate?: OnValidate;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export interface DiffEditorProps {
|
|
47
|
-
original?: string;
|
|
48
|
-
modified?: string;
|
|
49
|
-
language?: string;
|
|
50
|
-
originalLanguage?: string;
|
|
51
|
-
modifiedLanguage?: string;
|
|
52
|
-
originalModelPath?: string;
|
|
53
|
-
modifiedModelPath?: string;
|
|
54
|
-
keepCurrentOriginalModel?: boolean;
|
|
55
|
-
keepCurrentModifiedModel?: boolean;
|
|
56
|
-
theme?: Theme | string;
|
|
57
|
-
loading?: OctaneNode;
|
|
58
|
-
options?: editor.IDiffEditorConstructionOptions;
|
|
59
|
-
width?: number | string;
|
|
60
|
-
height?: number | string;
|
|
61
|
-
className?: string;
|
|
62
|
-
wrapperProps?: WrapperProps;
|
|
63
|
-
beforeMount?: DiffBeforeMount;
|
|
64
|
-
onMount?: DiffOnMount;
|
|
65
|
-
}
|
package/src/Container.tsrx
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import type { OctaneNode } from 'octane';
|
|
2
|
-
|
|
3
|
-
import type { WrapperProps } from './types';
|
|
4
|
-
|
|
5
|
-
const LOADING_STYLE = {
|
|
6
|
-
alignItems: 'center',
|
|
7
|
-
display: 'flex',
|
|
8
|
-
height: '100%',
|
|
9
|
-
justifyContent: 'center',
|
|
10
|
-
width: '100%',
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
export interface ContainerProps {
|
|
14
|
-
width: number | string;
|
|
15
|
-
height: number | string;
|
|
16
|
-
isEditorReady: boolean;
|
|
17
|
-
loading: OctaneNode;
|
|
18
|
-
containerRef: { current: HTMLDivElement | null };
|
|
19
|
-
className?: string;
|
|
20
|
-
wrapperProps: WrapperProps;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export function Container(props: ContainerProps) @{
|
|
24
|
-
const { width, height, isEditorReady, loading, containerRef, className, wrapperProps } = props;
|
|
25
|
-
const wrapperStyle = {
|
|
26
|
-
display: 'flex' as const,
|
|
27
|
-
position: 'relative' as const,
|
|
28
|
-
textAlign: 'initial' as const,
|
|
29
|
-
width,
|
|
30
|
-
height,
|
|
31
|
-
};
|
|
32
|
-
const editorStyle = {
|
|
33
|
-
display: isEditorReady ? undefined : 'none' as const,
|
|
34
|
-
width: '100%',
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
<section style={wrapperStyle} {...wrapperProps}>
|
|
38
|
-
@if (!isEditorReady) {
|
|
39
|
-
<div style={LOADING_STYLE}>{loading}</div>
|
|
40
|
-
}
|
|
41
|
-
<div ref={containerRef} style={editorStyle} class={className} />
|
|
42
|
-
</section>
|
|
43
|
-
}
|
package/src/Container.tsrx.d.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import type { OctaneNode } from 'octane';
|
|
2
|
-
import type { Octane } from 'octane/jsx-runtime';
|
|
3
|
-
|
|
4
|
-
import type { WrapperProps } from './types';
|
|
5
|
-
|
|
6
|
-
export interface ContainerProps {
|
|
7
|
-
width: number | string;
|
|
8
|
-
height: number | string;
|
|
9
|
-
isEditorReady: boolean;
|
|
10
|
-
loading: OctaneNode;
|
|
11
|
-
containerRef: { current: HTMLDivElement | null };
|
|
12
|
-
className?: string;
|
|
13
|
-
wrapperProps: WrapperProps;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export declare function Container(props: ContainerProps): Octane.JSX.Element;
|
package/src/DiffEditor.tsrx.d.ts
DELETED
package/src/useMonaco.tsrx.d.ts
DELETED