@tinacms/app 0.0.0-67b5c0b-20250414080731 → 0.0.0-6a83617-20250426154716

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/CHANGELOG.md CHANGED
@@ -1,13 +1,11 @@
1
1
  # @tinacms/app
2
2
 
3
- ## 0.0.0-67b5c0b-20250414080731
3
+ ## 0.0.0-6a83617-20250426154716
4
4
 
5
5
  ### Patch Changes
6
6
 
7
- - [#5623](https://github.com/tinacms/tinacms/pull/5623) [`613fbf0`](https://github.com/tinacms/tinacms/commit/613fbf04bd3ebcc0dbfdfadecc07f5e1068408e4) Thanks [@JackDevAU](https://github.com/JackDevAU)! - ⚠️ TESTING
8
-
9
- - Updated dependencies [[`613fbf0`](https://github.com/tinacms/tinacms/commit/613fbf04bd3ebcc0dbfdfadecc07f5e1068408e4)]:
10
- - tinacms@0.0.0-67b5c0b-20250414080731
7
+ - Updated dependencies []:
8
+ - tinacms@0.0.0-6a83617-20250426154716
11
9
 
12
10
  ## 2.2.5
13
11
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tinacms/app",
3
- "version": "0.0.0-67b5c0b-20250414080731",
3
+ "version": "0.0.0-6a83617-20250426154716",
4
4
  "main": "src/main.tsx",
5
5
  "license": "Apache-2.0",
6
6
  "devDependencies": {
@@ -8,23 +8,21 @@
8
8
  "@types/react-dom": "^18.3.5",
9
9
  "typescript": "^5.7.3"
10
10
  },
11
- "peerDependencies": {
12
- "react": ">=18.3.1 <20.0.0",
13
- "react-dom": ">=18.3.1 <20.0.0"
14
- },
15
11
  "dependencies": {
16
12
  "@graphiql/toolkit": "0.8.4",
17
13
  "@headlessui/react": "2.1.8",
18
14
  "@heroicons/react": "^1.0.6",
19
- "@monaco-editor/react": "4.7.0-rc.0",
15
+ "@monaco-editor/react": "4.4.5",
20
16
  "final-form": "4.20.10",
21
17
  "graphiql": "3.0.0-alpha.1",
22
18
  "graphql": "15.8.0",
23
19
  "monaco-editor": "0.31.0",
20
+ "react": "^18.3.1",
21
+ "react-dom": "^18.3.1",
24
22
  "react-router-dom": "6.3.0",
25
23
  "typescript": "^5.7.3",
26
24
  "zod": "^3.24.2",
27
25
  "@tinacms/mdx": "1.6.2",
28
- "tinacms": "0.0.0-67b5c0b-20250414080731"
26
+ "tinacms": "0.0.0-6a83617-20250426154716"
29
27
  }
30
28
  }
@@ -1,17 +1,29 @@
1
- import MonacoEditor, { Monaco } from '@monaco-editor/react';
1
+ /**
2
+
3
+
4
+
5
+ */
6
+
7
+ import React from 'react';
8
+ import MonacoEditor, { useMonaco, loader } from '@monaco-editor/react';
9
+ /**
10
+ * MDX is built directly to the app because of how we load dependencies.
11
+ * Since we drop the package.json in to the end users folder, we can't
12
+ * easily install the current version of the mdx package in all scenarios
13
+ * (when we're working in the monorepo, or working with a tagged npm version)
14
+ */
2
15
  import { parseMDX, stringifyMDX } from '@tinacms/mdx';
16
+ import { useDebounce } from './use-debounce';
3
17
  import type * as monaco from 'monaco-editor';
4
- import React from 'react';
5
- import { RichTextType } from 'tinacms';
6
18
  import {
19
+ buildError,
7
20
  ErrorMessage,
8
21
  InvalidMarkdownElement,
9
- buildError,
10
22
  } from './error-message';
11
- import { useDebounce } from './use-debounce';
12
- import useCustomMonaco from './use-monaco';
23
+ import { RichTextType } from 'tinacms';
13
24
 
14
25
  export const uuid = () => {
26
+ // @ts-ignore
15
27
  return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, (c) =>
16
28
  (
17
29
  c ^
@@ -20,144 +32,130 @@ export const uuid = () => {
20
32
  );
21
33
  };
22
34
 
35
+ type Monaco = typeof monaco;
36
+
37
+ // 0.33.0 has a bug https://github.com/microsoft/monaco-editor/issues/2947
38
+ loader.config({
39
+ paths: { vs: 'https://cdn.jsdelivr.net/npm/monaco-editor@0.31.1/min/vs' },
40
+ });
41
+
23
42
  /**
24
43
  * Since monaco lazy-loads we may have a delay from when the block is inserted
25
- * to when monaco has instantiated.
44
+ * to when monaco has intantiated, keep trying to focus on it.
45
+ *
46
+ * Will try for 3 seconds before moving on
26
47
  */
27
- const retryFocus = (editor) => {
28
- if (editor && editor.focus) {
29
- try {
30
- editor.focus();
31
- } catch (err) {
32
- console.warn('Error focusing editor:', err);
48
+ let retryCount = 0;
49
+ const retryFocus = (ref) => {
50
+ if (ref.current) {
51
+ ref.current.focus();
52
+ } else {
53
+ if (retryCount < 30) {
54
+ setTimeout(() => {
55
+ retryCount = retryCount + 1;
56
+ retryFocus(ref);
57
+ }, 100);
33
58
  }
34
59
  }
35
60
  };
36
61
 
37
62
  export const RawEditor = (props: RichTextType) => {
38
- const monacoInstance = useCustomMonaco();
39
- const editorRef = React.useRef<monaco.editor.IStandaloneCodeEditor | null>(
40
- null
41
- );
63
+ const monaco = useMonaco() as Monaco;
64
+ const monacoEditorRef =
65
+ React.useRef<monaco.editor.IStandaloneCodeEditor>(null);
42
66
  const [height, setHeight] = React.useState(100);
43
67
  const id = React.useMemo(() => uuid(), []);
44
68
  const field = props.field;
45
-
46
- // Get initial value safely
47
69
  const inputValue = React.useMemo(() => {
48
- try {
49
- // @ts-ignore no access to the rich-text type from this package
50
- const res = stringifyMDX(props.input.value, field, (value) => value);
51
- return typeof props.input.value === 'string' ? props.input.value : res;
52
- } catch (err) {
53
- console.error('Error stringifying MDX:', err);
54
- return '';
55
- }
56
- }, []); // Empty dependency array to only run once
57
-
70
+ // @ts-ignore no access to the rich-text type from this package
71
+ const res = stringifyMDX(props.input.value, field, (value) => value);
72
+ return typeof props.input.value === 'string' ? props.input.value : res;
73
+ }, []);
58
74
  const [value, setValue] = React.useState(inputValue);
59
75
  const [error, setError] = React.useState<InvalidMarkdownElement>(null);
60
76
 
61
77
  const debouncedValue = useDebounce(value, 500);
62
78
 
63
- // Update parsed MDX when value changes
64
79
  React.useEffect(() => {
65
- let isMounted = true;
66
-
67
- try {
68
- // @ts-ignore no access to the rich-text type from this package
69
- const parsedValue = parseMDX(debouncedValue, field, (value) => value);
70
-
71
- if (!isMounted) return;
72
-
73
- if (
74
- parsedValue.children[0] &&
75
- parsedValue.children[0].type === 'invalid_markdown'
76
- ) {
77
- setError(parsedValue.children[0]);
78
- } else {
79
- setError(null);
80
- props.input.onChange(parsedValue);
81
- }
82
- } catch (err) {
83
- console.error('Error parsing MDX:', err);
80
+ // @ts-ignore no access to the rich-text type from this package
81
+ const parsedValue = parseMDX(value, field, (value) => value);
82
+ if (
83
+ parsedValue.children[0] &&
84
+ parsedValue.children[0].type === 'invalid_markdown'
85
+ ) {
86
+ const invalidMarkdown = parsedValue.children[0];
87
+ setError(invalidMarkdown);
88
+ } else {
89
+ setError(null);
84
90
  }
91
+ props.input.onChange(parsedValue);
92
+ }, [JSON.stringify(debouncedValue)]);
85
93
 
86
- return () => {
87
- isMounted = false;
88
- };
89
- }, [debouncedValue, field]); // Only dependency should be the debounced value and field
90
-
91
- // Handle error markers in editor
92
94
  React.useEffect(() => {
93
- if (!monacoInstance || !editorRef.current) return;
94
-
95
- try {
96
- const model = editorRef.current.getModel();
97
- if (!model) return;
98
-
95
+ if (monacoEditorRef.current) {
99
96
  if (error) {
100
97
  const errorMessage = buildError(error);
101
-
102
- // Make sure all position properties are numbers (not undefined)
103
- const markerData = {
104
- message: errorMessage.message,
105
- severity: monacoInstance.MarkerSeverity?.Error || 8,
106
- startLineNumber: errorMessage.position?.startLineNumber || 1,
107
- endLineNumber: errorMessage.position?.endLineNumber || 1,
108
- startColumn: errorMessage.position?.startColumn || 1,
109
- endColumn: errorMessage.position?.endColumn || 1,
110
- };
111
-
112
- monacoInstance.editor.setModelMarkers(model, id, [markerData]);
98
+ monaco.editor.setModelMarkers(monacoEditorRef.current.getModel(), id, [
99
+ {
100
+ ...errorMessage.position,
101
+ message: errorMessage.message,
102
+ severity: 8,
103
+ },
104
+ ]);
113
105
  } else {
114
- monacoInstance.editor.setModelMarkers(model, id, []);
106
+ monaco.editor.setModelMarkers(
107
+ monacoEditorRef.current.getModel(),
108
+ id,
109
+ []
110
+ );
115
111
  }
116
- } catch (err) {
117
- console.error('Error setting model markers:', err);
118
112
  }
119
- }, [error, monacoInstance, id]);
113
+ }, [JSON.stringify(error), monacoEditorRef.current]);
120
114
 
121
- // Configure TypeScript settings once when Monaco loads
122
115
  React.useEffect(() => {
123
- if (!monacoInstance) return;
124
-
125
- try {
126
- monacoInstance.languages.typescript.typescriptDefaults.setEagerModelSync(
127
- true
128
- );
129
- monacoInstance.languages.typescript.typescriptDefaults.setDiagnosticsOptions(
130
- {
131
- noSemanticValidation: true,
132
- noSyntaxValidation: true,
133
- }
134
- );
135
- } catch (err) {
136
- console.error('Error configuring Monaco TypeScript settings:', err);
116
+ if (monaco) {
117
+ monaco.languages.typescript.typescriptDefaults.setEagerModelSync(true);
118
+ monaco.languages.typescript.typescriptDefaults.setDiagnosticsOptions({
119
+ // disable errors
120
+ noSemanticValidation: true,
121
+ noSyntaxValidation: true,
122
+ });
123
+ // TODO: autocomplete suggestions
124
+ // monaco.languages.registerCompletionItemProvider('markdown', {
125
+ // provideCompletionItems: function (model, position) {
126
+ // const word = model.getWordUntilPosition(position)
127
+ // const range = {
128
+ // startLineNumber: position.lineNumber,
129
+ // endLineNumber: position.lineNumber,
130
+ // startColumn: word.startColumn,
131
+ // endColumn: word.endColumn,
132
+ // }
133
+ // return {
134
+ // suggestions: [
135
+ // {
136
+ // label: '<DateTime />',
137
+ // insertText: '<DateTime format="iso" />',
138
+ // kind: 0,
139
+ // range,
140
+ // },
141
+ // ],
142
+ // }
143
+ // },
144
+ // })
137
145
  }
138
- }, [monacoInstance]);
146
+ }, [monaco]);
139
147
 
140
148
  function handleEditorDidMount(
141
- editor: monaco.editor.IStandaloneCodeEditor,
149
+ monacoEditor: monaco.editor.IStandaloneCodeEditor,
142
150
  monaco: Monaco
143
151
  ) {
144
- if (!editor) return;
145
-
146
- try {
147
- editorRef.current = editor;
148
-
149
- // Focus the editor once when mounted
150
- setTimeout(() => editor.focus(), 100);
151
-
152
- // Set up content size listener
153
- editor.onDidContentSizeChange(() => {
154
- const contentHeight = editor.getContentHeight();
155
- setHeight(Math.min(Math.max(100, contentHeight), 1000));
156
- editor.layout();
157
- });
158
- } catch (err) {
159
- console.error('Error in editor mount handler:', err);
160
- }
152
+ monacoEditorRef.current = monacoEditor;
153
+ monacoEditor.onDidContentSizeChange(() => {
154
+ // FIXME: if the window is too tall the performance degrades, come up with a nice
155
+ // balance between the two
156
+ setHeight(Math.min(Math.max(100, monacoEditor.getContentHeight()), 1000));
157
+ monacoEditor.layout();
158
+ });
161
159
  }
162
160
 
163
161
  return (
@@ -172,6 +170,9 @@ export const RawEditor = (props: RichTextType) => {
172
170
  <MonacoEditor
173
171
  path={id}
174
172
  onMount={handleEditorDidMount}
173
+ // Setting a custom theme is kind of buggy because it doesn't get defined until monaco has mounted.
174
+ // So we end up with the default (light) theme in some scenarios. Seems like a race condition.
175
+ // theme="vs-dark"
175
176
  options={{
176
177
  scrollBeyondLastLine: false,
177
178
  tabSize: 2,
@@ -189,19 +190,23 @@ export const RawEditor = (props: RichTextType) => {
189
190
  lineNumbersMinChars: 2,
190
191
  formatOnType: true,
191
192
  fixedOverflowWidgets: true,
193
+ // Takes too much horizontal space for iframe
192
194
  folding: false,
193
195
  renderLineHighlight: 'none',
194
196
  scrollbar: {
195
197
  verticalScrollbarSize: 4,
196
198
  horizontalScrollbarSize: 4,
199
+ // https://github.com/microsoft/monaco-editor/issues/2007#issuecomment-644425664
197
200
  alwaysConsumeMouseWheel: false,
198
201
  },
199
202
  }}
200
- language='markdown'
203
+ language={'markdown'}
201
204
  value={value}
202
- onChange={(newValue) => {
203
- if (newValue !== undefined) {
204
- setValue(newValue);
205
+ onChange={(value) => {
206
+ try {
207
+ setValue(value);
208
+ } catch (e) {
209
+ console.log('error', e);
205
210
  }
206
211
  }}
207
212
  />
@@ -1,53 +0,0 @@
1
- import { loader } from '@monaco-editor/react';
2
- import * as monaco from 'monaco-editor';
3
- // hooks/useCustomMonaco.ts
4
- import { useEffect, useRef, useState } from 'react';
5
-
6
- export function useCustomMonaco() {
7
- const [monacoInstance, setMonacoInstance] = useState<typeof monaco | null>(
8
- null
9
- );
10
- const mountedRef = useRef(true);
11
- const loaderRef = useRef<any>(null);
12
-
13
- useEffect(() => {
14
- const instance = loader.__getMonacoInstance();
15
-
16
- if (instance) {
17
- setMonacoInstance(instance);
18
- return;
19
- }
20
-
21
- if (!loaderRef.current) {
22
- loader.config({
23
- 'vs/nls': { availableLanguages: {} },
24
- });
25
-
26
- try {
27
- loaderRef.current = loader.init();
28
-
29
- loaderRef.current
30
- .then((monacoApi: typeof monaco) => {
31
- if (mountedRef.current) {
32
- setMonacoInstance(monacoApi);
33
- }
34
- })
35
- .catch((error: any) => {
36
- if (mountedRef.current && error.type !== 'cancelation') {
37
- console.error('Monaco initialization error:', error);
38
- }
39
- });
40
- } catch (err) {
41
- console.error('Failed to initialize Monaco:', err);
42
- }
43
- }
44
-
45
- return () => {
46
- mountedRef.current = false;
47
- };
48
- }, []);
49
-
50
- return monacoInstance;
51
- }
52
-
53
- export default useCustomMonaco;