From faf64a0ff9d5528076264a5c50cec7453513deec Mon Sep 17 00:00:00 2001 From: Pedro Carreno <34664891+Pkcarreno@users.noreply.github.com> Date: Fri, 25 Oct 2024 19:07:06 -0400 Subject: [PATCH 1/4] fix(engine): increase memory limit & max stack size in quickjs runtime --- src/features/editor/utils/engine/runtime.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/features/editor/utils/engine/runtime.ts b/src/features/editor/utils/engine/runtime.ts index 9d4d468..31c92b3 100644 --- a/src/features/editor/utils/engine/runtime.ts +++ b/src/features/editor/utils/engine/runtime.ts @@ -92,8 +92,8 @@ export const executeCode: ( ctxRef = ctx; let interruptCycles = 0; - ctx.runtime.setMemoryLimit(1024 * 640); - ctx.runtime.setMaxStackSize(1024 * 320); + ctx.runtime.setMemoryLimit(1024 * 1024); + ctx.runtime.setMaxStackSize(1024 * 1024); ctx.runtime.setInterruptHandler(() => { DEBUG('interrupt handler triggered. time: ', interruptCycles); return ++interruptCycles > loopThreshold; From 7f5d322747187391dc1c860949bfbcae32a979bd Mon Sep 17 00:00:00 2001 From: Pedro Carreno <34664891+Pkcarreno@users.noreply.github.com> Date: Fri, 1 Nov 2024 19:49:37 -0400 Subject: [PATCH 2/4] fix: improve clickability of untrusted mode sign --- src/features/editor/components/header/untrusted-mode-sign.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/features/editor/components/header/untrusted-mode-sign.tsx b/src/features/editor/components/header/untrusted-mode-sign.tsx index 4c40ae7..81a8899 100644 --- a/src/features/editor/components/header/untrusted-mode-sign.tsx +++ b/src/features/editor/components/header/untrusted-mode-sign.tsx @@ -10,7 +10,7 @@ export const UntrustedModeSign = () => {
setUntrustedDialogOpen(true)} > Editor in Untrusted Mode From 0b7d36dc384358984de9ded69916aa1cccd6274a Mon Sep 17 00:00:00 2001 From: Pedro Carreno <34664891+Pkcarreno@users.noreply.github.com> Date: Fri, 1 Nov 2024 22:59:53 -0400 Subject: [PATCH 3/4] fix(meta): improve metatags --- index.html | 22 +++++++++++++++++++++- vite.config.ts | 2 +- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/index.html b/index.html index f0e64d1..1459491 100644 --- a/index.html +++ b/index.html @@ -3,7 +3,27 @@ - JSoD - JS on Demand + + JSoD + + + + + + + + + + diff --git a/vite.config.ts b/vite.config.ts index 27d3d04..53fbc69 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -66,7 +66,7 @@ export default defineConfig(({ mode }) => { registerType: 'prompt', injectRegister: 'auto', manifest: { - name: 'JS on Demand', + name: 'JSOD', short_name: 'JSOD', id: 'com.pkcarreno.jsod', start_url: `${process.env.BASE_URL}/`, From 318eb2c7b713ae9ca1d3ea1296617f0c77511453 Mon Sep 17 00:00:00 2001 From: Pedro Carreno <34664891+Pkcarreno@users.noreply.github.com> Date: Sat, 2 Nov 2024 01:15:21 -0400 Subject: [PATCH 4/4] feat: add helmet & script title and description --- package.json | 4 + pnpm-lock.yaml | 53 ++++++ src/app.tsx | 16 +- src/components/ui/form.tsx | 171 ++++++++++++++++++ src/components/ui/textarea.tsx | 23 +++ .../editor/components/header/index.tsx | 17 +- .../editor/components/header/info/index.tsx | 67 +++++++ .../components/header/info/info-form.tsx | 85 +++++++++ .../components/header/untrusted-mode-sign.tsx | 6 +- src/features/editor/providers/index.tsx | 17 +- .../editor/providers/meta-provider.tsx | 20 ++ .../editor/stores/editor/code-store.ts | 15 +- src/features/not-found/index.tsx | 22 ++- 13 files changed, 482 insertions(+), 34 deletions(-) create mode 100644 src/components/ui/form.tsx create mode 100644 src/components/ui/textarea.tsx create mode 100644 src/features/editor/components/header/info/index.tsx create mode 100644 src/features/editor/components/header/info/info-form.tsx create mode 100644 src/features/editor/providers/meta-provider.tsx diff --git a/package.json b/package.json index 92446b8..878498f 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "@codemirror/lint": "^6.8.1", "@fontsource/ibm-plex-mono": "^5.1.0", "@fontsource/ibm-plex-sans": "^5.1.0", + "@hookform/resolvers": "^3.9.1", "@jitl/quickjs-ng-wasmfile-release-sync": "0.31.0", "@lezer/highlight": "^1.2.1", "@radix-ui/react-accordion": "^1.2.1", @@ -60,6 +61,8 @@ "quickjs-emscripten-sync": "^1.5.2", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-helmet-async": "^2.0.5", + "react-hook-form": "^7.53.1", "react-hotkeys-hook": "^4.5.1", "react-resizable-panels": "^2.1.4", "react-virtuoso": "^4.12.0", @@ -67,6 +70,7 @@ "sonner": "^1.5.0", "tailwind-merge": "^2.5.4", "wouter": "^3.3.5", + "zod": "^3.23.8", "zustand": "^5.0.0" }, "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e5028ac..87a35f7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,6 +20,9 @@ importers: '@fontsource/ibm-plex-sans': specifier: ^5.1.0 version: 5.1.0 + '@hookform/resolvers': + specifier: ^3.9.1 + version: 3.9.1(react-hook-form@7.53.1(react@18.3.1)) '@jitl/quickjs-ng-wasmfile-release-sync': specifier: 0.31.0 version: 0.31.0 @@ -128,6 +131,12 @@ importers: react-dom: specifier: ^18.2.0 version: 18.3.1(react@18.3.1) + react-helmet-async: + specifier: ^2.0.5 + version: 2.0.5(react@18.3.1) + react-hook-form: + specifier: ^7.53.1 + version: 7.53.1(react@18.3.1) react-hotkeys-hook: specifier: ^4.5.1 version: 4.5.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -149,6 +158,9 @@ importers: wouter: specifier: ^3.3.5 version: 3.3.5(react@18.3.1) + zod: + specifier: ^3.23.8 + version: 3.23.8 zustand: specifier: ^5.0.0 version: 5.0.0(@types/react@18.3.11)(react@18.3.1)(use-sync-external-store@1.2.2(react@18.3.1)) @@ -1144,6 +1156,11 @@ packages: '@fontsource/ibm-plex-sans@5.1.0': resolution: {integrity: sha512-v2aFHGh33ogG+At6dVNUCX6vWlNAhQ6STWj5WrBKPxVWX1SsAnHNq8sXQBa7WHEt29Irmozuk7GTp6GzFlpwdQ==} + '@hookform/resolvers@3.9.1': + resolution: {integrity: sha512-ud2HqmGBM0P0IABqoskKWI6PEf6ZDDBZkFqe2Vnl+mTHCEHzr3ISjjZyCwTjC/qpL25JC9aIDkloQejvMeq0ug==} + peerDependencies: + react-hook-form: ^7.0.0 + '@humanfs/core@0.19.0': resolution: {integrity: sha512-2cbWIHbZVEweE853g8jymffCA+NCMiuqeECeBBLm8dg2oFdjuGJhgN4UAbI+6v0CKbbhvtXA4qV8YR5Ji86nmw==} engines: {node: '>=18.18.0'} @@ -3712,6 +3729,20 @@ packages: peerDependencies: react: ^18.3.1 + react-fast-compare@3.2.2: + resolution: {integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==} + + react-helmet-async@2.0.5: + resolution: {integrity: sha512-rYUYHeus+i27MvFE+Jaa4WsyBKGkL6qVgbJvSBoX8mbsWoABJXdEO0bZyi0F6i+4f0NuIb8AvqPMj3iXFHkMwg==} + peerDependencies: + react: ^16.6.0 || ^17.0.0 || ^18.0.0 + + react-hook-form@7.53.1: + resolution: {integrity: sha512-6aiQeBda4zjcuaugWvim9WsGqisoUk+etmFEsSUMm451/Ic8L/UAb7sRtMj3V+Hdzm6mMjU1VhiSzYUZeBm0Vg==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^16.8.0 || ^17 || ^18 || ^19 + react-hotkeys-hook@4.5.1: resolution: {integrity: sha512-scAEJOh3Irm0g95NIn6+tQVf/OICCjsQsC9NBHfQws/Vxw4sfq1tDQut5fhTEvPraXhu/sHxRd9lOtxzyYuNAg==} peerDependencies: @@ -3917,6 +3948,9 @@ packages: resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} engines: {node: '>= 0.4'} + shallowequal@1.1.0: + resolution: {integrity: sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==} + shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -5797,6 +5831,10 @@ snapshots: '@fontsource/ibm-plex-sans@5.1.0': {} + '@hookform/resolvers@3.9.1(react-hook-form@7.53.1(react@18.3.1))': + dependencies: + react-hook-form: 7.53.1(react@18.3.1) + '@humanfs/core@0.19.0': {} '@humanfs/node@0.16.5': @@ -8487,6 +8525,19 @@ snapshots: react: 18.3.1 scheduler: 0.23.2 + react-fast-compare@3.2.2: {} + + react-helmet-async@2.0.5(react@18.3.1): + dependencies: + invariant: 2.2.4 + react: 18.3.1 + react-fast-compare: 3.2.2 + shallowequal: 1.1.0 + + react-hook-form@7.53.1(react@18.3.1): + dependencies: + react: 18.3.1 + react-hotkeys-hook@4.5.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: react: 18.3.1 @@ -8711,6 +8762,8 @@ snapshots: functions-have-names: 1.2.3 has-property-descriptors: 1.0.2 + shallowequal@1.1.0: {} + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 diff --git a/src/app.tsx b/src/app.tsx index 67ae4a4..e038dc9 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -1,3 +1,5 @@ +import { HelmetProvider } from 'react-helmet-async'; + import { Toaster } from '@/components/ui/sonner'; import { ThemeProvider } from '@/providers/theme-provider'; import Router from '@/routes'; @@ -5,12 +7,14 @@ import Router from '@/routes'; import { PromptProvider } from './providers/prompt-provider'; const App = () => ( - - - - - - + + + + + + + + ); export default App; diff --git a/src/components/ui/form.tsx b/src/components/ui/form.tsx new file mode 100644 index 0000000..69cc7d5 --- /dev/null +++ b/src/components/ui/form.tsx @@ -0,0 +1,171 @@ +import type * as LabelPrimitive from '@radix-ui/react-label'; +import { Slot } from '@radix-ui/react-slot'; +import * as React from 'react'; +import type { ControllerProps, FieldPath, FieldValues } from 'react-hook-form'; +import { Controller, FormProvider, useFormContext } from 'react-hook-form'; + +import { Label } from '@/components/ui/label'; +import { cn } from '@/lib/utils'; + +const Form = FormProvider; + +type FormFieldContextValue< + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath = FieldPath, +> = { + name: TName; +}; + +const FormFieldContext = React.createContext( + {} as FormFieldContextValue, +); + +const FormField = < + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath = FieldPath, +>({ + ...props +}: ControllerProps) => { + return ( + + + + ); +}; + +const useFormField = () => { + const fieldContext = React.useContext(FormFieldContext); + const itemContext = React.useContext(FormItemContext); + const { getFieldState, formState } = useFormContext(); + + const fieldState = getFieldState(fieldContext.name, formState); + + if (!fieldContext) { + throw new Error('useFormField should be used within '); + } + + const { id } = itemContext; + + return { + id, + name: fieldContext.name, + formItemId: `${id}-form-item`, + formDescriptionId: `${id}-form-item-description`, + formMessageId: `${id}-form-item-message`, + ...fieldState, + }; +}; + +type FormItemContextValue = { + id: string; +}; + +const FormItemContext = React.createContext( + {} as FormItemContextValue, +); + +const FormItem = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => { + const id = React.useId(); + + return ( + +
+ + ); +}); +FormItem.displayName = 'FormItem'; + +const FormLabel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => { + const { error, formItemId } = useFormField(); + + return ( +