Open vadimkantorov opened 3 months ago
This is more of a vite question than anything else. Initialization of the editor is synchronous, no promises are involved unless one of the plug-ins is doing something async (but there isn't a general way to determine that).
I'll try to come up with such a config (or patch to the default config), but as long as it can fulfill (1), it can become also a Lexical distribution option and feature (e.g. bundled core and all Lexical plugins available in the lexical repo), similar to Quill
So I modified the vite.prod.config.js
to be like (full example in https://github.com/vadimkantorov/lexical-playground-only):
rollupOptions: {
input: {
main: new URL('./index.html', import.meta.url).pathname,
},
output: {
format: 'es',
manualChunks: false,
inlineDynamicImports: true,
entryFileNames: '[name].js', // currently does not work for the legacy bundle
assetFileNames: '[name].[ext]', // currently does not work for images
},
},
This produces main.js
, main.css
, landscape.jpg
, yellow-flower.jpg
, cat-typing.gif
and a bunch of svg/woff/woff2/ttf files.
To embed the svg/woff/woff2/ttf into the CSS I wrote a following embedder:
# dataurifycss.py
# python dataurifycss.py assets/main.css
import os, sys, re, base64
mime = {'.svg': 'image/svg+xml', '.jpg': 'image/jpeg', '.png': 'image/png', '.gif': 'image/gif', '.woff2': 'font/woff2', '.woff': 'font/woff', '.ttf': 'font/ttf'}
with open(sys.argv[1], 'w') as f: css = f.read()
root_dir = os.path.dirname(sys.argv[1])
seen = set()
def dataurify(s):
o = s.group(0)
g = s.group(1).strip("'" + '"')
e = os.path.splitext(g)[-1]
p = os.path.join(root_dir, g.lstrip('/')) if g.startswith('/') else g
if g.startswith('data:'):
return g
elif e in mime:
t = 'url(data:{mime};base64,{encoded})'.format(mime = mime[e], encoded = base64.b64encode(open(p, 'rb').read()).decode())
seen.add(p)
return t
else:
return o
pattern = 'url\((.+?)\)'
res = re.sub(pattern, dataurify, css)
for p in seen: os.remove(p)
with open(sys.argv[1], 'w') as f: f.write(res)
# dataurifyjs.py
# python dataurifyjs.py assets/main.js
import os, sys, re, base64
mime = {'.jpg': 'image/jpeg', '.png': 'image/png', '.gif': 'image/gif'}
with open(sys.argv[1], 'r') as f: css = f.read()
root_dir = os.path.dirname(sys.argv[1])
seen = set()
def dataurify(s):
o = s.group(0)
g = s.group(1).strip("'" + '"')
e = os.path.splitext(g)[-1]
p = os.path.join(root_dir, g.lstrip('/')) if g.startswith('/') else g
if g.startswith('data:'):
return g
elif e in mime:
t = '"data:{mime};base64,{encoded}"'.format(mime = mime[e], encoded = base64.b64encode(open(p, 'rb').read()).decode())
seen.add(p)
return t
else:
return o
pattern = '"(/.+?\.(' + '|'.join(ext.lstrip('.') for ext in mime) + '))"'
res = re.sub(pattern, dataurify, css)
for p in seen: os.remove(p)
with open(sys.argv[1], 'w') as f: f.write(res)
The JPG and GIF files can be embedded in a similar way with dataurifyjs.py
, which gives self-contained main.js
and main.css
files
So I would propose:
1) to somehow fix the usage of JPG/GIF files to be also in CSS (currently they are used in main.js as var landscapeImage = "/landscape.jpg";
/var yellowFlowerImage = "/yellow-flower.jpg";
/var catTypingGif = "/cat-typing.gif";
) and have an option/explainer to remove these examples files. They are quite heavy and increase the size of the JS if embedded.
2) and maybe include in README or in repo/examples a kind of vite.prod.config.js to have such a target that produces only a main.js/main.css with all images/fonts embedded. If vite doesn't have such image/font embedder, maybe this can also be done with a JavaScript snippet similar to my Python snippets - and then this snippet can be added in vite.prod.config.js
Vite does inlining when it makes sense (<4kb), at least in situations when you are using it for resolution of the asset (imported from js or using relative paths in css). You can configure it to behave differently https://vitejs.dev/config/build-options.html#build-assetsinlinelimit
I don't think it's a terribly common use case to want to inline everything into a pair of CSS and JS files. It's not very clear what the use case would be where you could use two files, but not a directory with more than two files.
If everything is all bundled up, how would you propose that the editor remains extensible? Lexical is an extensible framework for building text editors, not a specific text editor. It's unclear what you would do with an "drop-in" playground editor that is built for demonstration purposes and doesn't really have infrastructure for serialization/deserialization or configuration.
I think, a single or a pair of CSS/JS with some preset amount of plugins make a great starting point for non-webdevs (like machine learning devs) who just need a markdown/rtf editor as part of a small hack. In this usecase, Lexical + plugins should just provide their APIs, but adding of a new plugin at runtime is a non-goal
I think it's fair to say that when extensibility becomes needed, that user can invest some time in learning react/npm/vite etc. So not allowing extensibility in the two-files-distribution is okay and allows to try out and already use this modern editor with good markdown support out of the box.
What would be the goals of getting this into the Lexical distribution, rather than having a standalone project with a very clear purpose? I think that even if Lexical provided this example, it would not be very easy to find or use, because the vast majority of Lexical's resources are dedicated to the more general use case.
Perhaps a purpose-built editor that depends on Lexical, but not part of the Lexical distribution, currently makes more sense for your purposes? For example, https://mdxeditor.dev/ is based on Lexical and its usage and documentation are all very focused on the markdown editor use case, and because it takes a stance on framework/asset/CSS management it can include UI components much more easily than the Lexical distribution.
I'm not saying that this should be in the main Lexical repo per se, but having a release variant like e.g. tarball with lexical-batteriesincluded-rtf-editor.js
with lexical-batteriesincluded-rtf-editor.css
would be very nice for small hacks/tryouts for non-web developers (for who RTF/Markdown functionality in existing Lexical Playground is sufficient), for who grokking react/npm/rollup/es/umd etc is a considerable investment. This usecase would enable hacks in a plain html file locally, trying things out even without a web server
I agree that placing sth like this "not part of Lexical distribution" is fine as well, but ideally I'd like to have this auto-released along with Lexical releases, given how actively Lexical is developed. Such a package can be extremely is minimal (e.g. making Playground's CSS not style global elements like <header>
and providing only a few API methods like getText/setText/getMarkdown/setMarkdown) and thus not require any separate testing (apart from Lexical's) or other people's regular effort to follow Lexical's release cadence.
https://mdxeditor.dev seems exteremely awesome and maybe I'll use it as well. I hope some its plugins/inmprovements related to Markdown support be upstreamed as well
Basically my ask was providing some automated releases with limited functionality for non-web developers (like myself) who do not need extensibility, but rather would appreciate an option with very simple <script src="">
import for having a fully-functional basic RTF editor supporting Markdown, non requiring to learn/install react to have something minimally working in the browser. If such a hack evolves and such a user needs more flexibility/UI complexity, surely they would migrate to a full React-based offering.
Upgrading to release v0.16.0 broke my editor: (not minified) main.js increased in size from 12Mb to 26Mb (for producing a single main.js
output I'm doing in rollupOptions
: output: { format: 'iife',/*'es',*/ compact: false, manualChunks: false, inlineDynamicImports: true, entryFileNames: '[name].js', /* currently does not work for the legacy bundle*/ assetFileNames: '[name].[ext]',}
in packages/lexical-playground/vite.prod.config.ts
Also, ActionsPlugin now at loading produces the error (at release 0.12.5 there was no such error):
main.js:9173 Error: Missing FlashMessageContext
at useFlashMessageContext (main.js:33801:13)
at useFlashMessage (main.js:33806:12)
at ActionsPlugin (main.js:63984:30)
at Nh (main.js:8630:10)
at Vk (main.js:11804:14)
at Uk (main.js:11471:14)
at Tk (main.js:11464:7)
at Ik (main.js:11447:9)
at Nk (main.js:11163:10)
at Gk (main.js:11094:60)
(I'm doing minify: false
but I still could not figure out how to disable obfuscation/minification completely)
const useFlashMessageContext = ()=>{
const ctx = reactExports.useContext(Context$1);
if (!ctx) {
throw new Error("Missing FlashMessageContext");
}
return ctx;
}
;
Would you have a suggestion on how to fix this FlashMessageContext thing? Thanks!
Hard to say without being able to see the project but doubling of size makes it seem like you're including multiple versions of dependencies which will cause all sorts of problems. ActionsPlugin isn't something that's exported from any public lexical package so whatever broke there is in your project.
@etrepum My project is a simplified version of App.tsx from playground. I just fixed this problem by adding import {FlashMessageContext} from './context/FlashMessageContext';
and inserting a FlashMessageContext inside a LexicalComposer. I think in recent version ActionPlugin refuses to load if it cannot find an existing FlashMessageContext
Coming back to getting a single main.js
file, currently when using output: { format: 'iife',/*'es',*/ compact: false, manualChunks: false, inlineDynamicImports: true, entryFileNames: '[name].js', /* currently does not work for the legacy bundle*/ assetFileNames: '[name].[ext]', }
produces the following build/assets
directory listing:
KaTeX_AMS-Regular.ttf
KaTeX_AMS-Regular.woff
KaTeX_AMS-Regular.woff2
KaTeX_Caligraphic-Bold.ttf
KaTeX_Caligraphic-Bold.woff
KaTeX_Caligraphic-Bold.woff2
KaTeX_Caligraphic-Regular.ttf
KaTeX_Caligraphic-Regular.woff
KaTeX_Caligraphic-Regular.woff2
KaTeX_Fraktur-Bold.ttf
KaTeX_Fraktur-Bold.woff
KaTeX_Fraktur-Bold.woff2
KaTeX_Fraktur-Regular.ttf
KaTeX_Fraktur-Regular.woff
KaTeX_Fraktur-Regular.woff2
KaTeX_Main-Bold.ttf
KaTeX_Main-Bold.woff
KaTeX_Main-Bold.woff2
KaTeX_Main-BoldItalic.ttf
KaTeX_Main-BoldItalic.woff
KaTeX_Main-BoldItalic.woff2
KaTeX_Main-Italic.ttf
KaTeX_Main-Italic.woff
KaTeX_Main-Italic.woff2
KaTeX_Main-Regular.ttf
KaTeX_Main-Regular.woff
KaTeX_Main-Regular.woff2
KaTeX_Math-BoldItalic.ttf
KaTeX_Math-BoldItalic.woff
KaTeX_Math-BoldItalic.woff2
KaTeX_Math-Italic.ttf
KaTeX_Math-Italic.woff
KaTeX_Math-Italic.woff2
KaTeX_SansSerif-Bold.ttf
KaTeX_SansSerif-Bold.woff
KaTeX_SansSerif-Bold.woff2
KaTeX_SansSerif-Italic.ttf
KaTeX_SansSerif-Italic.woff
KaTeX_SansSerif-Italic.woff2
KaTeX_SansSerif-Regular.ttf
KaTeX_SansSerif-Regular.woff
KaTeX_SansSerif-Regular.woff2
KaTeX_Script-Regular.ttf
KaTeX_Script-Regular.woff
KaTeX_Script-Regular.woff2
KaTeX_Size1-Regular.ttf
KaTeX_Size1-Regular.woff
KaTeX_Size1-Regular.woff2
KaTeX_Size2-Regular.ttf
KaTeX_Size2-Regular.woff
KaTeX_Size2-Regular.woff2
KaTeX_Size3-Regular.ttf
KaTeX_Size3-Regular.woff
KaTeX_Size4-Regular.ttf
KaTeX_Size4-Regular.woff
KaTeX_Size4-Regular.woff2
KaTeX_Typewriter-Regular.ttf
KaTeX_Typewriter-Regular.woff
KaTeX_Typewriter-Regular.woff2
apple-touch-icon.png
cat-typing.gif
esm/index.html
esm/index.mjs
esm/prepopulatedRichText.mjs
esm/styles.css
favicon-16x16.png
favicon-32x32.png
favicon.ico
index.html
landscape.jpg
main.js
yellow-flower.jpg
All svg menu images in 0.16.0 are now embedded in main.js by default (was not the case in 0.12.5)
Could you recommend any options for rollup
/vite
to make embed these KaTeX fonts into main.js
?
Also, somehow unminified main.js
size (practically it's just lexical-playground) rose from 8Mb to 24Mb when updated from release 0.12.5 to 0.16.0.
Or could I now use esm/index.mjs
/esm/styles.css
for this purpose?
Looking at the playground it seems that most of the build size comes from excalidraw
$ find packages/lexical-playground/build -name "*.js"|xargs du -hc
4.0K packages/lexical-playground/build/assets/ImageResizer-BOglis69.js
4.0K packages/lexical-playground/build/assets/EquationComponent-0rKEFys_.js
152K packages/lexical-playground/build/assets/parser-html-C9wyCVVk.js
1.3M packages/lexical-playground/build/assets/main-Cx1fdMhU.js
4.0K packages/lexical-playground/build/assets/PollComponent-CB4jlrjq.js
4.0K packages/lexical-playground/build/assets/LexicalNestedComposer-fvfXQv0w.js
152K packages/lexical-playground/build/assets/parser-postcss-DWk0NhTE.js
4.0K packages/lexical-playground/build/assets/StickyComponent-Cz0oLPVj.js
4.0K packages/lexical-playground/build/assets/ImageComponent-RRTY-P5X.js
432K packages/lexical-playground/build/assets/standalone-CukwHxG0.js
160K packages/lexical-playground/build/assets/parser-markdown-C4fzb1xR.js
20M packages/lexical-playground/build/assets/ExcalidrawComponent-X4FaPXXk.js
308K packages/lexical-playground/build/assets/parser-babel-U9l6AbUH.js
4.0K packages/lexical-playground/build/assets/InlineImageComponent-B4WSWRO-.js
22M total
Beyond that you're really just using code in an unsupported way. The playground is all demo code built with lexical's build infrastructure, it's not designed to be easily extractable into a standalone project.
The esm folder is not relevant to what you're trying to do, it's basically just a demo that lexical can be used without a bundler at all. https://playground.lexical.dev/esm/
I made a cleaner extraction of lexical-playground: https://github.com/vadimkantorov/moncms
It contains packages/lexical-playground
and packages/shared
, without any modifications, from lexical 0.16.0. The only needed patch is to packages/lexical-playground/vite.prod.config.ts
and specified in https://github.com/vadimkantorov/moncms/blob/gh-pages/Makefile#L46-L56
You can find the produced main.js
( 26 megabytes :( ) in https://github.com/vadimkantorov/moncms/raw/gh-pages/assets/main.js . The build dir produced by Makefile in fact contains only main.js
and no other js files that you listed above (probably I don't have them because of rollup's output options I used in Makefile)
The root tsx
file is https://github.com/vadimkantorov/moncms/blob/gh-pages/indexEditorOnly.tsx (modified merger of packages/lexical-playground/src/index.tsx
and packages/lexical-playground/src/Editor.tsx
)
So far problems with this approach:
packages/shared
, but I'll try again to get rid of itmain.js
is extremely large: 26 megabytes (with disabled minification, but still)Attaching the build artifacts (produced by https://github.com/vadimkantorov/moncms/blob/gh-pages/.github/workflows/build.yml): lexicalplaygroundonlyassets (15).zip
I'm developing a single-file HTML app (for ease of distribution and drop-in) which needs a rich-text editor. I'm currently using Lexical for this purpose.
Extracting one of asks from https://github.com/facebook/lexical/issues/5840, for non-webdevs like me, it would be greate to have an example of Vite config bundling Lexical+Plugins+React into:
1) one JS and one CSS file - e.g. for deployment to GitHub pages (the existing vite config produces many CSS/font/image/JS files) 2) the JS/CSS should be fully embeddable in
<script>...</script>
and<style>...</style>
and it should be possible to somehow register a Promise to be triggered when initialization of the editor completes (and ideally also a synchronous initialization should be supported)Am I correct that (1) should already be achievable in https://vitejs.dev/guide/build#library-mode ?