@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-
|
|
3
|
+
## 0.0.0-6a83617-20250426154716
|
|
4
4
|
|
|
5
5
|
### Patch Changes
|
|
6
6
|
|
|
7
|
-
-
|
|
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-
|
|
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.
|
|
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-
|
|
26
|
+
"tinacms": "0.0.0-6a83617-20250426154716"
|
|
29
27
|
}
|
|
30
28
|
}
|
|
@@ -1,17 +1,29 @@
|
|
|
1
|
-
|
|
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 {
|
|
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
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
|
39
|
-
const
|
|
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
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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 (
|
|
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
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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
|
-
|
|
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,
|
|
113
|
+
}, [JSON.stringify(error), monacoEditorRef.current]);
|
|
120
114
|
|
|
121
|
-
// Configure TypeScript settings once when Monaco loads
|
|
122
115
|
React.useEffect(() => {
|
|
123
|
-
if (
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
true
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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
|
-
}, [
|
|
146
|
+
}, [monaco]);
|
|
139
147
|
|
|
140
148
|
function handleEditorDidMount(
|
|
141
|
-
|
|
149
|
+
monacoEditor: monaco.editor.IStandaloneCodeEditor,
|
|
142
150
|
monaco: Monaco
|
|
143
151
|
) {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
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={(
|
|
203
|
-
|
|
204
|
-
setValue(
|
|
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;
|