0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-02-10 23:36:14 -05:00

Added AdminX code injection section (#17596)

refs. https://github.com/TryGhost/Product/issues/3688

---------

Co-authored-by: Jono Mingard <reason.koan@gmail.com>
This commit is contained in:
Peter Zimon 2023-08-08 09:14:46 +02:00 committed by GitHub
parent 9aef028c4c
commit cb21763865
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 493 additions and 12 deletions

View file

@ -43,11 +43,13 @@
"prepublishOnly": "yarn build"
},
"dependencies": {
"@codemirror/lang-html": "^6.4.5",
"@dnd-kit/core": "6.0.8",
"@dnd-kit/sortable": "7.0.2",
"@ebay/nice-modal-react": "1.2.10",
"@tanstack/react-query": "4.29.25",
"@tryghost/timezone-data": "0.3.0",
"@uiw/react-codemirror": "^4.21.9",
"clsx": "2.0.0",
"react": "18.2.0",
"react-dom": "18.2.0",

View file

@ -1,8 +1,8 @@
import React from 'react';
import clsx from 'clsx';
export type Tab = {
id: string;
export type Tab<ID = string> = {
id: ID;
title: string;
/**
@ -11,21 +11,21 @@ export type Tab = {
contents?: React.ReactNode;
}
interface TabViewProps {
tabs: Tab[];
onTabChange: (id: string) => void;
selectedTab?: string;
border?:boolean;
interface TabViewProps<ID = string> {
tabs: readonly Tab<ID>[];
onTabChange: (id: ID) => void;
selectedTab?: ID;
border?: boolean;
width?: 'narrow' | 'normal' | 'wide';
}
const TabView: React.FC<TabViewProps> = ({
function TabView<ID extends string = string>({
tabs,
onTabChange,
selectedTab,
border = true,
width = 'normal'
}) => {
}: TabViewProps<ID>) {
if (tabs.length !== 0 && selectedTab === undefined) {
selectedTab = tabs[0].id;
}
@ -35,7 +35,7 @@ const TabView: React.FC<TabViewProps> = ({
}
const handleTabChange = (e: React.MouseEvent<HTMLButtonElement>) => {
const newTab = e.currentTarget.id;
const newTab = e.currentTarget.id as ID;
onTabChange(newTab);
};

View file

@ -0,0 +1,62 @@
import {html} from '@codemirror/lang-html';
import {useArgs} from '@storybook/preview-api';
import type {Meta, StoryObj} from '@storybook/react';
import CodeEditor from './CodeEditor';
const meta = {
title: 'Global / Form / Code Editor',
component: CodeEditor,
tags: ['autodocs'],
// decorators: [(_story: () => ReactNode) => (<div style={{maxWidth: '400px'}}>{_story()}</div>)],
argTypes: {
hint: {
control: 'text'
},
extensions: {
table: {
disable: true
}
}
}
} satisfies Meta<typeof CodeEditor>;
export default meta;
type Story = StoryObj<typeof CodeEditor>;
export const WithValue: Story = {
render: function Component(args) {
const [, updateArgs] = useArgs();
return <CodeEditor {...args} onChange={value => updateArgs({value})} />;
},
args: {
extensions: [html()],
value: '<p>HTML goes here</p>'
}
};
export const WithTitle: Story = {
args: {
extensions: [html()],
title: 'Header code'
}
};
export const WithHint: Story = {
args: {
extensions: [html()],
hint: 'Here\'s some hint'
}
};
export const Error: Story = {
args: {
title: 'Header code',
extensions: [html()],
hint: 'Don\'t use script tags',
value: '<script>alert("bad")</script>',
error: true
}
};

View file

@ -0,0 +1,69 @@
import CodeMirror, {ReactCodeMirrorProps, ReactCodeMirrorRef} from '@uiw/react-codemirror';
import Heading from '../Heading';
import Hint from '../Hint';
import React, {forwardRef, useId} from 'react';
import clsx from 'clsx';
import {EditorView} from '@codemirror/view';
import {Extension} from '@codemirror/state';
export interface CodeEditorProps extends Omit<ReactCodeMirrorProps, 'value' | 'onChange'> {
title?: string;
value?: string;
height?: string;
error?: boolean;
hint?: React.ReactNode;
clearBg?: boolean;
extensions: Extension[];
onChange?: (value: string) => void;
}
const theme = EditorView.theme({
'& .cm-scroller': {
fontFamily: 'Consolas, Liberation Mono, Menlo, Courier, monospace'
},
'& .cm-activeLine, & .cm-activeLineGutter': {
backgroundColor: 'transparent'
}
});
const CodeEditor = forwardRef<ReactCodeMirrorRef, CodeEditorProps>(function CodeEditor({
title,
value,
height = '200px',
error,
hint,
clearBg = true,
extensions,
onChange,
...props
}, ref) {
const id = useId();
let styles = clsx(
'peer order-2 overflow-hidden rounded-sm border',
clearBg ? 'bg-transparent' : 'bg-grey-75',
error ? 'border-red' : 'border-grey-500 hover:border-grey-700 focus:border-grey-800',
title && 'mt-2',
height === 'full' && 'h-full'
);
return (
<div className={height === 'full' ? 'h-full' : ''}>
<CodeMirror
ref={ref}
className={styles}
extensions={extensions}
height={height === 'full' ? '100%' : height}
theme={theme}
value={value}
onChange={onChange}
{...props}
/>
{title && <Heading className={'order-1 !text-grey-700 peer-focus:!text-black'} htmlFor={id} useLabelTag={true}>{title}</Heading>}
{hint && <Hint className='order-3' color={error ? 'red' : ''}>{hint}</Hint>}
</div>
);
});
export default CodeEditor;

View file

@ -1,5 +1,6 @@
import React from 'react';
import AdvancedSettings from './settings/advanced/AdvancedSettings';
import EmailSettings from './settings/email/EmailSettings';
import GeneralSettings from './settings/general/GeneralSettings';
import MembershipSettings from './settings/membership/MembershipSettings';
@ -12,6 +13,7 @@ const Settings: React.FC = () => {
<SiteSettings />
<MembershipSettings />
<EmailSettings />
<AdvancedSettings />
<div className='mt-40 text-sm'>
<a className='text-green' href="/ghost/#/settings">Click here</a> to open the original Admin settings.
</div>

View file

@ -0,0 +1,17 @@
import CodeInjection from './CodeInjection';
import React from 'react';
import SettingSection from '../../../admin-x-ds/settings/SettingSection';
const searchKeywords = {
codeInjection: ['newsletter', 'enable', 'disable', 'turn on']
};
const AdvancedSettings: React.FC = () => {
return (
<SettingSection keywords={Object.values(searchKeywords).flat()} title='Advanced'>
<CodeInjection keywords={searchKeywords.codeInjection} />
</SettingSection>
);
};
export default AdvancedSettings;

View file

@ -0,0 +1,95 @@
import Button from '../../../admin-x-ds/global/Button';
import CodeEditor from '../../../admin-x-ds/global/form/CodeEditor';
import CodeModal from './code/CodeModal';
import NiceModal from '@ebay/nice-modal-react';
import React, {useRef, useState} from 'react';
import SettingGroup from '../../../admin-x-ds/settings/SettingGroup';
import TabView from '../../../admin-x-ds/global/TabView';
import useSettingGroup from '../../../hooks/useSettingGroup';
import {ReactCodeMirrorRef} from '@uiw/react-codemirror';
import {getSettingValues} from '../../../utils/helpers';
import {html} from '@codemirror/lang-html';
const CodeInjection: React.FC<{ keywords: string[] }> = ({keywords}) => {
const {
localSettings,
isEditing,
saveState,
handleSave,
handleCancel,
updateSetting,
handleEditingChange
} = useSettingGroup();
const [headerContent, footerContent] = getSettingValues<string>(localSettings, ['codeinjection_head', 'codeinjection_foot']);
const [selectedTab, setSelectedTab] = useState<'header' | 'footer'>('header');
const headerEditorRef = useRef<ReactCodeMirrorRef>(null);
const footerEditorRef = useRef<ReactCodeMirrorRef>(null);
const headerProps = {
extensions: [html()],
hint: 'Code here will be injected into the {{ghost_head}} tag on every page of the site',
value: headerContent,
onChange: (value: string) => updateSetting('codeinjection_head', value)
};
const footerProps = {
extensions: [html()],
hint: 'Code here will be injected into the {{ghost_foot}} tag on every page of the site',
value: footerContent,
onChange: (value: string) => updateSetting('codeinjection_foot', value)
};
const tabs = [
{
id: 'header',
title: 'Site header',
contents: (<CodeEditor {...headerProps} ref={headerEditorRef} autoFocus />)
},
{
id: 'footer',
title: 'Site footer',
contents: (<CodeEditor {...footerProps} ref={footerEditorRef} />)
}
] as const;
return (
<SettingGroup
description="Add custom code to your publication"
isEditing={isEditing}
keywords={keywords}
navid='code-injection'
saveState={saveState}
testId='code-injection'
title="Code injection"
onCancel={handleCancel}
onEditingChange={handleEditingChange}
onSave={handleSave}
>
{isEditing && (
<div className='relative'>
<TabView<'header' | 'footer'> selectedTab={selectedTab} tabs={tabs} onTabChange={setSelectedTab} />
<Button
className='absolute right-0 top-1 text-sm'
label='Fullscreen'
unstyled
onClick={() => NiceModal.show(CodeModal, {
...(selectedTab === 'header' ? headerProps : footerProps),
afterClose: () => {
if (selectedTab === 'header') {
headerEditorRef.current?.view?.focus();
} else {
footerEditorRef.current?.view?.focus();
}
}
})}
/>
</div>
)}
</SettingGroup>
);
};
export default CodeInjection;

View file

@ -0,0 +1,27 @@
import CodeEditor from '../../../../admin-x-ds/global/form/CodeEditor';
import Modal from '../../../../admin-x-ds/global/modal/Modal';
import NiceModal, {useModal} from '@ebay/nice-modal-react';
import React from 'react';
import {html} from '@codemirror/lang-html';
interface CodeModalProps {
hint?: React.ReactNode;
value?: string;
onChange: (value: string) => void;
afterClose?: () => void
}
const CodeModal: React.FC<CodeModalProps> = ({hint, value, onChange, afterClose}) => {
const modal = useModal();
const onOk = () => {
modal.remove();
afterClose?.();
};
return <Modal afterClose={afterClose} cancelLabel='' okColor='grey' okLabel='Done' size='full' onOk={onOk}>
<CodeEditor extensions={[html()]} height='full' hint={hint} value={value} autoFocus onChange={onChange} />
</Modal>;
};
export default NiceModal.create(CodeModal);

211
yarn.lock
View file

@ -1862,6 +1862,13 @@
dependencies:
regenerator-runtime "^0.13.11"
"@babel/runtime@^7.18.6":
version "7.22.6"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.6.tgz#57d64b9ae3cff1d67eb067ae117dac087f5bd438"
integrity sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ==
dependencies:
regenerator-runtime "^0.13.11"
"@babel/template@^7.20.7", "@babel/template@^7.22.5":
version "7.22.5"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.5.tgz#0c8c4d944509875849bd0344ff0050756eefc6ec"
@ -1919,6 +1926,119 @@
exec-sh "^0.3.2"
minimist "^1.2.0"
"@codemirror/autocomplete@^6.0.0":
version "6.9.0"
resolved "https://registry.yarnpkg.com/@codemirror/autocomplete/-/autocomplete-6.9.0.tgz#1a1e63122288b8f8e1e9d7aff2eb39a83e04d8a9"
integrity sha512-Fbwm0V/Wn3BkEJZRhr0hi5BhCo5a7eBL6LYaliPjOSwCyfOpnjXY59HruSxOUNV+1OYer0Tgx1zRNQttjXyDog==
dependencies:
"@codemirror/language" "^6.0.0"
"@codemirror/state" "^6.0.0"
"@codemirror/view" "^6.6.0"
"@lezer/common" "^1.0.0"
"@codemirror/commands@^6.0.0", "@codemirror/commands@^6.1.0":
version "6.2.4"
resolved "https://registry.yarnpkg.com/@codemirror/commands/-/commands-6.2.4.tgz#b8a0e5ce72448c092ba4c4b1d902e6f183948aec"
integrity sha512-42lmDqVH0ttfilLShReLXsDfASKLXzfyC36bzwcqzox9PlHulMcsUOfHXNo2X2aFMVNUoQ7j+d4q5bnfseYoOA==
dependencies:
"@codemirror/language" "^6.0.0"
"@codemirror/state" "^6.2.0"
"@codemirror/view" "^6.0.0"
"@lezer/common" "^1.0.0"
"@codemirror/lang-css@^6.0.0":
version "6.2.1"
resolved "https://registry.yarnpkg.com/@codemirror/lang-css/-/lang-css-6.2.1.tgz#5dc0a43b8e3c31f6af7aabd55ff07fe9aef2a227"
integrity sha512-/UNWDNV5Viwi/1lpr/dIXJNWiwDxpw13I4pTUAsNxZdg6E0mI2kTQb0P2iHczg1Tu+H4EBgJR+hYhKiHKko7qg==
dependencies:
"@codemirror/autocomplete" "^6.0.0"
"@codemirror/language" "^6.0.0"
"@codemirror/state" "^6.0.0"
"@lezer/common" "^1.0.2"
"@lezer/css" "^1.0.0"
"@codemirror/lang-html@^6.4.5":
version "6.4.5"
resolved "https://registry.yarnpkg.com/@codemirror/lang-html/-/lang-html-6.4.5.tgz#4cf014da02624a8a4365ef6c8e343f35afa0c784"
integrity sha512-dUCSxkIw2G+chaUfw3Gfu5kkN83vJQN8gfQDp9iEHsIZluMJA0YJveT12zg/28BJx+uPsbQ6VimKCgx3oJrZxA==
dependencies:
"@codemirror/autocomplete" "^6.0.0"
"@codemirror/lang-css" "^6.0.0"
"@codemirror/lang-javascript" "^6.0.0"
"@codemirror/language" "^6.4.0"
"@codemirror/state" "^6.0.0"
"@codemirror/view" "^6.2.2"
"@lezer/common" "^1.0.0"
"@lezer/css" "^1.1.0"
"@lezer/html" "^1.3.0"
"@codemirror/lang-javascript@^6.0.0":
version "6.1.9"
resolved "https://registry.yarnpkg.com/@codemirror/lang-javascript/-/lang-javascript-6.1.9.tgz#19065ad32db7b3797829eca01b8d9c69da5fd0d6"
integrity sha512-z3jdkcqOEBT2txn2a87A0jSy6Te3679wg/U8QzMeftFt+4KA6QooMwfdFzJiuC3L6fXKfTXZcDocoaxMYfGz0w==
dependencies:
"@codemirror/autocomplete" "^6.0.0"
"@codemirror/language" "^6.6.0"
"@codemirror/lint" "^6.0.0"
"@codemirror/state" "^6.0.0"
"@codemirror/view" "^6.0.0"
"@lezer/common" "^1.0.0"
"@lezer/javascript" "^1.0.0"
"@codemirror/language@^6.0.0", "@codemirror/language@^6.4.0", "@codemirror/language@^6.6.0":
version "6.8.0"
resolved "https://registry.yarnpkg.com/@codemirror/language/-/language-6.8.0.tgz#f2d7eea6b338c25593d800f2293b062d9f9856db"
integrity sha512-r1paAyWOZkfY0RaYEZj3Kul+MiQTEbDvYqf8gPGaRvNneHXCmfSaAVFjwRUPlgxS8yflMxw2CTu6uCMp8R8A2g==
dependencies:
"@codemirror/state" "^6.0.0"
"@codemirror/view" "^6.0.0"
"@lezer/common" "^1.0.0"
"@lezer/highlight" "^1.0.0"
"@lezer/lr" "^1.0.0"
style-mod "^4.0.0"
"@codemirror/lint@^6.0.0":
version "6.4.0"
resolved "https://registry.yarnpkg.com/@codemirror/lint/-/lint-6.4.0.tgz#3507e937aa9415ef0831ff04734ef0e736e75014"
integrity sha512-6VZ44Ysh/Zn07xrGkdtNfmHCbGSHZzFBdzWi0pbd7chAQ/iUcpLGX99NYRZTa7Ugqg4kEHCqiHhcZnH0gLIgSg==
dependencies:
"@codemirror/state" "^6.0.0"
"@codemirror/view" "^6.0.0"
crelt "^1.0.5"
"@codemirror/search@^6.0.0":
version "6.5.1"
resolved "https://registry.yarnpkg.com/@codemirror/search/-/search-6.5.1.tgz#bdd19ba54d314e55df87b7105ec440eaab4521f6"
integrity sha512-4jupk4JwkeVbrN2pStY74q6OJEYqwosB4koA66nyLeVedadtX9MHI38j2vbYmnfDGurDApP3OZO46MrWalcjiQ==
dependencies:
"@codemirror/state" "^6.0.0"
"@codemirror/view" "^6.0.0"
crelt "^1.0.5"
"@codemirror/state@^6.0.0", "@codemirror/state@^6.1.1", "@codemirror/state@^6.1.4", "@codemirror/state@^6.2.0":
version "6.2.1"
resolved "https://registry.yarnpkg.com/@codemirror/state/-/state-6.2.1.tgz#6dc8d8e5abb26b875e3164191872d69a5e85bd73"
integrity sha512-RupHSZ8+OjNT38zU9fKH2sv+Dnlr8Eb8sl4NOnnqz95mCFTZUaiRP8Xv5MeeaG0px2b8Bnfe7YGwCV3nsBhbuw==
"@codemirror/theme-one-dark@^6.0.0":
version "6.1.2"
resolved "https://registry.yarnpkg.com/@codemirror/theme-one-dark/-/theme-one-dark-6.1.2.tgz#fcef9f9cfc17a07836cb7da17c9f6d7231064df8"
integrity sha512-F+sH0X16j/qFLMAfbciKTxVOwkdAS336b7AXTKOZhy8BR3eH/RelsnLgLFINrpST63mmN2OuwUt0W2ndUgYwUA==
dependencies:
"@codemirror/language" "^6.0.0"
"@codemirror/state" "^6.0.0"
"@codemirror/view" "^6.0.0"
"@lezer/highlight" "^1.0.0"
"@codemirror/view@^6.0.0", "@codemirror/view@^6.2.2", "@codemirror/view@^6.6.0":
version "6.16.0"
resolved "https://registry.yarnpkg.com/@codemirror/view/-/view-6.16.0.tgz#047001b8dd04e104776c476e45ee9c4eed9f99fa"
integrity sha512-1Z2HkvkC3KR/oEZVuW9Ivmp8TWLzGEd8T8TA04TTwPvqogfkHBdYSlflytDOqmkUxM2d1ywTg7X2dU5mC+SXvg==
dependencies:
"@codemirror/state" "^6.1.4"
style-mod "^4.0.0"
w3c-keyname "^2.2.4"
"@colors/colors@1.5.0":
version "1.5.0"
resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9"
@ -3437,6 +3557,50 @@
"@lexical/selection" "0.11.1"
"@lexical/table" "0.11.1"
"@lezer/common@^1.0.0", "@lezer/common@^1.0.2":
version "1.0.3"
resolved "https://registry.yarnpkg.com/@lezer/common/-/common-1.0.3.tgz#1808f70e2b0a7b1fdcbaf5c074723d2d4ed1e4c5"
integrity sha512-JH4wAXCgUOcCGNekQPLhVeUtIqjH0yPBs7vvUdSjyQama9618IOKFJwkv2kcqdhF0my8hQEgCTEJU0GIgnahvA==
"@lezer/css@^1.0.0", "@lezer/css@^1.1.0":
version "1.1.3"
resolved "https://registry.yarnpkg.com/@lezer/css/-/css-1.1.3.tgz#605495b00fd8a122088becf196a93744cbe817fc"
integrity sha512-SjSM4pkQnQdJDVc80LYzEaMiNy9txsFbI7HsMgeVF28NdLaAdHNtQ+kB/QqDUzRBV/75NTXjJ/R5IdC8QQGxMg==
dependencies:
"@lezer/highlight" "^1.0.0"
"@lezer/lr" "^1.0.0"
"@lezer/highlight@^1.0.0", "@lezer/highlight@^1.1.3":
version "1.1.6"
resolved "https://registry.yarnpkg.com/@lezer/highlight/-/highlight-1.1.6.tgz#87e56468c0f43c2a8b3dc7f0b7c2804b34901556"
integrity sha512-cmSJYa2us+r3SePpRCjN5ymCqCPv+zyXmDl0ciWtVaNiORT/MxM7ZgOMQZADD0o51qOaOg24qc/zBViOIwAjJg==
dependencies:
"@lezer/common" "^1.0.0"
"@lezer/html@^1.3.0":
version "1.3.6"
resolved "https://registry.yarnpkg.com/@lezer/html/-/html-1.3.6.tgz#26a2a17da4e0f91835e36db9ccd025b2ed8d33f7"
integrity sha512-Kk9HJARZTc0bAnMQUqbtuhFVsB4AnteR2BFUWfZV7L/x1H0aAKz6YabrfJ2gk/BEgjh9L3hg5O4y2IDZRBdzuQ==
dependencies:
"@lezer/common" "^1.0.0"
"@lezer/highlight" "^1.0.0"
"@lezer/lr" "^1.0.0"
"@lezer/javascript@^1.0.0":
version "1.4.5"
resolved "https://registry.yarnpkg.com/@lezer/javascript/-/javascript-1.4.5.tgz#4ab56dbcbff3e58ef331294a549903a5dd8d154a"
integrity sha512-FmBUHz8K1V22DgjTd6SrIG9owbzOYZ1t3rY6vGEmw+e2RVBd7sqjM8uXEVRFmfxKFn1Mx2ABJehHjrN3G2ZpmA==
dependencies:
"@lezer/highlight" "^1.1.3"
"@lezer/lr" "^1.3.0"
"@lezer/lr@^1.0.0", "@lezer/lr@^1.3.0":
version "1.3.9"
resolved "https://registry.yarnpkg.com/@lezer/lr/-/lr-1.3.9.tgz#cb299816d1c58efcca23ebbeb70bb4204fdd001b"
integrity sha512-XPz6dzuTHlnsbA5M2DZgjflNQ+9Hi5Swhic0RULdp3oOs3rh6bqGZolosVqN/fQIT8uNiepzINJDnS39oweTHQ==
dependencies:
"@lezer/common" "^1.0.0"
"@linaria/core@4.2.9":
version "4.2.9"
resolved "https://registry.yarnpkg.com/@linaria/core/-/core-4.2.9.tgz#4917bde18d064a29cff4fd86aa99621f953a2a2c"
@ -7380,6 +7544,31 @@
"@typescript-eslint/types" "6.1.0"
eslint-visitor-keys "^3.4.1"
"@uiw/codemirror-extensions-basic-setup@4.21.9":
version "4.21.9"
resolved "https://registry.yarnpkg.com/@uiw/codemirror-extensions-basic-setup/-/codemirror-extensions-basic-setup-4.21.9.tgz#e886c6e6ad477bc0943691b9572958c81a2beab3"
integrity sha512-TQT6aF8brxZpFnk/K4fm/K/9k9eF3PMav/KKjHlYrGUT8BTNk/qL+ximLtIzvTUhmBFchjM1lrqSJdvpVom7/w==
dependencies:
"@codemirror/autocomplete" "^6.0.0"
"@codemirror/commands" "^6.0.0"
"@codemirror/language" "^6.0.0"
"@codemirror/lint" "^6.0.0"
"@codemirror/search" "^6.0.0"
"@codemirror/state" "^6.0.0"
"@codemirror/view" "^6.0.0"
"@uiw/react-codemirror@^4.21.9":
version "4.21.9"
resolved "https://registry.yarnpkg.com/@uiw/react-codemirror/-/react-codemirror-4.21.9.tgz#74393955d159a7d452731e61957773ae053c65b8"
integrity sha512-aeLegPz2iCvqJjhzXp2WUMqpMZDqxsTnF3rX9kGRlfY6vQLsrjoctj0cQ29uxEtFYJChOVjtCOtnQUlyIuNAHQ==
dependencies:
"@babel/runtime" "^7.18.6"
"@codemirror/commands" "^6.1.0"
"@codemirror/state" "^6.1.1"
"@codemirror/theme-one-dark" "^6.0.0"
"@uiw/codemirror-extensions-basic-setup" "4.21.9"
codemirror "^6.0.0"
"@vitejs/plugin-react@4.0.3":
version "4.0.3"
resolved "https://registry.yarnpkg.com/@vitejs/plugin-react/-/plugin-react-4.0.3.tgz#007d27ad5ef1eac4bf8c29e168ba9be2203c371b"
@ -11496,6 +11685,19 @@ codemirror@5.48.2:
resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.48.2.tgz#a9dd3d426dea4cd59efd59cd98e20a9152a30922"
integrity sha512-i9VsmC8AfA5ji6EDIZ+aoSe4vt9FcwPLdHB1k1ItFbVyuOFRrcfvnoKqwZlC7EVA2UmTRiNEypE4Uo7YvzVY8Q==
codemirror@^6.0.0:
version "6.0.1"
resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-6.0.1.tgz#62b91142d45904547ee3e0e0e4c1a79158035a29"
integrity sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==
dependencies:
"@codemirror/autocomplete" "^6.0.0"
"@codemirror/commands" "^6.0.0"
"@codemirror/language" "^6.0.0"
"@codemirror/lint" "^6.0.0"
"@codemirror/search" "^6.0.0"
"@codemirror/state" "^6.0.0"
"@codemirror/view" "^6.0.0"
collapse-white-space@^1.0.2:
version "1.0.6"
resolved "https://registry.yarnpkg.com/collapse-white-space/-/collapse-white-space-1.0.6.tgz#e63629c0016665792060dbbeb79c42239d2c5287"
@ -12053,7 +12255,7 @@ create-require@^1.1.0:
resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333"
integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==
crelt@^1.0.0:
crelt@^1.0.0, crelt@^1.0.5:
version "1.0.6"
resolved "https://registry.yarnpkg.com/crelt/-/crelt-1.0.6.tgz#7cc898ea74e190fb6ef9dae57f8f81cf7302df72"
integrity sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==
@ -27514,6 +27716,11 @@ style-loader@^3.3.2:
resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-3.3.2.tgz#eaebca714d9e462c19aa1e3599057bc363924899"
integrity sha512-RHs/vcrKdQK8wZliteNK4NKzxvLBzpuHMqYmUVWeKa6MkaIQ97ZTOS0b+zapZhy6GcrgWnvWYCMHRirC3FsUmw==
style-mod@^4.0.0:
version "4.0.3"
resolved "https://registry.yarnpkg.com/style-mod/-/style-mod-4.0.3.tgz#136c4abc905f82a866a18b39df4dc08ec762b1ad"
integrity sha512-78Jv8kYJdjbvRwwijtCevYADfsI0lGzYJe4mMFdceO8l75DFFDoqBhR1jVDicDRRaX4//g1u9wKeo+ztc2h1Rw==
style-search@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/style-search/-/style-search-0.1.0.tgz#7958c793e47e32e07d2b5cafe5c0bf8e12e77902"
@ -29407,7 +29614,7 @@ w3c-hr-time@^1.0.2:
dependencies:
browser-process-hrtime "^1.0.0"
w3c-keyname@^2.2.0:
w3c-keyname@^2.2.0, w3c-keyname@^2.2.4:
version "2.2.8"
resolved "https://registry.yarnpkg.com/w3c-keyname/-/w3c-keyname-2.2.8.tgz#7b17c8c6883d4e8b86ac8aba79d39e880f8869c5"
integrity sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==