@octanejs/monaco-editor 0.0.3 → 0.0.5

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 CHANGED
@@ -1,30 +1,117 @@
1
- # @octanejs/monaco-editor
1
+ # `@octanejs/monaco-editor`
2
2
 
3
- Monaco Editor components for Octane. This binding reuses `monaco-editor` and
4
- `@monaco-editor/loader` unchanged and ports the `@monaco-editor/react@4.7.0`
5
- adapter layer to compiled Octane components.
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
- export function CodeEditor(props: { value: string; onChange(value: string): void }) @{
14
+ function App() @{
18
15
  <Editor
19
16
  height="400px"
20
- language="typescript"
21
- value={props.value}
22
- onChange={(value) => props.onChange(value ?? '')}
17
+ defaultLanguage="typescript"
18
+ defaultValue="// hello"
19
+ onChange={(value) => console.log(value)}
23
20
  />
24
21
  }
25
22
  ```
26
23
 
27
- The root entry exports `Editor` (also the default export), `DiffEditor`,
28
- `useMonaco`, `loader`, and the corresponding public Monaco-backed prop and
29
- callback types. Models created by a component are disposed on unmount unless
30
- the matching `keepCurrent*Model` option preserves the active model.
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
- - Package: `@monaco-editor/react`
4
- - Version: `4.7.0`
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
- The component and hook layer is re-authored for Octane. The framework-neutral
10
- Monaco editor and loader packages are consumed unchanged.
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",
3
+ "version": "0.0.5",
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.35"
40
+ "octane": "0.1.37"
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.32",
48
- "octane": "0.1.35"
57
+ "@octanejs/testing-library": "0.1.34",
58
+ "octane": "0.1.37"
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 { Container } from './Container.tsrx';
6
- import { isLoaderCancelation } from './loader';
7
- import { getOrCreateModel, releaseModels } from './model';
8
- import type { DiffEditorProps, Monaco, MonacoDiffEditor } from './types';
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
- <Container
235
+ <MonacoContainer
235
236
  width={width}
236
237
  height={height}
237
238
  isEditorReady={isEditorReady}
238
239
  loading={loading}
239
- containerRef={containerRef}
240
+ ref={containerRef}
240
241
  className={className}
241
242
  wrapperProps={wrapperProps}
242
243
  />
243
244
  }
244
245
 
245
- export const DiffEditor = memo(DiffEditorComponent);
246
+ const DiffEditor = memo(DiffEditorComponent);
247
+ export default DiffEditor;
@@ -0,0 +1,6 @@
1
+ import type { Octane } from 'octane/jsx-runtime';
2
+
3
+ import type { DiffEditorProps } from './types';
4
+
5
+ declare const DiffEditor: (props: DiffEditorProps) => Octane.JSX.Element;
6
+ export default DiffEditor;
@@ -0,0 +1,2 @@
1
+ export { default } from './DiffEditor.tsrx';
2
+ export * from './types';
@@ -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 { Container } from './Container.tsrx';
6
- import { isLoaderCancelation } from './loader';
7
- import { getOrCreateModel, releaseModels } from './model';
8
- import type { EditorProps, Monaco, MonacoEditor } from './types';
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
- <Container
259
+ <MonacoContainer
259
260
  width={width}
260
261
  height={height}
261
262
  isEditorReady={isEditorReady}
262
263
  loading={loading}
263
- containerRef={containerRef}
264
+ ref={containerRef}
264
265
  className={className}
265
266
  wrapperProps={wrapperProps}
266
267
  />
267
268
  }
268
269
 
269
- export const Editor = memo(EditorComponent);
270
+ const Editor = memo(EditorComponent);
270
271
  export default Editor;
@@ -2,5 +2,5 @@ import type { Octane } from 'octane/jsx-runtime';
2
2
 
3
3
  import type { EditorProps } from './types';
4
4
 
5
- export declare const Editor: (props: EditorProps) => Octane.JSX.Element;
5
+ declare const Editor: (props: EditorProps) => Octane.JSX.Element;
6
6
  export default Editor;
@@ -0,0 +1,2 @@
1
+ export { default } from './Editor.tsrx';
2
+ export * from './types';
@@ -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,9 @@
1
+ import type { Octane } from 'octane/jsx-runtime';
2
+ import type { OctaneNode } from 'octane';
3
+
4
+ export type LoadingProps = {
5
+ children?: OctaneNode;
6
+ };
7
+
8
+ declare function Loading(props?: LoadingProps): Octane.JSX.Element;
9
+ export default Loading;
@@ -0,0 +1,2 @@
1
+ export { default } from './Loading.tsrx';
2
+ export type { LoadingProps } from './Loading.tsrx';
@@ -0,0 +1,11 @@
1
+ const styles: Record<string, Record<string, string | number>> = {
2
+ container: {
3
+ display: 'flex',
4
+ height: '100%',
5
+ width: '100%',
6
+ justifyContent: 'center',
7
+ alignItems: 'center',
8
+ },
9
+ };
10
+
11
+ export default styles;
@@ -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,6 @@
1
+ import type { Octane } from 'octane/jsx-runtime';
2
+
3
+ import type { ContainerProps } from './types';
4
+
5
+ declare function MonacoContainer(props: ContainerProps): Octane.JSX.Element;
6
+ export default MonacoContainer;
@@ -0,0 +1,2 @@
1
+ export { default } from './MonacoContainer.tsrx';
2
+ export type { ContainerProps, WrapperProps } from './types';
@@ -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 './loader';
5
- import type { Monaco } from './types';
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;
@@ -0,0 +1,4 @@
1
+ import type { Monaco } from '../../types';
2
+
3
+ export declare function useMonaco(): Monaco | null;
4
+ export default useMonaco;
package/src/index.ts CHANGED
@@ -1,19 +1,17 @@
1
- export { default, Editor } from './Editor.tsrx';
2
- export { DiffEditor } from './DiffEditor.tsrx';
3
- export { useMonaco } from './useMonaco.tsrx';
4
- export { default as loader } from '@monaco-editor/loader';
5
- export type {
6
- BeforeMount,
7
- DiffBeforeMount,
8
- DiffEditorProps,
9
- DiffOnMount,
10
- EditorProps,
11
- Monaco,
12
- MonacoDiffEditor,
13
- MonacoEditor,
14
- OnChange,
15
- OnMount,
16
- OnValidate,
17
- Theme,
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
- }
@@ -0,0 +1,4 @@
1
+ export { getOrCreateModel, releaseModels } from '../model';
2
+
3
+ /** Upstream utils also export a no-op; kept for layout parity. */
4
+ export function noop(): void {}
@@ -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
- }
@@ -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;
@@ -1,5 +0,0 @@
1
- import type { Octane } from 'octane/jsx-runtime';
2
-
3
- import type { DiffEditorProps } from './types';
4
-
5
- export declare const DiffEditor: (props: DiffEditorProps) => Octane.JSX.Element;
@@ -1,3 +0,0 @@
1
- import type { Monaco } from './types';
2
-
3
- export declare function useMonaco(): Monaco | null;