handlePaneChange('left', updated, previous, status)
@@ -398,7 +406,7 @@ export function JsonFormatterLayout() {
{renderPaneToolbar('right')}
handlePaneChange('right', updated, previous, status)
diff --git a/apps/web/src/components/json-formatter/vanilla-editor.tsx b/apps/web/src/components/json-formatter/vanilla-editor.tsx
index ab25a574..d4a821d1 100644
--- a/apps/web/src/components/json-formatter/vanilla-editor.tsx
+++ b/apps/web/src/components/json-formatter/vanilla-editor.tsx
@@ -1,7 +1,7 @@
'use client'
import { useEffect, useRef, useState } from 'react'
-import { JSONEditor, JSONEditorPropsOptional } from 'vanilla-jsoneditor'
+import type { JSONEditorPropsOptional } from 'vanilla-jsoneditor'
import { useTheme } from 'next-themes'
import { cn } from '@/lib/utils'
import 'vanilla-jsoneditor/themes/jse-theme-dark.css'
@@ -12,7 +12,9 @@ interface VanillaEditorProps extends JSONEditorPropsOptional {
export function VanillaEditor({ className = '', ...props }: VanillaEditorProps) {
const refContainer = useRef(null)
- const refEditor = useRef | null>(null)
+ const refEditor = useRef | null>(null)
+ const propsRef = useRef(props)
+ propsRef.current = props
const { resolvedTheme } = useTheme()
const [mounted, setMounted] = useState(false)
@@ -23,22 +25,24 @@ export function VanillaEditor({ className = '', ...props }: VanillaEditorProps)
const isDarkTheme = mounted && resolvedTheme === 'dark'
useEffect(() => {
- // create editor
- if (refContainer.current && !refEditor.current) {
+ let disposed = false
+ // vanilla-jsoneditor is ~485KB; load it on mount so the tool page paints without it.
+ void import('vanilla-jsoneditor').then(({ JSONEditor }) => {
+ if (disposed || !refContainer.current || refEditor.current) return
refEditor.current = JSONEditor({
target: refContainer.current,
- props,
+ props: propsRef.current,
})
- }
+ })
return () => {
// destroy editor
+ disposed = true
if (refEditor.current) {
refEditor.current.destroy()
refEditor.current = null
}
}
- // eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
// update props
diff --git a/apps/web/src/components/sql-formatter/sql-formatter-layout.tsx b/apps/web/src/components/sql-formatter/sql-formatter-layout.tsx
index 3df7a03a..c342cf26 100644
--- a/apps/web/src/components/sql-formatter/sql-formatter-layout.tsx
+++ b/apps/web/src/components/sql-formatter/sql-formatter-layout.tsx
@@ -1,6 +1,6 @@
'use client';
-import { format, type KeywordCase, type SqlLanguage } from 'sql-formatter';
+import type { KeywordCase, SqlLanguage } from 'sql-formatter';
import { useCallback, useState } from 'react';
import { useCopyToClipboard } from '@/hooks/use-copy-to-clipboard';
import { Button } from '@/components/ui/button';
@@ -51,7 +51,7 @@ export function SqlFormatterLayout() {
const [error, setError] = useState('');
const { isCopied: copied, copyToClipboard, reset: resetCopied } = useCopyToClipboard();
- const runFormat = useCallback(() => {
+ const runFormat = useCallback(async () => {
setError('');
resetCopied();
if (isMobile) setMobileTab('output');
@@ -67,6 +67,8 @@ export function SqlFormatterLayout() {
}
try {
const tw = Math.min(8, Math.max(1, parseInt(tabWidth, 10) || 2));
+ // sql-formatter is ~278KB; load on first format so the tool page paints without it.
+ const { format } = await import('sql-formatter');
setOutput(
format(q, {
language: dialect,
diff --git a/apps/web/src/components/svg-optimizer/svg-optimizer-layout.tsx b/apps/web/src/components/svg-optimizer/svg-optimizer-layout.tsx
index e236f1e8..ae8cd2d8 100644
--- a/apps/web/src/components/svg-optimizer/svg-optimizer-layout.tsx
+++ b/apps/web/src/components/svg-optimizer/svg-optimizer-layout.tsx
@@ -11,7 +11,8 @@ import { Textarea } from '@/components/ui/textarea'
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'
import { Check, Copy, Download, Trash2, Sparkles } from 'lucide-react'
import { cn } from '@/lib/utils'
-import { optimizeSvgMarkup, utf8ByteLength } from '@/lib/svg-optimize'
+import { utf8ByteLength } from '@/lib/svg-optimize'
+import { useSvgOptimizeWorker } from '@/hooks/use-svg-optimize-worker'
const SAMPLE_SVG = `